From 05a815d61965daf3a01ffb3b50fdb8aa7c7df81e Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 24 Dec 2016 20:39:04 +0000 Subject: [PATCH] Import gnupg2_2.1.17.orig.tar.bz2 [dgit import orig gnupg2_2.1.17.orig.tar.bz2] --- ABOUT-NLS | 1068 + AUTHORS | 256 + COPYING | 676 + COPYING.CC0 | 123 + COPYING.LIB | 167 + COPYING.other | 60 + ChangeLog | 16762 +++++++++++++++ ChangeLog-2011 | 1394 ++ INSTALL | 234 + Makefile.am | 147 + Makefile.in | 1045 + NEWS | 3243 +++ README | 244 + README.GIT | 49 + THANKS | 297 + TODO | 118 + VERSION | 1 + acinclude.m4 | 351 + aclocal.m4 | 1267 ++ agent/ChangeLog-2011 | 3107 +++ agent/Makefile.am | 109 + agent/Makefile.in | 1310 ++ agent/agent.h | 575 + agent/cache.c | 480 + agent/call-pinentry.c | 1493 ++ agent/call-scd.c | 1270 ++ agent/command-ssh.c | 3692 ++++ agent/command.c | 3351 +++ agent/cvt-openpgp.c | 1410 ++ agent/cvt-openpgp.h | 37 + agent/divert-scd.c | 492 + agent/findkey.c | 1539 ++ agent/genkey.c | 617 + agent/gpg-agent-w32info.rc | 50 + agent/gpg-agent.c | 3104 +++ agent/learncard.c | 446 + agent/pkdecrypt.c | 146 + agent/pksign.c | 557 + agent/preset-passphrase.c | 266 + agent/protect-tool.c | 819 + agent/protect.c | 1709 ++ agent/t-protect.c | 350 + agent/trans.c | 41 + agent/trustlist.c | 825 + am/cmacros.am | 81 + autogen.rc | 44 + autogen.sh | 469 + build-aux/ChangeLog-2011 | 62 + build-aux/compile | 347 + build-aux/config.guess | 1456 ++ build-aux/config.rpath | 690 + build-aux/config.sub | 1823 ++ build-aux/depcomp | 791 + build-aux/getswdb.sh | 190 + build-aux/git-log-fix | 3 + build-aux/git-log-footer | 14 + build-aux/gitlog-to-changelog | 374 + build-aux/install-sh | 527 + build-aux/mdate-sh | 224 + build-aux/missing | 215 + build-aux/mkinstalldirs | 161 + build-aux/potomo | 64 + build-aux/speedo.mk | 1240 ++ build-aux/speedo/patches/atk-1.32.0.patch | 671 + build-aux/speedo/patches/libiconv-1.14.patch | 19 + build-aux/speedo/patches/pango-1.29.4.patch | 27 + build-aux/speedo/patches/sqlite.patch | 42 + build-aux/speedo/w32/README.txt | 75 + build-aux/speedo/w32/exdll.h | 151 + build-aux/speedo/w32/g4wihelp.c | 1136 + build-aux/speedo/w32/gdk-pixbuf-loaders.cache | 138 + build-aux/speedo/w32/gnupg-logo-150x57.bmp | Bin 0 -> 3842 bytes build-aux/speedo/w32/gnupg-logo-164x314.bmp | Bin 0 -> 4182 bytes build-aux/speedo/w32/inst-options.ini | 46 + build-aux/speedo/w32/inst.nsi | 1613 ++ build-aux/speedo/w32/pango.modules | 3 + build-aux/speedo/w32/pkg-copyright.txt | 181 + build-aux/speedo/zlib.pc | 10 + build-aux/texinfo.tex | 8638 ++++++++ common/ChangeLog-2011 | 2494 +++ common/ChangeLog-2011.include | 458 + common/ChangeLog.jnlib | 783 + common/Makefile.am | 224 + common/Makefile.in | 2897 +++ common/README | 1 + common/agent-opt.c | 71 + common/argparse.c | 1629 ++ common/argparse.h | 203 + common/asshelp.c | 673 + common/asshelp.h | 97 + common/asshelp2.c | 73 + common/audit-events.h | 116 + common/audit.c | 1323 ++ common/audit.h | 225 + common/b64dec.c | 253 + common/b64enc.c | 422 + common/call-gpg.c | 753 + common/call-gpg.h | 54 + common/ccparray.c | 148 + common/ccparray.h | 51 + common/common-defs.h | 54 + common/convert.c | 266 + common/dotlock.c | 1379 ++ common/dotlock.h | 112 + common/dynload.h | 97 + common/exaudit.awk | 43 + common/exechelp-posix.c | 892 + common/exechelp-w32.c | 921 + common/exechelp-w32ce.c | 886 + common/exechelp.h | 199 + common/exectool.c | 629 + common/exectool.h | 69 + common/exstatus.awk | 39 + common/fwddecl.h | 39 + common/gc-opt-flags.h | 46 + common/get-passphrase.c | 255 + common/get-passphrase.h | 54 + common/gettime.c | 993 + common/gettime.h | 70 + common/gnupg.ico | Bin 0 -> 10134 bytes common/gpgrlhelp.c | 97 + common/helpfile.c | 272 + common/homedir.c | 1029 + common/host2net.h | 112 + common/i18n.c | 237 + common/i18n.h | 63 + common/init.c | 289 + common/init.h | 47 + common/iobuf.c | 2698 +++ common/iobuf.h | 622 + common/keyserver.h | 73 + common/localename.c | 126 + common/logging.c | 1013 + common/logging.h | 111 + common/mapstrings.c | 167 + common/mbox-util.c | 243 + common/mbox-util.h | 29 + common/membuf.c | 230 + common/membuf.h | 64 + common/miscellaneous.c | 565 + common/mischelp.c | 194 + common/mischelp.h | 98 + common/mkdir_p.c | 187 + common/mkdir_p.h | 52 + common/mkstrtable.awk | 185 + common/name-value.c | 806 + common/name-value.h | 120 + common/openpgp-oid.c | 443 + common/openpgpdefs.h | 184 + common/percent.c | 238 + common/recsel.c | 624 + common/recsel.h | 43 + common/server-help.c | 137 + common/server-help.h | 62 + common/session-env.c | 400 + common/session-env.h | 53 + common/sexp-parse.h | 137 + common/sexputil.c | 608 + common/shareddefs.h | 48 + common/signal.c | 249 + common/simple-pwquery.c | 494 + common/simple-pwquery.h | 70 + common/ssh-utils.c | 284 + common/ssh-utils.h | 39 + common/status-codes.h | 238 + common/status.c | 76 + common/status.h | 164 + common/stringhelp.c | 1567 ++ common/stringhelp.h | 164 + common/strlist.c | 281 + common/strlist.h | 69 + common/sysutils.c | 1283 ++ common/sysutils.h | 87 + common/t-b64.c | 181 + common/t-ccparray.c | 93 + common/t-convert.c | 463 + common/t-exechelp.c | 188 + common/t-exectool.c | 223 + common/t-gettime.c | 268 + common/t-helpfile.c | 66 + common/t-iobuf.c | 392 + common/t-mapstrings.c | 100 + common/t-mbox-util.c | 105 + common/t-name-value.c | 593 + common/t-openpgp-oid.c | 238 + common/t-percent.c | 114 + common/t-recsel.c | 438 + common/t-session-env.c | 304 + common/t-sexputil.c | 191 + common/t-ssh-utils.c | 314 + common/t-stringhelp.c | 999 + common/t-strlist.c | 84 + common/t-support.h | 84 + common/t-sysutils.c | 89 + common/t-timestuff.c | 175 + common/t-w32-reg.c | 80 + common/t-zb32.c | 305 + common/tlv.c | 312 + common/tlv.h | 116 + common/ttyio.c | 766 + common/ttyio.h | 74 + common/types.h | 116 + common/userids.c | 434 + common/userids.h | 39 + common/utf8conv.c | 838 + common/utf8conv.h | 58 + common/util.h | 378 + common/utilproto.h | 44 + common/w32-reg.c | 230 + common/w32help.h | 56 + common/w32info-rc.h.in | 32 + common/xasprintf.c | 70 + common/xreadline.c | 127 + common/yesno.c | 150 + common/zb32.c | 120 + common/zb32.h | 38 + config.h.in | 911 + configure | 17777 ++++++++++++++++ configure.ac | 1950 ++ dirmngr/ChangeLog-2011 | 2407 +++ dirmngr/Makefile.am | 161 + dirmngr/Makefile.in | 1439 ++ dirmngr/OAUTHORS | 38 + dirmngr/ONEWS | 240 + dirmngr/cdb.h | 94 + dirmngr/cdblib.c | 928 + dirmngr/certcache.c | 1391 ++ dirmngr/certcache.h | 103 + dirmngr/crlcache.c | 2584 +++ dirmngr/crlcache.h | 70 + dirmngr/crlfetch.c | 565 + dirmngr/crlfetch.h | 88 + dirmngr/dirmngr-client.c | 934 + dirmngr/dirmngr-err.h | 12 + dirmngr/dirmngr.c | 2155 ++ dirmngr/dirmngr.h | 218 + dirmngr/dirmngr_ldap.c | 732 + dirmngr/dns-stuff.c | 2012 ++ dirmngr/dns-stuff.h | 159 + dirmngr/dns.c | 11246 ++++++++++ dirmngr/dns.h | 1365 ++ dirmngr/http.c | 2899 +++ dirmngr/http.h | 163 + dirmngr/ks-action.c | 403 + dirmngr/ks-action.h | 36 + dirmngr/ks-engine-finger.c | 124 + dirmngr/ks-engine-hkp.c | 1517 ++ dirmngr/ks-engine-http.c | 184 + dirmngr/ks-engine-kdns.c | 79 + dirmngr/ks-engine-ldap.c | 2101 ++ dirmngr/ks-engine.h | 67 + dirmngr/ldap-parse-uri.c | 246 + dirmngr/ldap-parse-uri.h | 33 + dirmngr/ldap-url.c | 935 + dirmngr/ldap-url.h | 50 + dirmngr/ldap-wrapper-ce.c | 571 + dirmngr/ldap-wrapper.c | 782 + dirmngr/ldap-wrapper.h | 40 + dirmngr/ldap.c | 875 + dirmngr/ldapserver.c | 132 + dirmngr/ldapserver.h | 90 + dirmngr/loadswdb.c | 397 + dirmngr/misc.c | 645 + dirmngr/misc.h | 91 + dirmngr/ocsp.c | 801 + dirmngr/ocsp.h | 31 + dirmngr/server.c | 2691 +++ dirmngr/sks-keyservers.netCA.pem | 32 + dirmngr/t-dns-stuff.c | 327 + dirmngr/t-http.c | 398 + dirmngr/t-ldap-parse-uri.c | 257 + dirmngr/t-support.h | 42 + dirmngr/tls-ca.pem | 30 + dirmngr/validate.c | 1155 + dirmngr/validate.h | 55 + dirmngr/w32-ldap-help.h | 169 + doc/ChangeLog-2011 | 871 + doc/DCO | 29 + doc/DETAILS | 1442 ++ doc/FAQ | 13 + doc/HACKING | 432 + doc/KEYSERVER | 83 + doc/Makefile.am | 198 + doc/Makefile.in | 1241 ++ doc/OpenPGP | 116 + doc/TRANSLATE | 61 + doc/com-certs.pem | 67 + doc/contrib.texi | 106 + doc/debugging.texi | 291 + doc/defsincdate | 1 + doc/dirmngr.texi | 1133 + doc/examples/README | 11 + doc/examples/gpgconf.conf | 63 + doc/examples/pwpattern.list | 48 + doc/examples/scd-event | 102 + doc/examples/systemd-user/README | 66 + doc/examples/systemd-user/dirmngr.service | 10 + doc/examples/systemd-user/dirmngr.socket | 11 + .../systemd-user/gpg-agent-browser.socket | 13 + .../systemd-user/gpg-agent-extra.socket | 13 + .../systemd-user/gpg-agent-ssh.socket | 13 + doc/examples/systemd-user/gpg-agent.service | 10 + doc/examples/systemd-user/gpg-agent.socket | 12 + doc/examples/trustlist.txt | 66 + doc/glossary.texi | 72 + doc/gnupg-card-architecture.fig | 419 + doc/gnupg-card-architecture.pdf | Bin 0 -> 19753 bytes doc/gnupg-card-architecture.png | Bin 0 -> 14730 bytes doc/gnupg-logo-tr.png | Bin 0 -> 9415 bytes doc/gnupg-logo.eps | 2704 +++ doc/gnupg-logo.pdf | Bin 0 -> 13838 bytes doc/gnupg-logo.png | Bin 0 -> 14471 bytes doc/gnupg-module-overview.pdf | Bin 0 -> 315248 bytes doc/gnupg-module-overview.png | Bin 0 -> 36868 bytes doc/gnupg-module-overview.svg | 892 + doc/gnupg.info | 219 + doc/gnupg.info-1 | 7314 +++++++ doc/gnupg.info-2 | 4747 +++++ doc/gnupg.texi | 233 + doc/gnupg7.texi | 31 + doc/gpg-agent.texi | 1533 ++ doc/gpg.texi | 4032 ++++ doc/gpgsm.texi | 1629 ++ doc/gpgv.texi | 187 + doc/gpl.texi | 732 + doc/help.be.txt | 286 + doc/help.ca.txt | 286 + doc/help.cs.txt | 286 + doc/help.da.txt | 286 + doc/help.de.txt | 279 + doc/help.el.txt | 286 + doc/help.eo.txt | 286 + doc/help.es.txt | 251 + doc/help.et.txt | 286 + doc/help.fi.txt | 256 + doc/help.fr.txt | 256 + doc/help.gl.txt | 286 + doc/help.hu.txt | 257 + doc/help.id.txt | 251 + doc/help.it.txt | 251 + doc/help.ja.txt | 335 + doc/help.nb.txt | 286 + doc/help.pl.txt | 250 + doc/help.pt.txt | 253 + doc/help.pt_BR.txt | 253 + doc/help.ro.txt | 251 + doc/help.ru.txt | 369 + doc/help.sk.txt | 254 + doc/help.sv.txt | 286 + doc/help.tr.txt | 242 + doc/help.txt | 398 + doc/help.zh_CN.txt | 233 + doc/help.zh_TW.txt | 245 + doc/howto-create-a-server-cert.texi | 274 + doc/howtos.texi | 15 + doc/instguide.texi | 77 + doc/mkdefsinc.c | 367 + doc/mksamplekeys | 10 + doc/opt-homedir.texi | 25 + doc/qualified.txt | 243 + doc/samplekeys.asc | 920 + doc/scdaemon.texi | 765 + doc/see-also-note.texi | 14 + doc/specify-user-id.texi | 176 + doc/sysnotes.texi | 58 + doc/tools.texi | 2022 ++ doc/whats-new-in-2.1.txt | 850 + doc/yat2m.c | 1634 ++ g10/ChangeLog-2011 | 12066 +++++++++++ g10/Makefile.am | 259 + g10/Makefile.in | 1247 ++ g10/armor.c | 1571 ++ g10/build-packet.c | 1696 ++ g10/call-agent.c | 2310 ++ g10/call-agent.h | 203 + g10/call-dirmngr.c | 1348 ++ g10/call-dirmngr.h | 47 + g10/card-util.c | 2134 ++ g10/cipher.c | 162 + g10/compress-bz2.c | 253 + g10/compress.c | 365 + g10/cpr.c | 627 + g10/dearmor.c | 131 + g10/decrypt-data.c | 501 + g10/decrypt.c | 281 + g10/dek.h | 41 + g10/delkey.c | 293 + g10/dirmngr-conf.skel | 69 + g10/distsigkey.gpg | Bin 0 -> 3452 bytes g10/ecdh.c | 495 + g10/encrypt.c | 992 + g10/exec.c | 635 + g10/exec.h | 51 + g10/export.c | 2326 ++ g10/filter.h | 162 + g10/free-packet.c | 527 + g10/getkey.c | 4259 ++++ g10/gpg-w32info.rc | 52 + g10/gpg.c | 5308 +++++ g10/gpg.h | 92 + g10/gpg.w32-manifest.in | 17 + g10/gpgcompose.c | 3034 +++ g10/gpgsql.c | 251 + g10/gpgsql.h | 61 + g10/gpgv.c | 727 + g10/helptext.c | 86 + g10/import.c | 3363 +++ g10/kbnode.c | 431 + g10/keydb.c | 2097 ++ g10/keydb.h | 498 + g10/keyedit.c | 6654 ++++++ g10/keygen.c | 5296 +++++ g10/keyid.c | 981 + g10/keylist.c | 1922 ++ g10/keyring.c | 1771 ++ g10/keyring.h | 45 + g10/keyserver-internal.h | 56 + g10/keyserver.c | 2151 ++ g10/main.h | 493 + g10/mainproc.c | 2491 +++ g10/mdfilter.c | 73 + g10/migrate.c | 118 + g10/misc.c | 1772 ++ g10/openfile.c | 523 + g10/options.h | 396 + g10/options.skel | 139 + g10/packet.h | 855 + g10/parse-packet.c | 3149 +++ g10/passphrase.c | 547 + g10/photoid.c | 382 + g10/photoid.h | 34 + g10/pkclist.c | 1694 ++ g10/pkglue.c | 422 + g10/pkglue.h | 50 + g10/plaintext.c | 813 + g10/progress.c | 202 + g10/pubkey-enc.c | 430 + g10/revoke.c | 883 + g10/rmd160.c | 425 + g10/rmd160.h | 24 + g10/server.c | 802 + g10/seskey.c | 359 + g10/sig-check.c | 1108 + g10/sign.c | 1618 ++ g10/skclist.c | 270 + g10/sqrtu32.c | 244 + g10/sqrtu32.h | 14 + g10/t-keydb-get-keyblock.c | 64 + g10/t-keydb-get-keyblock.gpg | Bin 0 -> 138824 bytes g10/t-keydb-keyring.kbx | Bin 0 -> 5104 bytes g10/t-keydb.c | 104 + g10/t-rmd160.c | 91 + g10/t-stutter-data.asc | 1 + g10/t-stutter.c | 611 + g10/tdbdump.c | 233 + g10/tdbio.c | 1868 ++ g10/tdbio.h | 118 + g10/test-stubs.c | 531 + g10/test.c | 191 + g10/textfilter.c | 245 + g10/tofu.c | 3912 ++++ g10/tofu.h | 142 + g10/trust.c | 769 + g10/trustdb.c | 2204 ++ g10/trustdb.h | 171 + g10/verify.c | 276 + g13/ChangeLog-2011 | 14 + g13/Makefile.am | 79 + g13/Makefile.in | 998 + g13/backend.c | 295 + g13/backend.h | 49 + g13/be-dmcrypt.c | 116 + g13/be-dmcrypt.h | 36 + g13/be-encfs.c | 471 + g13/be-encfs.h | 41 + g13/be-truecrypt.c | 37 + g13/be-truecrypt.h | 28 + g13/call-syshelp.c | 631 + g13/call-syshelp.h | 42 + g13/create.c | 302 + g13/create.h | 29 + g13/g13-common.c | 86 + g13/g13-common.h | 96 + g13/g13-syshelp.c | 748 + g13/g13-syshelp.h | 96 + g13/g13.c | 1067 + g13/g13.h | 60 + g13/g13tuple.c | 340 + g13/g13tuple.h | 52 + g13/keyblob.c | 207 + g13/keyblob.h | 162 + g13/mount.c | 265 + g13/mount.h | 31 + g13/mountinfo.c | 198 + g13/mountinfo.h | 40 + g13/runner.c | 539 + g13/runner.h | 76 + g13/server.c | 798 + g13/server.h | 32 + g13/sh-blockdev.c | 152 + g13/sh-cmd.c | 937 + g13/sh-dmcrypt.c | 1036 + g13/suspend.c | 144 + g13/suspend.h | 26 + g13/t-g13tuple.c | 223 + kbx/Makefile.am | 67 + kbx/Makefile.in | 1210 ++ kbx/kbxutil.c | 625 + kbx/keybox-blob.c | 1061 + kbx/keybox-defs.h | 274 + kbx/keybox-dump.c | 805 + kbx/keybox-file.c | 177 + kbx/keybox-init.c | 329 + kbx/keybox-openpgp.c | 522 + kbx/keybox-search-desc.h | 86 + kbx/keybox-search.c | 1227 ++ kbx/keybox-update.c | 800 + kbx/keybox-util.c | 155 + kbx/keybox.h | 142 + kbx/mkerrors | 70 + m4/ChangeLog-2011 | 175 + m4/Makefile.am | 13 + m4/Makefile.in | 501 + m4/autobuild.m4 | 34 + m4/codeset.m4 | 21 + m4/gettext.m4 | 381 + m4/glibc2.m4 | 30 + m4/glibc21.m4 | 30 + m4/gnupg-pth.m4 | 105 + m4/gpg-error.m4 | 121 + m4/iconv.m4 | 180 + m4/intdiv0.m4 | 70 + m4/intl.m4 | 259 + m4/intldir.m4 | 19 + m4/intmax.m4 | 33 + m4/inttypes-pri.m4 | 36 + m4/inttypes.m4 | 27 + m4/inttypes_h.m4 | 26 + m4/isc-posix.m4 | 26 + m4/ksba.m4 | 123 + m4/lcmessage.m4 | 30 + m4/ldap.m4 | 101 + m4/lib-ld.m4 | 110 + m4/lib-link.m4 | 709 + m4/lib-prefix.m4 | 185 + m4/libassuan.m4 | 175 + m4/libcurl.m4 | 253 + m4/libgcrypt.m4 | 143 + m4/libusb.m4 | 69 + m4/lock.m4 | 311 + m4/longdouble.m4 | 31 + m4/nls.m4 | 31 + m4/npth.m4 | 112 + m4/ntbtls.m4 | 137 + m4/pkg.m4 | 214 + m4/po.m4 | 449 + m4/printf-posix.m4 | 44 + m4/progtest.m4 | 92 + m4/readline.m4 | 66 + m4/signed.m4 | 17 + m4/size_max.m4 | 62 + m4/socklen.m4 | 52 + m4/stdint_h.m4 | 26 + m4/sys_socket_h.m4 | 23 + m4/tar-ustar.m4 | 43 + m4/uintmax_t.m4 | 30 + m4/visibility.m4 | 52 + m4/wchar_t.m4 | 20 + m4/wint_t.m4 | 20 + m4/xsize.m4 | 13 + po/ChangeLog-2011 | 330 + po/LINGUAS | 28 + po/Makefile.in.in | 429 + po/Makevars | 41 + po/POTFILES.in | 129 + po/Rules-quot | 47 + po/boldquot.sed | 10 + po/ca.gmo | Bin 0 -> 44832 bytes po/ca.po | 13373 ++++++++++++ po/cs.gmo | Bin 0 -> 214664 bytes po/cs.po | 12496 +++++++++++ po/da.gmo | Bin 0 -> 141545 bytes po/da.po | 12033 +++++++++++ po/de.gmo | Bin 0 -> 233600 bytes po/de.po | 12298 +++++++++++ po/el.gmo | Bin 0 -> 59115 bytes po/el.po | 12999 +++++++++++ po/en@boldquot.gmo | Bin 0 -> 219496 bytes po/en@boldquot.header | 25 + po/en@boldquot.po | 10671 ++++++++++ po/en@quot.gmo | Bin 0 -> 217016 bytes po/en@quot.header | 22 + po/en@quot.po | 10656 +++++++++ po/eo.gmo | Bin 0 -> 28854 bytes po/eo.po | 13034 +++++++++++ po/es.gmo | Bin 0 -> 141904 bytes po/es.po | 13684 ++++++++++++ po/et.gmo | Bin 0 -> 42848 bytes po/et.po | 12861 +++++++++++ po/fi.gmo | Bin 0 -> 44379 bytes po/fi.po | 12978 +++++++++++ po/fr.gmo | Bin 0 -> 219139 bytes po/fr.po | 12401 +++++++++++ po/gl.gmo | Bin 0 -> 44380 bytes po/gl.po | 13281 ++++++++++++ po/gnupg2.pot | 10340 +++++++++ po/hu.gmo | Bin 0 -> 45565 bytes po/hu.po | 12951 +++++++++++ po/id.gmo | Bin 0 -> 42646 bytes po/id.po | 12948 +++++++++++ po/insert-header.sin | 23 + po/it.gmo | Bin 0 -> 44169 bytes po/it.po | 12989 +++++++++++ po/ja.gmo | Bin 0 -> 247113 bytes po/ja.po | 10882 ++++++++++ po/nb.gmo | Bin 0 -> 215958 bytes po/nb.po | 11846 ++++++++++ po/pl.gmo | Bin 0 -> 147253 bytes po/pl.po | 12109 +++++++++++ po/pt.gmo | Bin 0 -> 37337 bytes po/pt.po | 12905 +++++++++++ po/quot.sed | 6 + po/remove-potcdate.sin | 19 + po/ro.gmo | Bin 0 -> 86193 bytes po/ro.po | 12973 +++++++++++ po/ru.gmo | Bin 0 -> 291115 bytes po/ru.po | 10965 ++++++++++ po/sk.gmo | Bin 0 -> 45375 bytes po/sk.po | 12957 +++++++++++ po/stamp-po | 1 + po/sv.gmo | Bin 0 -> 141734 bytes po/sv.po | 12447 +++++++++++ po/tr.gmo | Bin 0 -> 139401 bytes po/tr.po | 12114 +++++++++++ po/uk.gmo | Bin 0 -> 300368 bytes po/uk.po | 11584 ++++++++++ po/zh_CN.gmo | Bin 0 -> 86458 bytes po/zh_CN.po | 12172 +++++++++++ po/zh_TW.gmo | Bin 0 -> 190694 bytes po/zh_TW.po | 11047 ++++++++++ scd/ChangeLog-2011 | 2592 +++ scd/Makefile.am | 51 + scd/Makefile.in | 783 + scd/apdu.c | 4300 ++++ scd/apdu.h | 138 + scd/app-common.h | 229 + scd/app-dinsig.c | 574 + scd/app-geldkarte.c | 409 + scd/app-help.c | 180 + scd/app-nks.c | 1429 ++ scd/app-openpgp.c | 4949 +++++ scd/app-p15.c | 3427 +++ scd/app-sc-hsm.c | 2084 ++ scd/app.c | 959 + scd/atr.c | 290 + scd/atr.h | 27 + scd/ccid-driver.c | 3763 ++++ scd/ccid-driver.h | 138 + scd/command.c | 2396 +++ scd/iso7816.c | 834 + scd/iso7816.h | 119 + scd/scdaemon-w32info.rc | 50 + scd/scdaemon.c | 1323 ++ scd/scdaemon.h | 129 + sm/ChangeLog-2011 | 2968 +++ sm/Makefile.am | 71 + sm/Makefile.in | 818 + sm/base64.c | 700 + sm/call-agent.c | 1351 ++ sm/call-dirmngr.c | 1048 + sm/certchain.c | 2153 ++ sm/certcheck.c | 436 + sm/certdump.c | 858 + sm/certlist.c | 570 + sm/certreqgen-ui.c | 435 + sm/certreqgen.c | 1386 ++ sm/decrypt.c | 585 + sm/delete.c | 182 + sm/encrypt.c | 520 + sm/export.c | 749 + sm/fingerprint.c | 345 + sm/gpgsm-w32info.rc | 50 + sm/gpgsm.c | 2227 ++ sm/gpgsm.h | 450 + sm/import.c | 934 + sm/keydb.c | 1361 ++ sm/keydb.h | 78 + sm/keylist.c | 1586 ++ sm/minip12.c | 2620 +++ sm/minip12.h | 39 + sm/misc.c | 218 + sm/passphrase.c | 90 + sm/passphrase.h | 27 + sm/qualified.c | 325 + sm/server.c | 1496 ++ sm/sign.c | 785 + sm/verify.c | 661 + tests/ChangeLog-2011 | 152 + tests/Makefile.am | 76 + tests/Makefile.in | 908 + tests/asschk.c | 1094 + tests/fake-pinentries/README.txt | 38 + tests/fake-pinentries/fake-pinentry.php | 27 + tests/fake-pinentries/fake-pinentry.pl | 27 + tests/fake-pinentries/fake-pinentry.py | 30 + tests/fake-pinentries/fake-pinentry.sh | 33 + tests/gpgme/Makefile.am | 60 + tests/gpgme/Makefile.in | 612 + tests/gpgme/gpgme-defs.scm | 167 + tests/gpgme/run-tests.scm | 69 + tests/gpgme/setup.scm | 35 + tests/gpgme/wrap.scm | 60 + tests/gpgscm/LICENSE.TinySCHEME | 31 + tests/gpgscm/Makefile.am | 59 + tests/gpgscm/Makefile.in | 843 + tests/gpgscm/Manual.txt | 444 + tests/gpgscm/ffi-private.h | 148 + tests/gpgscm/ffi.c | 1419 ++ tests/gpgscm/ffi.h | 30 + tests/gpgscm/ffi.scm | 49 + tests/gpgscm/init.scm | 796 + tests/gpgscm/lib.scm | 288 + tests/gpgscm/main.c | 303 + tests/gpgscm/opdefines.h | 206 + tests/gpgscm/private.h | 26 + tests/gpgscm/repl.scm | 57 + tests/gpgscm/scheme-config.h | 36 + tests/gpgscm/scheme-private.h | 273 + tests/gpgscm/scheme.c | 5889 +++++ tests/gpgscm/scheme.h | 294 + tests/gpgscm/t-child.c | 74 + tests/gpgscm/t-child.scm | 118 + tests/gpgscm/tests.scm | 660 + tests/inittests | 99 + tests/migrations/Makefile.am | 68 + tests/migrations/Makefile.in | 620 + tests/migrations/common.scm | 54 + tests/migrations/extended-pkf.scm | 46 + tests/migrations/extended-pkf.tar.asc | 220 + tests/migrations/from-classic.scm | 64 + tests/migrations/from-classic.tar.asc | 209 + tests/migrations/issue2276.scm | 32 + tests/migrations/issue2276.tar.asc | 326 + tests/migrations/run-tests.scm | 26 + tests/migrations/setup.scm | 20 + tests/openpgp/4gb-packet.asc | Bin 0 -> 4983 bytes tests/openpgp/4gb-packet.scm | 28 + tests/openpgp/ChangeLog-2011 | 424 + tests/openpgp/Makefile.am | 244 + tests/openpgp/Makefile.in | 915 + tests/openpgp/README | 217 + tests/openpgp/armdetach.scm | 32 + tests/openpgp/armdetachm.scm | 36 + tests/openpgp/armencrypt.scm | 31 + tests/openpgp/armencryptp.scm | 32 + tests/openpgp/armor.scm | 767 + tests/openpgp/armsignencrypt.scm | 31 + tests/openpgp/armsigs.scm | 31 + tests/openpgp/bug1223-bogus.asc | 21 + tests/openpgp/bug1223-good.asc | 20 + tests/openpgp/bug537-test.data.asc | 960 + tests/openpgp/bug894-test.asc | 565 + tests/openpgp/clearsig.scm | 108 + tests/openpgp/compression.scm | 36 + tests/openpgp/conventional-mdc.scm | 50 + tests/openpgp/conventional.scm | 49 + tests/openpgp/decrypt-dsa.scm | 30 + tests/openpgp/decrypt-multifile.scm | 47 + tests/openpgp/decrypt.scm | 30 + tests/openpgp/default-key.scm | 77 + tests/openpgp/defs.scm | 357 + tests/openpgp/delete-keys.scm | 103 + tests/openpgp/detach.scm | 32 + tests/openpgp/detachm.scm | 36 + tests/openpgp/ecc.scm | 250 + tests/openpgp/enarmor.scm | 31 + tests/openpgp/encrypt-dsa.scm | 46 + tests/openpgp/encrypt-multifile.scm | 39 + tests/openpgp/encrypt.scm | 61 + tests/openpgp/encryptp.scm | 32 + tests/openpgp/export.scm | 100 + tests/openpgp/fake-pinentry.c | 316 + tests/openpgp/genkey1024.scm | 53 + tests/openpgp/gpg-agent.conf.tmpl | 3 + tests/openpgp/gpg.conf.tmpl | 4 + tests/openpgp/gpgtar.scm | 94 + tests/openpgp/gpgv-forged-keyring.scm | 68 + .../openpgp/import-revocation-certificate.scm | 37 + tests/openpgp/import.scm | 61 + tests/openpgp/issue2015.scm | 31 + tests/openpgp/issue2346.scm | 28 + tests/openpgp/issue2417.scm | 32 + tests/openpgp/issue2419.scm | 29 + tests/openpgp/key-selection.scm | 83 + tests/openpgp/key-selection/0.asc | 30 + tests/openpgp/key-selection/1.asc | 30 + tests/openpgp/key-selection/2.asc | 30 + tests/openpgp/key-selection/3.asc | 43 + tests/openpgp/key-selection/4.asc | 18 + tests/openpgp/mds.scm | 69 + tests/openpgp/mkdemodirs | 41 + tests/openpgp/multisig.scm | 169 + tests/openpgp/plain-1-pgp.asc | 27 + tests/openpgp/plain-1.asc | 26 + tests/openpgp/plain-1o.asc | 28 + tests/openpgp/plain-2.asc | 31 + tests/openpgp/plain-2o.asc | 36 + tests/openpgp/plain-3.asc | 13 + tests/openpgp/plain-3o.asc | 10 + tests/openpgp/plain-largeo.asc | 4205 ++++ ...FE67F28A52A8AA08FFAED20AF832DA916D1985.asc | 17 + ...6F6AD4C4C803B25470F9104E9F4E6A4CA64255.asc | 12 + ...D40284FF992CD24DC4AAC367037E066FCEE26A.asc | 27 + ...FDB8809B17C5547779F9D205C45F47CE0217CE.asc | 17 + ...F48228FEFF3EC2481B106E0ACA8C465C662CC5.asc | 23 + ...28F20E41B54C2D1234D896096495FF57E08D18.asc | 9 + ...C997C0B8691D41D29A4EC81CCBCF08454E4961.asc | 31 + ...3D8AF79796EE107D645A2787A9D9252F924E6F.asc | 17 + ...9D5ECA70130C2DBB1FC6AC0076BEEEC197716F.asc | 31 + ...9E644892C951A37525654730DD32C202079926.asc | 10 + ...F9172D6FF428C97A0E9AA96F03E8BCE3B2F188.asc | 31 + ...B2D4FA4122C212611048BC5FC31BD44393626E.asc | 21 + ...FFE844087634E62440224908BDE44BEA7EB730.asc | 31 + ...6B7ED0BD4425018FFC54F3921D5467A3AE00EB.asc | 31 + ...F7E2B35832976B50A27A282D9B87E44577EB66.asc | 21 + ...201E28B6FEB2927B321F443205F4724EBE637E.asc | 18 + ...5ABF3EF9EB8D96B91A0B8C2C4401C91C834C34.asc | 14 + ...7CD8F53F2F14C3E2177D1E9D1D11F39513A4A4.asc | 10 + ...747D5F9425E6664F4FFBEED20FBCA79FDED2BD.asc | 20 + ...832820DC9F40751BDCD375BB0945BA33EC6B4C.asc | 17 + ...E710D74409777B7729A7653373D820F67892E0.asc | 17 + ...BAA7144303DF19BB6FDE23781DD3FDD97918D4.asc | 31 + ...A6390E9388CDBAD71EAEA698233FE5E04F001E.asc | 27 + ...05D0AB6AE9655C5A35975939997BBF3325D6DD.asc | 13 + ...FC51AF91F68A2904FBFF62C4F075A4785B803F.asc | 17 + ...60965BF51F67CF80DECE853E0D2D343468571D.asc | 31 + ...9102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3.asc | 27 + ...00E361D34F80868D06879AC21D7A7D4E4FAD76.asc | 31 + ...33B687EB8581AB64D04852A54453E85F3DF62D.asc | 10 + ...692BD59D6640A84C8422573D469F84F3B98E53.asc | 15 + tests/openpgp/pubdemo.asc | 566 + tests/openpgp/pubring.asc | 720 + tests/openpgp/pubring.pkr.asc | 28 + tests/openpgp/quick-key-manipulation.scm | 154 + tests/openpgp/run-tests.scm | 35 + ...57FB607BB4F21C90BB6651BC067AF28BC90111.asc | 45 + tests/openpgp/samplekeys/README | 19 + .../samplekeys/authenticate-only.pub.asc | 31 + .../samplekeys/authenticate-only.sec.asc | 60 + .../openpgp/samplekeys/dda252ebb8ebe1af-1.asc | 29 + .../openpgp/samplekeys/dda252ebb8ebe1af-2.asc | 29 + tests/openpgp/samplekeys/e2e-p256-1-clr.asc | 37 + tests/openpgp/samplekeys/e2e-p256-1-prt.asc | 39 + tests/openpgp/samplekeys/ecc-sample-1-pub.asc | 22 + tests/openpgp/samplekeys/ecc-sample-1-sec.asc | 25 + tests/openpgp/samplekeys/ecc-sample-2-pub.asc | 25 + tests/openpgp/samplekeys/ecc-sample-2-sec.asc | 22 + tests/openpgp/samplekeys/ecc-sample-3-pub.asc | 28 + tests/openpgp/samplekeys/ecc-sample-3-sec.asc | 24 + .../samplekeys/ed25519-cv25519-sample-1.asc | 21 + .../openpgp/samplekeys/eddsa-sample-1-pub.asc | 15 + .../openpgp/samplekeys/eddsa-sample-1-sec.asc | 19 + tests/openpgp/samplekeys/issue2346.gpg | 57 + tests/openpgp/samplekeys/rsa-rsa-sample-1.asc | 38 + tests/openpgp/samplekeys/silent-running.asc | 120 + tests/openpgp/samplekeys/ssh-dsa.key | 12 + tests/openpgp/samplekeys/ssh-ecdsa.key | 5 + tests/openpgp/samplekeys/ssh-ed25519.key | 7 + tests/openpgp/samplekeys/ssh-rsa.key | 27 + tests/openpgp/samplekeys/whats-new-in-2.1.asc | 128 + tests/openpgp/samplemsgs/clearsig-1-key-1.asc | 17 + tests/openpgp/samplemsgs/issue2419.asc | 7 + .../samplemsgs/revoke-2D727CC768697734.asc | 8 + tests/openpgp/samplemsgs/signed-1-key-1.asc | 15 + tests/openpgp/seat.scm | 31 + tests/openpgp/secdemo.asc | 737 + tests/openpgp/secring.asc | 73 + tests/openpgp/secring.skr.asc | 27 + tests/openpgp/setup.scm | 30 + tests/openpgp/shell.scm | 33 + tests/openpgp/signdemokey | 16 + tests/openpgp/signencrypt-dsa.scm | 49 + tests/openpgp/signencrypt.scm | 40 + tests/openpgp/sigs-dsa.scm | 44 + tests/openpgp/sigs.scm | 51 + tests/openpgp/ssh-export.scm | 52 + tests/openpgp/ssh-import.scm | 67 + tests/openpgp/tofu.scm | 453 + tests/openpgp/tofu/conflicting/1C005AF3-1.txt | Bin 0 -> 342 bytes tests/openpgp/tofu/conflicting/1C005AF3-2.txt | Bin 0 -> 338 bytes tests/openpgp/tofu/conflicting/1C005AF3-3.txt | Bin 0 -> 339 bytes tests/openpgp/tofu/conflicting/1C005AF3-4.txt | Bin 0 -> 338 bytes tests/openpgp/tofu/conflicting/1C005AF3-5.txt | Bin 0 -> 339 bytes .../tofu/conflicting/1C005AF3-secret.gpg | Bin 0 -> 2537 bytes tests/openpgp/tofu/conflicting/1C005AF3.gpg | Bin 0 -> 1235 bytes tests/openpgp/tofu/conflicting/B662E42F-1.txt | Bin 0 -> 340 bytes tests/openpgp/tofu/conflicting/B662E42F-2.txt | Bin 0 -> 339 bytes tests/openpgp/tofu/conflicting/B662E42F-3.txt | Bin 0 -> 342 bytes tests/openpgp/tofu/conflicting/B662E42F-4.txt | Bin 0 -> 340 bytes tests/openpgp/tofu/conflicting/B662E42F-5.txt | 1 + .../tofu/conflicting/B662E42F-secret.gpg | Bin 0 -> 2537 bytes tests/openpgp/tofu/conflicting/B662E42F.gpg | Bin 0 -> 1235 bytes tests/openpgp/tofu/conflicting/BE04EB2B-1.txt | Bin 0 -> 340 bytes tests/openpgp/tofu/conflicting/BE04EB2B-2.txt | Bin 0 -> 342 bytes tests/openpgp/tofu/conflicting/BE04EB2B-3.txt | Bin 0 -> 340 bytes tests/openpgp/tofu/conflicting/BE04EB2B-4.txt | Bin 0 -> 342 bytes tests/openpgp/tofu/conflicting/BE04EB2B-5.txt | Bin 0 -> 340 bytes .../tofu/conflicting/BE04EB2B-secret.gpg | Bin 0 -> 2537 bytes tests/openpgp/tofu/conflicting/BE04EB2B.gpg | Bin 0 -> 1235 bytes tests/openpgp/tofu/cross-sigs/871C2247-1.gpg | Bin 0 -> 1173 bytes tests/openpgp/tofu/cross-sigs/871C2247-1.txt | Bin 0 -> 321 bytes tests/openpgp/tofu/cross-sigs/871C2247-2.gpg | Bin 0 -> 1460 bytes tests/openpgp/tofu/cross-sigs/871C2247-2.txt | Bin 0 -> 333 bytes tests/openpgp/tofu/cross-sigs/871C2247-3.gpg | Bin 0 -> 1800 bytes tests/openpgp/tofu/cross-sigs/871C2247-3.txt | Bin 0 -> 334 bytes tests/openpgp/tofu/cross-sigs/871C2247-4.gpg | Bin 0 -> 2087 bytes .../tofu/cross-sigs/871C2247-secret.gpg | Bin 0 -> 2475 bytes tests/openpgp/tofu/cross-sigs/EC38277E-1.gpg | Bin 0 -> 1171 bytes tests/openpgp/tofu/cross-sigs/EC38277E-1.txt | Bin 0 -> 321 bytes tests/openpgp/tofu/cross-sigs/EC38277E-2.gpg | Bin 0 -> 1458 bytes tests/openpgp/tofu/cross-sigs/EC38277E-2.txt | Bin 0 -> 334 bytes tests/openpgp/tofu/cross-sigs/EC38277E-3.txt | Bin 0 -> 334 bytes .../tofu/cross-sigs/EC38277E-secret.gpg | Bin 0 -> 2473 bytes tests/openpgp/tofu/cross-sigs/README | 79 + tests/openpgp/use-exact-key.scm | 69 + tests/openpgp/verify-multifile.scm | 41 + tests/openpgp/verify.scm | 350 + tests/openpgp/version.scm | 25 + tests/pkits/ChangeLog-2011 | 75 + tests/pkits/Makefile.am | 75 + tests/pkits/Makefile.in | 676 + tests/pkits/README | 37 + tests/pkits/basic-certificate-revocation | 31 + tests/pkits/certificate-policies | 31 + tests/pkits/common.sh | 275 + tests/pkits/delta-crls | 31 + tests/pkits/distribution-points | 31 + tests/pkits/import-all-certs | 58 + tests/pkits/import-all-certs.data | 471 + tests/pkits/inhibit-any-policy | 31 + tests/pkits/inhibit-policy-mapping | 31 + tests/pkits/inittests | 108 + tests/pkits/key-usage | 28 + tests/pkits/name-constraints | 31 + tests/pkits/policy-mappings | 31 + tests/pkits/private-certificate-extensions | 31 + tests/pkits/require-explicit-policy | 31 + tests/pkits/runtest | 4 + tests/pkits/signature-verification | 167 + tests/pkits/validate-all-certs | 59 + tests/pkits/validity-periods | 218 + tests/pkits/verifying-basic-constraints | 31 + tests/pkits/verifying-name-chaining | 31 + tests/pkits/verifying-paths-self-issued | 31 + tests/runtest | 3 + ...100C27173EF6E9C4E9A25D3D69F86D37A4F939.key | 18 + ...A638998DFABAC510EA645CE34F9686B2EDF7EA.key | 10 + tests/samplekeys/cert_g10code_pete1.pem | 24 + tests/samplekeys/cert_g10code_test1.pem | 19 + tests/samplekeys/cert_g10code_theo1.pem | 40 + .../steed-self-signing-nonthority.pem | 54 + tests/sm-sign+verify | 73 + tests/sm-verify | 114 + tests/text-1.dsig.pem | 27 + tests/text-1.osig-bad.pem | 45 + tests/text-1.osig.pem | 48 + tests/text-1.txt | 17 + tests/text-2.osig-bad.pem | 28 + tests/text-2.osig.pem | 29 + tests/text-2.txt | 2 + tests/text-3.txt | 2 + tools/ChangeLog-2011 | 1284 ++ tools/Makefile.am | 179 + tools/Makefile.in | 1434 ++ tools/Manifest | 6 + tools/addgnupghome | 122 + tools/applygnupgdefaults | 81 + tools/call-dirmngr.c | 312 + tools/call-dirmngr.h | 31 + tools/ccidmon.c | 882 + tools/clean-sat.c | 35 + tools/convert-from-106 | 55 + tools/gpg-check-pattern.c | 494 + tools/gpg-connect-agent-w32info.rc | 51 + tools/gpg-connect-agent.c | 2258 ++ tools/gpg-wks-client.c | 1158 + tools/gpg-wks-server.c | 1839 ++ tools/gpg-wks.h | 89 + tools/gpg-zip.in | 148 + tools/gpgconf-comp.c | 4066 ++++ tools/gpgconf.c | 758 + tools/gpgconf.h | 91 + tools/gpgparsemail.c | 816 + tools/gpgsplit.c | 890 + tools/gpgtar-create.c | 974 + tools/gpgtar-extract.c | 401 + tools/gpgtar-list.c | 375 + tools/gpgtar.c | 597 + tools/gpgtar.h | 133 + tools/lspgpot | 27 + tools/mail-signed-keys | 114 + tools/make-dns-cert.c | 247 + tools/mime-maker.c | 804 + tools/mime-maker.h | 47 + tools/mime-parser.c | 807 + tools/mime-parser.h | 59 + tools/no-libgcrypt.c | 162 + tools/rfc822parse.c | 1265 ++ tools/rfc822parse.h | 79 + tools/send-mail.c | 129 + tools/send-mail.h | 27 + tools/sockprox.c | 551 + tools/symcryptrun.c | 1023 + tools/watchgnupg.c | 519 + tools/wks-receive.c | 516 + tools/wks-util.c | 413 + 1016 files changed, 844931 insertions(+) create mode 100644 ABOUT-NLS create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 COPYING.CC0 create mode 100644 COPYING.LIB create mode 100644 COPYING.other create mode 100644 ChangeLog create mode 100644 ChangeLog-2011 create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 README.GIT create mode 100644 THANKS create mode 100644 TODO create mode 100644 VERSION create mode 100644 acinclude.m4 create mode 100644 aclocal.m4 create mode 100644 agent/ChangeLog-2011 create mode 100644 agent/Makefile.am create mode 100644 agent/Makefile.in create mode 100644 agent/agent.h create mode 100644 agent/cache.c create mode 100644 agent/call-pinentry.c create mode 100644 agent/call-scd.c create mode 100644 agent/command-ssh.c create mode 100644 agent/command.c create mode 100644 agent/cvt-openpgp.c create mode 100644 agent/cvt-openpgp.h create mode 100644 agent/divert-scd.c create mode 100644 agent/findkey.c create mode 100644 agent/genkey.c create mode 100644 agent/gpg-agent-w32info.rc create mode 100644 agent/gpg-agent.c create mode 100644 agent/learncard.c create mode 100644 agent/pkdecrypt.c create mode 100644 agent/pksign.c create mode 100644 agent/preset-passphrase.c create mode 100644 agent/protect-tool.c create mode 100644 agent/protect.c create mode 100644 agent/t-protect.c create mode 100644 agent/trans.c create mode 100644 agent/trustlist.c create mode 100644 am/cmacros.am create mode 100644 autogen.rc create mode 100755 autogen.sh create mode 100644 build-aux/ChangeLog-2011 create mode 100755 build-aux/compile create mode 100755 build-aux/config.guess create mode 100755 build-aux/config.rpath create mode 100755 build-aux/config.sub create mode 100755 build-aux/depcomp create mode 100755 build-aux/getswdb.sh create mode 100644 build-aux/git-log-fix create mode 100644 build-aux/git-log-footer create mode 100755 build-aux/gitlog-to-changelog create mode 100755 build-aux/install-sh create mode 100755 build-aux/mdate-sh create mode 100755 build-aux/missing create mode 100755 build-aux/mkinstalldirs create mode 100755 build-aux/potomo create mode 100644 build-aux/speedo.mk create mode 100755 build-aux/speedo/patches/atk-1.32.0.patch create mode 100755 build-aux/speedo/patches/libiconv-1.14.patch create mode 100755 build-aux/speedo/patches/pango-1.29.4.patch create mode 100755 build-aux/speedo/patches/sqlite.patch create mode 100644 build-aux/speedo/w32/README.txt create mode 100644 build-aux/speedo/w32/exdll.h create mode 100644 build-aux/speedo/w32/g4wihelp.c create mode 100755 build-aux/speedo/w32/gdk-pixbuf-loaders.cache create mode 100644 build-aux/speedo/w32/gnupg-logo-150x57.bmp create mode 100644 build-aux/speedo/w32/gnupg-logo-164x314.bmp create mode 100644 build-aux/speedo/w32/inst-options.ini create mode 100644 build-aux/speedo/w32/inst.nsi create mode 100755 build-aux/speedo/w32/pango.modules create mode 100644 build-aux/speedo/w32/pkg-copyright.txt create mode 100644 build-aux/speedo/zlib.pc create mode 100644 build-aux/texinfo.tex create mode 100644 common/ChangeLog-2011 create mode 100644 common/ChangeLog-2011.include create mode 100644 common/ChangeLog.jnlib create mode 100644 common/Makefile.am create mode 100644 common/Makefile.in create mode 100644 common/README create mode 100644 common/agent-opt.c create mode 100644 common/argparse.c create mode 100644 common/argparse.h create mode 100644 common/asshelp.c create mode 100644 common/asshelp.h create mode 100644 common/asshelp2.c create mode 100644 common/audit-events.h create mode 100644 common/audit.c create mode 100644 common/audit.h create mode 100644 common/b64dec.c create mode 100644 common/b64enc.c create mode 100644 common/call-gpg.c create mode 100644 common/call-gpg.h create mode 100644 common/ccparray.c create mode 100644 common/ccparray.h create mode 100644 common/common-defs.h create mode 100644 common/convert.c create mode 100644 common/dotlock.c create mode 100644 common/dotlock.h create mode 100644 common/dynload.h create mode 100644 common/exaudit.awk create mode 100644 common/exechelp-posix.c create mode 100644 common/exechelp-w32.c create mode 100644 common/exechelp-w32ce.c create mode 100644 common/exechelp.h create mode 100644 common/exectool.c create mode 100644 common/exectool.h create mode 100644 common/exstatus.awk create mode 100644 common/fwddecl.h create mode 100644 common/gc-opt-flags.h create mode 100644 common/get-passphrase.c create mode 100644 common/get-passphrase.h create mode 100644 common/gettime.c create mode 100644 common/gettime.h create mode 100644 common/gnupg.ico create mode 100644 common/gpgrlhelp.c create mode 100644 common/helpfile.c create mode 100644 common/homedir.c create mode 100644 common/host2net.h create mode 100644 common/i18n.c create mode 100644 common/i18n.h create mode 100644 common/init.c create mode 100644 common/init.h create mode 100644 common/iobuf.c create mode 100644 common/iobuf.h create mode 100644 common/keyserver.h create mode 100644 common/localename.c create mode 100644 common/logging.c create mode 100644 common/logging.h create mode 100644 common/mapstrings.c create mode 100644 common/mbox-util.c create mode 100644 common/mbox-util.h create mode 100644 common/membuf.c create mode 100644 common/membuf.h create mode 100644 common/miscellaneous.c create mode 100644 common/mischelp.c create mode 100644 common/mischelp.h create mode 100644 common/mkdir_p.c create mode 100644 common/mkdir_p.h create mode 100644 common/mkstrtable.awk create mode 100644 common/name-value.c create mode 100644 common/name-value.h create mode 100644 common/openpgp-oid.c create mode 100644 common/openpgpdefs.h create mode 100644 common/percent.c create mode 100644 common/recsel.c create mode 100644 common/recsel.h create mode 100644 common/server-help.c create mode 100644 common/server-help.h create mode 100644 common/session-env.c create mode 100644 common/session-env.h create mode 100644 common/sexp-parse.h create mode 100644 common/sexputil.c create mode 100644 common/shareddefs.h create mode 100644 common/signal.c create mode 100644 common/simple-pwquery.c create mode 100644 common/simple-pwquery.h create mode 100644 common/ssh-utils.c create mode 100644 common/ssh-utils.h create mode 100644 common/status-codes.h create mode 100644 common/status.c create mode 100644 common/status.h create mode 100644 common/stringhelp.c create mode 100644 common/stringhelp.h create mode 100644 common/strlist.c create mode 100644 common/strlist.h create mode 100644 common/sysutils.c create mode 100644 common/sysutils.h create mode 100644 common/t-b64.c create mode 100644 common/t-ccparray.c create mode 100644 common/t-convert.c create mode 100644 common/t-exechelp.c create mode 100644 common/t-exectool.c create mode 100644 common/t-gettime.c create mode 100644 common/t-helpfile.c create mode 100644 common/t-iobuf.c create mode 100644 common/t-mapstrings.c create mode 100644 common/t-mbox-util.c create mode 100644 common/t-name-value.c create mode 100644 common/t-openpgp-oid.c create mode 100644 common/t-percent.c create mode 100644 common/t-recsel.c create mode 100644 common/t-session-env.c create mode 100644 common/t-sexputil.c create mode 100644 common/t-ssh-utils.c create mode 100644 common/t-stringhelp.c create mode 100644 common/t-strlist.c create mode 100644 common/t-support.h create mode 100644 common/t-sysutils.c create mode 100644 common/t-timestuff.c create mode 100644 common/t-w32-reg.c create mode 100644 common/t-zb32.c create mode 100644 common/tlv.c create mode 100644 common/tlv.h create mode 100644 common/ttyio.c create mode 100644 common/ttyio.h create mode 100644 common/types.h create mode 100644 common/userids.c create mode 100644 common/userids.h create mode 100644 common/utf8conv.c create mode 100644 common/utf8conv.h create mode 100644 common/util.h create mode 100644 common/utilproto.h create mode 100644 common/w32-reg.c create mode 100644 common/w32help.h create mode 100644 common/w32info-rc.h.in create mode 100644 common/xasprintf.c create mode 100644 common/xreadline.c create mode 100644 common/yesno.c create mode 100644 common/zb32.c create mode 100644 common/zb32.h create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.ac create mode 100644 dirmngr/ChangeLog-2011 create mode 100644 dirmngr/Makefile.am create mode 100644 dirmngr/Makefile.in create mode 100644 dirmngr/OAUTHORS create mode 100644 dirmngr/ONEWS create mode 100644 dirmngr/cdb.h create mode 100644 dirmngr/cdblib.c create mode 100644 dirmngr/certcache.c create mode 100644 dirmngr/certcache.h create mode 100644 dirmngr/crlcache.c create mode 100644 dirmngr/crlcache.h create mode 100644 dirmngr/crlfetch.c create mode 100644 dirmngr/crlfetch.h create mode 100644 dirmngr/dirmngr-client.c create mode 100644 dirmngr/dirmngr-err.h create mode 100644 dirmngr/dirmngr.c create mode 100644 dirmngr/dirmngr.h create mode 100644 dirmngr/dirmngr_ldap.c create mode 100644 dirmngr/dns-stuff.c create mode 100644 dirmngr/dns-stuff.h create mode 100644 dirmngr/dns.c create mode 100644 dirmngr/dns.h create mode 100644 dirmngr/http.c create mode 100644 dirmngr/http.h create mode 100644 dirmngr/ks-action.c create mode 100644 dirmngr/ks-action.h create mode 100644 dirmngr/ks-engine-finger.c create mode 100644 dirmngr/ks-engine-hkp.c create mode 100644 dirmngr/ks-engine-http.c create mode 100644 dirmngr/ks-engine-kdns.c create mode 100644 dirmngr/ks-engine-ldap.c create mode 100644 dirmngr/ks-engine.h create mode 100644 dirmngr/ldap-parse-uri.c create mode 100644 dirmngr/ldap-parse-uri.h create mode 100644 dirmngr/ldap-url.c create mode 100644 dirmngr/ldap-url.h create mode 100644 dirmngr/ldap-wrapper-ce.c create mode 100644 dirmngr/ldap-wrapper.c create mode 100644 dirmngr/ldap-wrapper.h create mode 100644 dirmngr/ldap.c create mode 100644 dirmngr/ldapserver.c create mode 100644 dirmngr/ldapserver.h create mode 100644 dirmngr/loadswdb.c create mode 100644 dirmngr/misc.c create mode 100644 dirmngr/misc.h create mode 100644 dirmngr/ocsp.c create mode 100644 dirmngr/ocsp.h create mode 100644 dirmngr/server.c create mode 100644 dirmngr/sks-keyservers.netCA.pem create mode 100644 dirmngr/t-dns-stuff.c create mode 100644 dirmngr/t-http.c create mode 100644 dirmngr/t-ldap-parse-uri.c create mode 100644 dirmngr/t-support.h create mode 100644 dirmngr/tls-ca.pem create mode 100644 dirmngr/validate.c create mode 100644 dirmngr/validate.h create mode 100644 dirmngr/w32-ldap-help.h create mode 100644 doc/ChangeLog-2011 create mode 100644 doc/DCO create mode 100644 doc/DETAILS create mode 100644 doc/FAQ create mode 100644 doc/HACKING create mode 100644 doc/KEYSERVER create mode 100644 doc/Makefile.am create mode 100644 doc/Makefile.in create mode 100644 doc/OpenPGP create mode 100644 doc/TRANSLATE create mode 100644 doc/com-certs.pem create mode 100644 doc/contrib.texi create mode 100644 doc/debugging.texi create mode 100644 doc/defsincdate create mode 100644 doc/dirmngr.texi create mode 100644 doc/examples/README create mode 100644 doc/examples/gpgconf.conf create mode 100644 doc/examples/pwpattern.list create mode 100755 doc/examples/scd-event create mode 100644 doc/examples/systemd-user/README create mode 100644 doc/examples/systemd-user/dirmngr.service create mode 100644 doc/examples/systemd-user/dirmngr.socket create mode 100644 doc/examples/systemd-user/gpg-agent-browser.socket create mode 100644 doc/examples/systemd-user/gpg-agent-extra.socket create mode 100644 doc/examples/systemd-user/gpg-agent-ssh.socket create mode 100644 doc/examples/systemd-user/gpg-agent.service create mode 100644 doc/examples/systemd-user/gpg-agent.socket create mode 100644 doc/examples/trustlist.txt create mode 100644 doc/glossary.texi create mode 100644 doc/gnupg-card-architecture.fig create mode 100644 doc/gnupg-card-architecture.pdf create mode 100644 doc/gnupg-card-architecture.png create mode 100644 doc/gnupg-logo-tr.png create mode 100644 doc/gnupg-logo.eps create mode 100644 doc/gnupg-logo.pdf create mode 100644 doc/gnupg-logo.png create mode 100644 doc/gnupg-module-overview.pdf create mode 100644 doc/gnupg-module-overview.png create mode 100644 doc/gnupg-module-overview.svg create mode 100644 doc/gnupg.info create mode 100644 doc/gnupg.info-1 create mode 100644 doc/gnupg.info-2 create mode 100644 doc/gnupg.texi create mode 100644 doc/gnupg7.texi create mode 100644 doc/gpg-agent.texi create mode 100644 doc/gpg.texi create mode 100644 doc/gpgsm.texi create mode 100644 doc/gpgv.texi create mode 100644 doc/gpl.texi create mode 100644 doc/help.be.txt create mode 100644 doc/help.ca.txt create mode 100644 doc/help.cs.txt create mode 100644 doc/help.da.txt create mode 100644 doc/help.de.txt create mode 100644 doc/help.el.txt create mode 100644 doc/help.eo.txt create mode 100644 doc/help.es.txt create mode 100644 doc/help.et.txt create mode 100644 doc/help.fi.txt create mode 100644 doc/help.fr.txt create mode 100644 doc/help.gl.txt create mode 100644 doc/help.hu.txt create mode 100644 doc/help.id.txt create mode 100644 doc/help.it.txt create mode 100644 doc/help.ja.txt create mode 100644 doc/help.nb.txt create mode 100644 doc/help.pl.txt create mode 100644 doc/help.pt.txt create mode 100644 doc/help.pt_BR.txt create mode 100644 doc/help.ro.txt create mode 100644 doc/help.ru.txt create mode 100644 doc/help.sk.txt create mode 100644 doc/help.sv.txt create mode 100644 doc/help.tr.txt create mode 100644 doc/help.txt create mode 100644 doc/help.zh_CN.txt create mode 100644 doc/help.zh_TW.txt create mode 100644 doc/howto-create-a-server-cert.texi create mode 100644 doc/howtos.texi create mode 100644 doc/instguide.texi create mode 100644 doc/mkdefsinc.c create mode 100755 doc/mksamplekeys create mode 100644 doc/opt-homedir.texi create mode 100644 doc/qualified.txt create mode 100644 doc/samplekeys.asc create mode 100644 doc/scdaemon.texi create mode 100644 doc/see-also-note.texi create mode 100644 doc/specify-user-id.texi create mode 100644 doc/sysnotes.texi create mode 100644 doc/tools.texi create mode 100644 doc/whats-new-in-2.1.txt create mode 100644 doc/yat2m.c create mode 100644 g10/ChangeLog-2011 create mode 100644 g10/Makefile.am create mode 100644 g10/Makefile.in create mode 100644 g10/armor.c create mode 100644 g10/build-packet.c create mode 100644 g10/call-agent.c create mode 100644 g10/call-agent.h create mode 100644 g10/call-dirmngr.c create mode 100644 g10/call-dirmngr.h create mode 100644 g10/card-util.c create mode 100644 g10/cipher.c create mode 100644 g10/compress-bz2.c create mode 100644 g10/compress.c create mode 100644 g10/cpr.c create mode 100644 g10/dearmor.c create mode 100644 g10/decrypt-data.c create mode 100644 g10/decrypt.c create mode 100644 g10/dek.h create mode 100644 g10/delkey.c create mode 100644 g10/dirmngr-conf.skel create mode 100644 g10/distsigkey.gpg create mode 100644 g10/ecdh.c create mode 100644 g10/encrypt.c create mode 100644 g10/exec.c create mode 100644 g10/exec.h create mode 100644 g10/export.c create mode 100644 g10/filter.h create mode 100644 g10/free-packet.c create mode 100644 g10/getkey.c create mode 100644 g10/gpg-w32info.rc create mode 100644 g10/gpg.c create mode 100644 g10/gpg.h create mode 100644 g10/gpg.w32-manifest.in create mode 100644 g10/gpgcompose.c create mode 100644 g10/gpgsql.c create mode 100644 g10/gpgsql.h create mode 100644 g10/gpgv.c create mode 100644 g10/helptext.c create mode 100644 g10/import.c create mode 100644 g10/kbnode.c create mode 100644 g10/keydb.c create mode 100644 g10/keydb.h create mode 100644 g10/keyedit.c create mode 100644 g10/keygen.c create mode 100644 g10/keyid.c create mode 100644 g10/keylist.c create mode 100644 g10/keyring.c create mode 100644 g10/keyring.h create mode 100644 g10/keyserver-internal.h create mode 100644 g10/keyserver.c create mode 100644 g10/main.h create mode 100644 g10/mainproc.c create mode 100644 g10/mdfilter.c create mode 100644 g10/migrate.c create mode 100644 g10/misc.c create mode 100644 g10/openfile.c create mode 100644 g10/options.h create mode 100644 g10/options.skel create mode 100644 g10/packet.h create mode 100644 g10/parse-packet.c create mode 100644 g10/passphrase.c create mode 100644 g10/photoid.c create mode 100644 g10/photoid.h create mode 100644 g10/pkclist.c create mode 100644 g10/pkglue.c create mode 100644 g10/pkglue.h create mode 100644 g10/plaintext.c create mode 100644 g10/progress.c create mode 100644 g10/pubkey-enc.c create mode 100644 g10/revoke.c create mode 100644 g10/rmd160.c create mode 100644 g10/rmd160.h create mode 100644 g10/server.c create mode 100644 g10/seskey.c create mode 100644 g10/sig-check.c create mode 100644 g10/sign.c create mode 100644 g10/skclist.c create mode 100644 g10/sqrtu32.c create mode 100644 g10/sqrtu32.h create mode 100644 g10/t-keydb-get-keyblock.c create mode 100644 g10/t-keydb-get-keyblock.gpg create mode 100644 g10/t-keydb-keyring.kbx create mode 100644 g10/t-keydb.c create mode 100644 g10/t-rmd160.c create mode 100644 g10/t-stutter-data.asc create mode 100644 g10/t-stutter.c create mode 100644 g10/tdbdump.c create mode 100644 g10/tdbio.c create mode 100644 g10/tdbio.h create mode 100644 g10/test-stubs.c create mode 100644 g10/test.c create mode 100644 g10/textfilter.c create mode 100644 g10/tofu.c create mode 100644 g10/tofu.h create mode 100644 g10/trust.c create mode 100644 g10/trustdb.c create mode 100644 g10/trustdb.h create mode 100644 g10/verify.c create mode 100644 g13/ChangeLog-2011 create mode 100644 g13/Makefile.am create mode 100644 g13/Makefile.in create mode 100644 g13/backend.c create mode 100644 g13/backend.h create mode 100644 g13/be-dmcrypt.c create mode 100644 g13/be-dmcrypt.h create mode 100644 g13/be-encfs.c create mode 100644 g13/be-encfs.h create mode 100644 g13/be-truecrypt.c create mode 100644 g13/be-truecrypt.h create mode 100644 g13/call-syshelp.c create mode 100644 g13/call-syshelp.h create mode 100644 g13/create.c create mode 100644 g13/create.h create mode 100644 g13/g13-common.c create mode 100644 g13/g13-common.h create mode 100644 g13/g13-syshelp.c create mode 100644 g13/g13-syshelp.h create mode 100644 g13/g13.c create mode 100644 g13/g13.h create mode 100644 g13/g13tuple.c create mode 100644 g13/g13tuple.h create mode 100644 g13/keyblob.c create mode 100644 g13/keyblob.h create mode 100644 g13/mount.c create mode 100644 g13/mount.h create mode 100644 g13/mountinfo.c create mode 100644 g13/mountinfo.h create mode 100644 g13/runner.c create mode 100644 g13/runner.h create mode 100644 g13/server.c create mode 100644 g13/server.h create mode 100644 g13/sh-blockdev.c create mode 100644 g13/sh-cmd.c create mode 100644 g13/sh-dmcrypt.c create mode 100644 g13/suspend.c create mode 100644 g13/suspend.h create mode 100644 g13/t-g13tuple.c create mode 100644 kbx/Makefile.am create mode 100644 kbx/Makefile.in create mode 100644 kbx/kbxutil.c create mode 100644 kbx/keybox-blob.c create mode 100644 kbx/keybox-defs.h create mode 100644 kbx/keybox-dump.c create mode 100644 kbx/keybox-file.c create mode 100644 kbx/keybox-init.c create mode 100644 kbx/keybox-openpgp.c create mode 100644 kbx/keybox-search-desc.h create mode 100644 kbx/keybox-search.c create mode 100644 kbx/keybox-update.c create mode 100644 kbx/keybox-util.c create mode 100644 kbx/keybox.h create mode 100755 kbx/mkerrors create mode 100644 m4/ChangeLog-2011 create mode 100644 m4/Makefile.am create mode 100644 m4/Makefile.in create mode 100644 m4/autobuild.m4 create mode 100644 m4/codeset.m4 create mode 100644 m4/gettext.m4 create mode 100644 m4/glibc2.m4 create mode 100644 m4/glibc21.m4 create mode 100644 m4/gnupg-pth.m4 create mode 100644 m4/gpg-error.m4 create mode 100644 m4/iconv.m4 create mode 100644 m4/intdiv0.m4 create mode 100644 m4/intl.m4 create mode 100644 m4/intldir.m4 create mode 100644 m4/intmax.m4 create mode 100644 m4/inttypes-pri.m4 create mode 100644 m4/inttypes.m4 create mode 100644 m4/inttypes_h.m4 create mode 100644 m4/isc-posix.m4 create mode 100644 m4/ksba.m4 create mode 100644 m4/lcmessage.m4 create mode 100644 m4/ldap.m4 create mode 100644 m4/lib-ld.m4 create mode 100644 m4/lib-link.m4 create mode 100644 m4/lib-prefix.m4 create mode 100644 m4/libassuan.m4 create mode 100644 m4/libcurl.m4 create mode 100644 m4/libgcrypt.m4 create mode 100644 m4/libusb.m4 create mode 100644 m4/lock.m4 create mode 100644 m4/longdouble.m4 create mode 100644 m4/nls.m4 create mode 100644 m4/npth.m4 create mode 100644 m4/ntbtls.m4 create mode 100644 m4/pkg.m4 create mode 100644 m4/po.m4 create mode 100644 m4/printf-posix.m4 create mode 100644 m4/progtest.m4 create mode 100644 m4/readline.m4 create mode 100644 m4/signed.m4 create mode 100644 m4/size_max.m4 create mode 100644 m4/socklen.m4 create mode 100644 m4/stdint_h.m4 create mode 100644 m4/sys_socket_h.m4 create mode 100644 m4/tar-ustar.m4 create mode 100644 m4/uintmax_t.m4 create mode 100644 m4/visibility.m4 create mode 100644 m4/wchar_t.m4 create mode 100644 m4/wint_t.m4 create mode 100644 m4/xsize.m4 create mode 100644 po/ChangeLog-2011 create mode 100644 po/LINGUAS create mode 100644 po/Makefile.in.in create mode 100644 po/Makevars create mode 100644 po/POTFILES.in create mode 100644 po/Rules-quot create mode 100644 po/boldquot.sed create mode 100644 po/ca.gmo create mode 100644 po/ca.po create mode 100644 po/cs.gmo create mode 100644 po/cs.po create mode 100644 po/da.gmo create mode 100644 po/da.po create mode 100644 po/de.gmo create mode 100644 po/de.po create mode 100644 po/el.gmo create mode 100644 po/el.po create mode 100644 po/en@boldquot.gmo create mode 100644 po/en@boldquot.header create mode 100644 po/en@boldquot.po create mode 100644 po/en@quot.gmo create mode 100644 po/en@quot.header create mode 100644 po/en@quot.po create mode 100644 po/eo.gmo create mode 100644 po/eo.po create mode 100644 po/es.gmo create mode 100644 po/es.po create mode 100644 po/et.gmo create mode 100644 po/et.po create mode 100644 po/fi.gmo create mode 100644 po/fi.po create mode 100644 po/fr.gmo create mode 100644 po/fr.po create mode 100644 po/gl.gmo create mode 100644 po/gl.po create mode 100644 po/gnupg2.pot create mode 100644 po/hu.gmo create mode 100644 po/hu.po create mode 100644 po/id.gmo create mode 100644 po/id.po create mode 100644 po/insert-header.sin create mode 100644 po/it.gmo create mode 100644 po/it.po create mode 100644 po/ja.gmo create mode 100644 po/ja.po create mode 100644 po/nb.gmo create mode 100644 po/nb.po create mode 100644 po/pl.gmo create mode 100644 po/pl.po create mode 100644 po/pt.gmo create mode 100644 po/pt.po create mode 100644 po/quot.sed create mode 100644 po/remove-potcdate.sin create mode 100644 po/ro.gmo create mode 100644 po/ro.po create mode 100644 po/ru.gmo create mode 100644 po/ru.po create mode 100644 po/sk.gmo create mode 100644 po/sk.po create mode 100644 po/stamp-po create mode 100644 po/sv.gmo create mode 100644 po/sv.po create mode 100644 po/tr.gmo create mode 100644 po/tr.po create mode 100644 po/uk.gmo create mode 100644 po/uk.po create mode 100644 po/zh_CN.gmo create mode 100644 po/zh_CN.po create mode 100644 po/zh_TW.gmo create mode 100644 po/zh_TW.po create mode 100644 scd/ChangeLog-2011 create mode 100644 scd/Makefile.am create mode 100644 scd/Makefile.in create mode 100644 scd/apdu.c create mode 100644 scd/apdu.h create mode 100644 scd/app-common.h create mode 100644 scd/app-dinsig.c create mode 100644 scd/app-geldkarte.c create mode 100644 scd/app-help.c create mode 100644 scd/app-nks.c create mode 100644 scd/app-openpgp.c create mode 100644 scd/app-p15.c create mode 100644 scd/app-sc-hsm.c create mode 100644 scd/app.c create mode 100644 scd/atr.c create mode 100644 scd/atr.h create mode 100644 scd/ccid-driver.c create mode 100644 scd/ccid-driver.h create mode 100644 scd/command.c create mode 100644 scd/iso7816.c create mode 100644 scd/iso7816.h create mode 100644 scd/scdaemon-w32info.rc create mode 100644 scd/scdaemon.c create mode 100644 scd/scdaemon.h create mode 100644 sm/ChangeLog-2011 create mode 100644 sm/Makefile.am create mode 100644 sm/Makefile.in create mode 100644 sm/base64.c create mode 100644 sm/call-agent.c create mode 100644 sm/call-dirmngr.c create mode 100644 sm/certchain.c create mode 100644 sm/certcheck.c create mode 100644 sm/certdump.c create mode 100644 sm/certlist.c create mode 100644 sm/certreqgen-ui.c create mode 100644 sm/certreqgen.c create mode 100644 sm/decrypt.c create mode 100644 sm/delete.c create mode 100644 sm/encrypt.c create mode 100644 sm/export.c create mode 100644 sm/fingerprint.c create mode 100644 sm/gpgsm-w32info.rc create mode 100644 sm/gpgsm.c create mode 100644 sm/gpgsm.h create mode 100644 sm/import.c create mode 100644 sm/keydb.c create mode 100644 sm/keydb.h create mode 100644 sm/keylist.c create mode 100644 sm/minip12.c create mode 100644 sm/minip12.h create mode 100644 sm/misc.c create mode 100644 sm/passphrase.c create mode 100644 sm/passphrase.h create mode 100644 sm/qualified.c create mode 100644 sm/server.c create mode 100644 sm/sign.c create mode 100644 sm/verify.c create mode 100644 tests/ChangeLog-2011 create mode 100644 tests/Makefile.am create mode 100644 tests/Makefile.in create mode 100644 tests/asschk.c create mode 100644 tests/fake-pinentries/README.txt create mode 100755 tests/fake-pinentries/fake-pinentry.php create mode 100755 tests/fake-pinentries/fake-pinentry.pl create mode 100755 tests/fake-pinentries/fake-pinentry.py create mode 100755 tests/fake-pinentries/fake-pinentry.sh create mode 100644 tests/gpgme/Makefile.am create mode 100644 tests/gpgme/Makefile.in create mode 100644 tests/gpgme/gpgme-defs.scm create mode 100644 tests/gpgme/run-tests.scm create mode 100644 tests/gpgme/setup.scm create mode 100644 tests/gpgme/wrap.scm create mode 100644 tests/gpgscm/LICENSE.TinySCHEME create mode 100644 tests/gpgscm/Makefile.am create mode 100644 tests/gpgscm/Makefile.in create mode 100644 tests/gpgscm/Manual.txt create mode 100644 tests/gpgscm/ffi-private.h create mode 100644 tests/gpgscm/ffi.c create mode 100644 tests/gpgscm/ffi.h create mode 100644 tests/gpgscm/ffi.scm create mode 100644 tests/gpgscm/init.scm create mode 100644 tests/gpgscm/lib.scm create mode 100644 tests/gpgscm/main.c create mode 100644 tests/gpgscm/opdefines.h create mode 100644 tests/gpgscm/private.h create mode 100644 tests/gpgscm/repl.scm create mode 100644 tests/gpgscm/scheme-config.h create mode 100644 tests/gpgscm/scheme-private.h create mode 100644 tests/gpgscm/scheme.c create mode 100644 tests/gpgscm/scheme.h create mode 100644 tests/gpgscm/t-child.c create mode 100644 tests/gpgscm/t-child.scm create mode 100644 tests/gpgscm/tests.scm create mode 100755 tests/inittests create mode 100644 tests/migrations/Makefile.am create mode 100644 tests/migrations/Makefile.in create mode 100644 tests/migrations/common.scm create mode 100755 tests/migrations/extended-pkf.scm create mode 100644 tests/migrations/extended-pkf.tar.asc create mode 100755 tests/migrations/from-classic.scm create mode 100644 tests/migrations/from-classic.tar.asc create mode 100755 tests/migrations/issue2276.scm create mode 100644 tests/migrations/issue2276.tar.asc create mode 100644 tests/migrations/run-tests.scm create mode 100644 tests/migrations/setup.scm create mode 100644 tests/openpgp/4gb-packet.asc create mode 100755 tests/openpgp/4gb-packet.scm create mode 100644 tests/openpgp/ChangeLog-2011 create mode 100644 tests/openpgp/Makefile.am create mode 100644 tests/openpgp/Makefile.in create mode 100644 tests/openpgp/README create mode 100755 tests/openpgp/armdetach.scm create mode 100755 tests/openpgp/armdetachm.scm create mode 100755 tests/openpgp/armencrypt.scm create mode 100755 tests/openpgp/armencryptp.scm create mode 100755 tests/openpgp/armor.scm create mode 100755 tests/openpgp/armsignencrypt.scm create mode 100755 tests/openpgp/armsigs.scm create mode 100644 tests/openpgp/bug1223-bogus.asc create mode 100644 tests/openpgp/bug1223-good.asc create mode 100644 tests/openpgp/bug537-test.data.asc create mode 100644 tests/openpgp/bug894-test.asc create mode 100755 tests/openpgp/clearsig.scm create mode 100755 tests/openpgp/compression.scm create mode 100755 tests/openpgp/conventional-mdc.scm create mode 100755 tests/openpgp/conventional.scm create mode 100755 tests/openpgp/decrypt-dsa.scm create mode 100755 tests/openpgp/decrypt-multifile.scm create mode 100755 tests/openpgp/decrypt.scm create mode 100755 tests/openpgp/default-key.scm create mode 100644 tests/openpgp/defs.scm create mode 100755 tests/openpgp/delete-keys.scm create mode 100755 tests/openpgp/detach.scm create mode 100755 tests/openpgp/detachm.scm create mode 100755 tests/openpgp/ecc.scm create mode 100755 tests/openpgp/enarmor.scm create mode 100755 tests/openpgp/encrypt-dsa.scm create mode 100755 tests/openpgp/encrypt-multifile.scm create mode 100755 tests/openpgp/encrypt.scm create mode 100755 tests/openpgp/encryptp.scm create mode 100755 tests/openpgp/export.scm create mode 100644 tests/openpgp/fake-pinentry.c create mode 100755 tests/openpgp/genkey1024.scm create mode 100644 tests/openpgp/gpg-agent.conf.tmpl create mode 100644 tests/openpgp/gpg.conf.tmpl create mode 100755 tests/openpgp/gpgtar.scm create mode 100755 tests/openpgp/gpgv-forged-keyring.scm create mode 100644 tests/openpgp/import-revocation-certificate.scm create mode 100755 tests/openpgp/import.scm create mode 100755 tests/openpgp/issue2015.scm create mode 100755 tests/openpgp/issue2346.scm create mode 100755 tests/openpgp/issue2417.scm create mode 100755 tests/openpgp/issue2419.scm create mode 100644 tests/openpgp/key-selection.scm create mode 100644 tests/openpgp/key-selection/0.asc create mode 100644 tests/openpgp/key-selection/1.asc create mode 100644 tests/openpgp/key-selection/2.asc create mode 100644 tests/openpgp/key-selection/3.asc create mode 100644 tests/openpgp/key-selection/4.asc create mode 100755 tests/openpgp/mds.scm create mode 100755 tests/openpgp/mkdemodirs create mode 100755 tests/openpgp/multisig.scm create mode 100644 tests/openpgp/plain-1-pgp.asc create mode 100644 tests/openpgp/plain-1.asc create mode 100644 tests/openpgp/plain-1o.asc create mode 100644 tests/openpgp/plain-2.asc create mode 100644 tests/openpgp/plain-2o.asc create mode 100644 tests/openpgp/plain-3.asc create mode 100644 tests/openpgp/plain-3o.asc create mode 100644 tests/openpgp/plain-largeo.asc create mode 100644 tests/openpgp/privkeys/00FE67F28A52A8AA08FFAED20AF832DA916D1985.asc create mode 100644 tests/openpgp/privkeys/0D6F6AD4C4C803B25470F9104E9F4E6A4CA64255.asc create mode 100644 tests/openpgp/privkeys/0DD40284FF992CD24DC4AAC367037E066FCEE26A.asc create mode 100644 tests/openpgp/privkeys/13FDB8809B17C5547779F9D205C45F47CE0217CE.asc create mode 100644 tests/openpgp/privkeys/1DF48228FEFF3EC2481B106E0ACA8C465C662CC5.asc create mode 100644 tests/openpgp/privkeys/1E28F20E41B54C2D1234D896096495FF57E08D18.asc create mode 100644 tests/openpgp/privkeys/2BC997C0B8691D41D29A4EC81CCBCF08454E4961.asc create mode 100644 tests/openpgp/privkeys/343D8AF79796EE107D645A2787A9D9252F924E6F.asc create mode 100644 tests/openpgp/privkeys/3C9D5ECA70130C2DBB1FC6AC0076BEEEC197716F.asc create mode 100644 tests/openpgp/privkeys/449E644892C951A37525654730DD32C202079926.asc create mode 100644 tests/openpgp/privkeys/4DF9172D6FF428C97A0E9AA96F03E8BCE3B2F188.asc create mode 100644 tests/openpgp/privkeys/50B2D4FA4122C212611048BC5FC31BD44393626E.asc create mode 100644 tests/openpgp/privkeys/58FFE844087634E62440224908BDE44BEA7EB730.asc create mode 100644 tests/openpgp/privkeys/6E6B7ED0BD4425018FFC54F3921D5467A3AE00EB.asc create mode 100644 tests/openpgp/privkeys/76F7E2B35832976B50A27A282D9B87E44577EB66.asc create mode 100644 tests/openpgp/privkeys/7E201E28B6FEB2927B321F443205F4724EBE637E.asc create mode 100644 tests/openpgp/privkeys/8B5ABF3EF9EB8D96B91A0B8C2C4401C91C834C34.asc create mode 100644 tests/openpgp/privkeys/9D7CD8F53F2F14C3E2177D1E9D1D11F39513A4A4.asc create mode 100644 tests/openpgp/privkeys/A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD.asc create mode 100644 tests/openpgp/privkeys/A2832820DC9F40751BDCD375BB0945BA33EC6B4C.asc create mode 100644 tests/openpgp/privkeys/ADE710D74409777B7729A7653373D820F67892E0.asc create mode 100644 tests/openpgp/privkeys/B2BAA7144303DF19BB6FDE23781DD3FDD97918D4.asc create mode 100644 tests/openpgp/privkeys/C6A6390E9388CDBAD71EAEA698233FE5E04F001E.asc create mode 100644 tests/openpgp/privkeys/C905D0AB6AE9655C5A35975939997BBF3325D6DD.asc create mode 100644 tests/openpgp/privkeys/CEFC51AF91F68A2904FBFF62C4F075A4785B803F.asc create mode 100644 tests/openpgp/privkeys/CF60965BF51F67CF80DECE853E0D2D343468571D.asc create mode 100644 tests/openpgp/privkeys/D69102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3.asc create mode 100644 tests/openpgp/privkeys/DF00E361D34F80868D06879AC21D7A7D4E4FAD76.asc create mode 100644 tests/openpgp/privkeys/EB33B687EB8581AB64D04852A54453E85F3DF62D.asc create mode 100644 tests/openpgp/privkeys/FD692BD59D6640A84C8422573D469F84F3B98E53.asc create mode 100644 tests/openpgp/pubdemo.asc create mode 100644 tests/openpgp/pubring.asc create mode 100644 tests/openpgp/pubring.pkr.asc create mode 100755 tests/openpgp/quick-key-manipulation.scm create mode 100644 tests/openpgp/run-tests.scm create mode 100644 tests/openpgp/samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc create mode 100644 tests/openpgp/samplekeys/README create mode 100644 tests/openpgp/samplekeys/authenticate-only.pub.asc create mode 100644 tests/openpgp/samplekeys/authenticate-only.sec.asc create mode 100644 tests/openpgp/samplekeys/dda252ebb8ebe1af-1.asc create mode 100644 tests/openpgp/samplekeys/dda252ebb8ebe1af-2.asc create mode 100644 tests/openpgp/samplekeys/e2e-p256-1-clr.asc create mode 100644 tests/openpgp/samplekeys/e2e-p256-1-prt.asc create mode 100644 tests/openpgp/samplekeys/ecc-sample-1-pub.asc create mode 100644 tests/openpgp/samplekeys/ecc-sample-1-sec.asc create mode 100644 tests/openpgp/samplekeys/ecc-sample-2-pub.asc create mode 100644 tests/openpgp/samplekeys/ecc-sample-2-sec.asc create mode 100644 tests/openpgp/samplekeys/ecc-sample-3-pub.asc create mode 100644 tests/openpgp/samplekeys/ecc-sample-3-sec.asc create mode 100644 tests/openpgp/samplekeys/ed25519-cv25519-sample-1.asc create mode 100644 tests/openpgp/samplekeys/eddsa-sample-1-pub.asc create mode 100644 tests/openpgp/samplekeys/eddsa-sample-1-sec.asc create mode 100644 tests/openpgp/samplekeys/issue2346.gpg create mode 100644 tests/openpgp/samplekeys/rsa-rsa-sample-1.asc create mode 100644 tests/openpgp/samplekeys/silent-running.asc create mode 100644 tests/openpgp/samplekeys/ssh-dsa.key create mode 100644 tests/openpgp/samplekeys/ssh-ecdsa.key create mode 100644 tests/openpgp/samplekeys/ssh-ed25519.key create mode 100644 tests/openpgp/samplekeys/ssh-rsa.key create mode 100644 tests/openpgp/samplekeys/whats-new-in-2.1.asc create mode 100644 tests/openpgp/samplemsgs/clearsig-1-key-1.asc create mode 100644 tests/openpgp/samplemsgs/issue2419.asc create mode 100644 tests/openpgp/samplemsgs/revoke-2D727CC768697734.asc create mode 100644 tests/openpgp/samplemsgs/signed-1-key-1.asc create mode 100755 tests/openpgp/seat.scm create mode 100644 tests/openpgp/secdemo.asc create mode 100644 tests/openpgp/secring.asc create mode 100644 tests/openpgp/secring.skr.asc create mode 100755 tests/openpgp/setup.scm create mode 100644 tests/openpgp/shell.scm create mode 100755 tests/openpgp/signdemokey create mode 100755 tests/openpgp/signencrypt-dsa.scm create mode 100755 tests/openpgp/signencrypt.scm create mode 100755 tests/openpgp/sigs-dsa.scm create mode 100755 tests/openpgp/sigs.scm create mode 100755 tests/openpgp/ssh-export.scm create mode 100755 tests/openpgp/ssh-import.scm create mode 100755 tests/openpgp/tofu.scm create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3-1.txt create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3-2.txt create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3-3.txt create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3-4.txt create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3-5.txt create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3-secret.gpg create mode 100644 tests/openpgp/tofu/conflicting/1C005AF3.gpg create mode 100644 tests/openpgp/tofu/conflicting/B662E42F-1.txt create mode 100644 tests/openpgp/tofu/conflicting/B662E42F-2.txt create mode 100644 tests/openpgp/tofu/conflicting/B662E42F-3.txt create mode 100644 tests/openpgp/tofu/conflicting/B662E42F-4.txt create mode 100644 tests/openpgp/tofu/conflicting/B662E42F-5.txt create mode 100644 tests/openpgp/tofu/conflicting/B662E42F-secret.gpg create mode 100644 tests/openpgp/tofu/conflicting/B662E42F.gpg create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B-1.txt create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B-2.txt create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B-3.txt create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B-4.txt create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B-5.txt create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B-secret.gpg create mode 100644 tests/openpgp/tofu/conflicting/BE04EB2B.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-1.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-1.txt create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-2.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-2.txt create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-3.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-3.txt create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-4.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/871C2247-secret.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/EC38277E-1.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/EC38277E-1.txt create mode 100644 tests/openpgp/tofu/cross-sigs/EC38277E-2.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/EC38277E-2.txt create mode 100644 tests/openpgp/tofu/cross-sigs/EC38277E-3.txt create mode 100644 tests/openpgp/tofu/cross-sigs/EC38277E-secret.gpg create mode 100644 tests/openpgp/tofu/cross-sigs/README create mode 100755 tests/openpgp/use-exact-key.scm create mode 100755 tests/openpgp/verify-multifile.scm create mode 100755 tests/openpgp/verify.scm create mode 100755 tests/openpgp/version.scm create mode 100644 tests/pkits/ChangeLog-2011 create mode 100644 tests/pkits/Makefile.am create mode 100644 tests/pkits/Makefile.in create mode 100644 tests/pkits/README create mode 100755 tests/pkits/basic-certificate-revocation create mode 100755 tests/pkits/certificate-policies create mode 100644 tests/pkits/common.sh create mode 100755 tests/pkits/delta-crls create mode 100755 tests/pkits/distribution-points create mode 100755 tests/pkits/import-all-certs create mode 100644 tests/pkits/import-all-certs.data create mode 100755 tests/pkits/inhibit-any-policy create mode 100755 tests/pkits/inhibit-policy-mapping create mode 100755 tests/pkits/inittests create mode 100755 tests/pkits/key-usage create mode 100755 tests/pkits/name-constraints create mode 100755 tests/pkits/policy-mappings create mode 100755 tests/pkits/private-certificate-extensions create mode 100755 tests/pkits/require-explicit-policy create mode 100755 tests/pkits/runtest create mode 100755 tests/pkits/signature-verification create mode 100755 tests/pkits/validate-all-certs create mode 100755 tests/pkits/validity-periods create mode 100755 tests/pkits/verifying-basic-constraints create mode 100755 tests/pkits/verifying-name-chaining create mode 100755 tests/pkits/verifying-paths-self-issued create mode 100755 tests/runtest create mode 100644 tests/samplekeys/32100C27173EF6E9C4E9A25D3D69F86D37A4F939.key create mode 100644 tests/samplekeys/68A638998DFABAC510EA645CE34F9686B2EDF7EA.key create mode 100644 tests/samplekeys/cert_g10code_pete1.pem create mode 100644 tests/samplekeys/cert_g10code_test1.pem create mode 100644 tests/samplekeys/cert_g10code_theo1.pem create mode 100644 tests/samplekeys/steed-self-signing-nonthority.pem create mode 100644 tests/sm-sign+verify create mode 100644 tests/sm-verify create mode 100644 tests/text-1.dsig.pem create mode 100644 tests/text-1.osig-bad.pem create mode 100644 tests/text-1.osig.pem create mode 100644 tests/text-1.txt create mode 100644 tests/text-2.osig-bad.pem create mode 100644 tests/text-2.osig.pem create mode 100644 tests/text-2.txt create mode 100644 tests/text-3.txt create mode 100644 tools/ChangeLog-2011 create mode 100644 tools/Makefile.am create mode 100644 tools/Makefile.in create mode 100644 tools/Manifest create mode 100755 tools/addgnupghome create mode 100755 tools/applygnupgdefaults create mode 100644 tools/call-dirmngr.c create mode 100644 tools/call-dirmngr.h create mode 100644 tools/ccidmon.c create mode 100644 tools/clean-sat.c create mode 100755 tools/convert-from-106 create mode 100644 tools/gpg-check-pattern.c create mode 100644 tools/gpg-connect-agent-w32info.rc create mode 100644 tools/gpg-connect-agent.c create mode 100644 tools/gpg-wks-client.c create mode 100644 tools/gpg-wks-server.c create mode 100644 tools/gpg-wks.h create mode 100644 tools/gpg-zip.in create mode 100644 tools/gpgconf-comp.c create mode 100644 tools/gpgconf.c create mode 100644 tools/gpgconf.h create mode 100644 tools/gpgparsemail.c create mode 100644 tools/gpgsplit.c create mode 100644 tools/gpgtar-create.c create mode 100644 tools/gpgtar-extract.c create mode 100644 tools/gpgtar-list.c create mode 100644 tools/gpgtar.c create mode 100644 tools/gpgtar.h create mode 100755 tools/lspgpot create mode 100755 tools/mail-signed-keys create mode 100644 tools/make-dns-cert.c create mode 100644 tools/mime-maker.c create mode 100644 tools/mime-maker.h create mode 100644 tools/mime-parser.c create mode 100644 tools/mime-parser.h create mode 100644 tools/no-libgcrypt.c create mode 100644 tools/rfc822parse.c create mode 100644 tools/rfc822parse.h create mode 100644 tools/send-mail.c create mode 100644 tools/send-mail.h create mode 100644 tools/sockprox.c create mode 100644 tools/symcryptrun.c create mode 100644 tools/watchgnupg.c create mode 100644 tools/wks-receive.c create mode 100644 tools/wks-util.c diff --git a/ABOUT-NLS b/ABOUT-NLS new file mode 100644 index 0000000..83bc72e --- /dev/null +++ b/ABOUT-NLS @@ -0,0 +1,1068 @@ +1 Notes on the Free Translation Project +*************************************** + +Free software is going international! The Free Translation Project is +a way to get maintainers of free software, translators, and users all +together, so that free software will gradually become able to speak many +languages. A few packages already provide translations for their +messages. + + If you found this `ABOUT-NLS' file inside a distribution, you may +assume that the distributed package does use GNU `gettext' internally, +itself available at your nearest GNU archive site. But you do _not_ +need to install GNU `gettext' prior to configuring, installing or using +this package with messages translated. + + Installers will find here some useful hints. These notes also +explain how users should proceed for getting the programs to use the +available translations. They tell how people wanting to contribute and +work on translations can contact the appropriate team. + + When reporting bugs in the `intl/' directory or bugs which may be +related to internationalization, you should tell about the version of +`gettext' which is used. The information can be found in the +`intl/VERSION' file, in internationalized packages. + +1.1 Quick configuration advice +============================== + +If you want to exploit the full power of internationalization, you +should configure it using + + ./configure --with-included-gettext + +to force usage of internationalizing routines provided within this +package, despite the existence of internationalizing capabilities in the +operating system where this package is being installed. So far, only +the `gettext' implementation in the GNU C library version 2 provides as +many features (such as locale alias, message inheritance, automatic +charset conversion or plural form handling) as the implementation here. +It is also not possible to offer this additional functionality on top +of a `catgets' implementation. Future versions of GNU `gettext' will +very likely convey even more functionality. So it might be a good idea +to change to GNU `gettext' as soon as possible. + + So you need _not_ provide this option if you are using GNU libc 2 or +you have installed a recent copy of the GNU gettext package with the +included `libintl'. + +1.2 INSTALL Matters +=================== + +Some packages are "localizable" when properly installed; the programs +they contain can be made to speak your own native language. Most such +packages use GNU `gettext'. Other packages have their own ways to +internationalization, predating GNU `gettext'. + + By default, this package will be installed to allow translation of +messages. It will automatically detect whether the system already +provides the GNU `gettext' functions. If not, the included GNU +`gettext' library will be used. This library is wholly contained +within this package, usually in the `intl/' subdirectory, so prior +installation of the GNU `gettext' package is _not_ required. +Installers may use special options at configuration time for changing +the default behaviour. The commands: + + ./configure --with-included-gettext + ./configure --disable-nls + +will, respectively, bypass any pre-existing `gettext' to use the +internationalizing routines provided within this package, or else, +_totally_ disable translation of messages. + + When you already have GNU `gettext' installed on your system and run +configure without an option for your new package, `configure' will +probably detect the previously built and installed `libintl.a' file and +will decide to use this. This might not be desirable. You should use +the more recent version of the GNU `gettext' library. I.e. if the file +`intl/VERSION' shows that the library which comes with this package is +more recent, you should use + + ./configure --with-included-gettext + +to prevent auto-detection. + + The configuration process will not test for the `catgets' function +and therefore it will not be used. The reason is that even an +emulation of `gettext' on top of `catgets' could not provide all the +extensions of the GNU `gettext' library. + + Internationalized packages usually have many `po/LL.po' files, where +LL gives an ISO 639 two-letter code identifying the language. Unless +translations have been forbidden at `configure' time by using the +`--disable-nls' switch, all available translations are installed +together with the package. However, the environment variable `LINGUAS' +may be set, prior to configuration, to limit the installed set. +`LINGUAS' should then contain a space separated list of two-letter +codes, stating which languages are allowed. + +1.3 Using This Package +====================== + +As a user, if your language has been installed for this package, you +only have to set the `LANG' environment variable to the appropriate +`LL_CC' combination. If you happen to have the `LC_ALL' or some other +`LC_xxx' environment variables set, you should unset them before +setting `LANG', otherwise the setting of `LANG' will not have the +desired effect. Here `LL' is an ISO 639 two-letter language code, and +`CC' is an ISO 3166 two-letter country code. For example, let's +suppose that you speak German and live in Germany. At the shell +prompt, merely execute `setenv LANG de_DE' (in `csh'), +`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). +This can be done from your `.login' or `.profile' file, once and for +all. + + You might think that the country code specification is redundant. +But in fact, some languages have dialects in different countries. For +example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The +country code serves to distinguish the dialects. + + The locale naming convention of `LL_CC', with `LL' denoting the +language and `CC' denoting the country, is the one use on systems based +on GNU libc. On other systems, some variations of this scheme are +used, such as `LL' or `LL_CC.ENCODING'. You can get the list of +locales supported by your system for your language by running the +command `locale -a | grep '^LL''. + + Not all programs have translations for all languages. By default, an +English message is shown in place of a nonexistent translation. If you +understand other languages, you can set up a priority list of languages. +This is done through a different environment variable, called +`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' +for the purpose of message handling, but you still need to have `LANG' +set to the primary language; this is required by other parts of the +system libraries. For example, some Swedish users who would rather +read translations in German than English for when Swedish is not +available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. + + Special advice for Norwegian users: The language code for Norwegian +bokma*l changed from `no' to `nb' recently (in 2003). During the +transition period, while some message catalogs for this language are +installed under `nb' and some older ones under `no', it's recommended +for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and +older translations are used. + + In the `LANGUAGE' environment variable, but not in the `LANG' +environment variable, `LL_CC' combinations can be abbreviated as `LL' +to denote the language's main dialect. For example, `de' is equivalent +to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' +(Portuguese as spoken in Portugal) in this context. + +1.4 Translating Teams +===================== + +For the Free Translation Project to be a success, we need interested +people who like their own language and write it well, and who are also +able to synergize with other translators speaking the same language. +Each translation team has its own mailing list. The up-to-date list of +teams can be found at the Free Translation Project's homepage, +`http://translationproject.org/', in the "Teams" area. + + If you'd like to volunteer to _work_ at translating messages, you +should become a member of the translating team for your own language. +The subscribing address is _not_ the same as the list itself, it has +`-request' appended. For example, speakers of Swedish can send a +message to `sv-request@li.org', having this message body: + + subscribe + + Keep in mind that team members are expected to participate +_actively_ in translations, or at solving translational difficulties, +rather than merely lurking around. If your team does not exist yet and +you want to start one, or if you are unsure about what to do or how to +get started, please write to `coordinator@translationproject.org' to +reach the coordinator for all translator teams. + + The English team is special. It works at improving and uniformizing +the terminology in use. Proven linguistic skills are praised more than +programming skills, here. + +1.5 Available Packages +====================== + +Languages are not equally supported in all packages. The following +matrix shows the current state of internationalization, as of November +2007. The matrix shows, in regard of each package, for which languages +PO files have been submitted to translation coordination, with a +translation percentage of at least 50%. + + Ready PO files af am ar az be bg bs ca cs cy da de el en en_GB eo + +----------------------------------------------------+ + Compendium | [] [] [] [] | + a2ps | [] [] [] [] [] | + aegis | () | + ant-phone | () | + anubis | [] | + ap-utils | | + aspell | [] [] [] [] [] | + bash | [] | + bfd | | + bibshelf | [] | + binutils | | + bison | [] [] | + bison-runtime | [] | + bluez-pin | [] [] [] [] [] | + cflow | [] | + clisp | [] [] [] | + console-tools | [] [] | + coreutils | [] [] [] [] | + cpio | | + cpplib | [] [] [] | + cryptonit | [] | + dialog | | + diffutils | [] [] [] [] [] [] | + doodle | [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] | + fetchmail | [] [] () [] [] | + findutils | [] | + findutils_stable | [] [] [] | + flex | [] [] [] | + fslint | | + gas | | + gawk | [] [] [] | + gcal | [] | + gcc | [] | + gettext-examples | [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] | + gettext-tools | [] [] | + gip | [] | + gliv | [] [] | + glunarclock | [] | + gmult | [] [] | + gnubiff | () | + gnucash | [] [] () () [] | + gnuedu | | + gnulib | [] | + gnunet | | + gnunet-gtk | | + gnutls | [] | + gpe-aerial | [] [] | + gpe-beam | [] [] | + gpe-calendar | | + gpe-clock | [] [] | + gpe-conf | [] [] | + gpe-contacts | | + gpe-edit | [] | + gpe-filemanager | | + gpe-go | [] | + gpe-login | [] [] | + gpe-ownerinfo | [] [] | + gpe-package | | + gpe-sketchbook | [] [] | + gpe-su | [] [] | + gpe-taskmanager | [] [] | + gpe-timesheet | [] | + gpe-today | [] [] | + gpe-todo | | + gphoto2 | [] [] [] [] | + gprof | [] [] | + gpsdrive | | + gramadoir | [] [] | + grep | [] [] | + gretl | () | + gsasl | | + gss | | + gst-plugins-bad | [] [] | + gst-plugins-base | [] [] | + gst-plugins-good | [] [] [] | + gst-plugins-ugly | [] [] | + gstreamer | [] [] [] [] [] [] [] | + gtick | () | + gtkam | [] [] [] [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] | + herrie | [] | + hylafax | | + idutils | [] [] | + indent | [] [] [] [] | + iso_15924 | | + iso_3166 | [] [] [] [] [] [] [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | [] [] [] | + iso_639 | [] [] [] [] | + jpilot | [] | + jtag | | + jwhois | | + kbd | [] [] [] [] | + keytouch | [] [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + latrine | () | + ld | [] | + leafpad | [] [] [] [] [] | + libc | [] [] [] [] | + libexif | [] | + libextractor | [] | + libgpewidget | [] [] [] | + libgpg-error | [] | + libgphoto2 | [] [] | + libgphoto2_port | [] [] | + libgsasl | | + libiconv | [] [] | + libidn | [] [] [] | + lifelines | [] () | + lilypond | [] | + lingoteach | | + lprng | | + lynx | [] [] [] [] | + m4 | [] [] [] [] | + mailfromd | | + mailutils | [] | + make | [] [] | + man-db | [] [] [] | + minicom | [] [] [] | + nano | [] [] [] | + opcodes | [] | + parted | [] [] | + pilot-qof | | + popt | [] [] [] | + psmisc | [] | + pwdutils | | + qof | | + radius | [] | + recode | [] [] [] [] [] [] | + rpm | [] | + screem | | + scrollkeeper | [] [] [] [] [] [] [] [] | + sed | [] [] [] | + shared-mime-info | [] [] [] [] () [] [] [] | + sharutils | [] [] [] [] [] [] | + shishi | | + skencil | [] () | + solfege | | + soundtracker | [] [] | + sp | [] | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] [] | + texinfo | [] [] [] | + tin | () () | + tuxpaint | [] [] [] [] [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] [] [] | + util-linux-ng | [] [] [] [] | + vorbis-tools | [] | + wastesedge | () | + wdiff | [] [] [] [] | + wget | [] [] [] | + xchat | [] [] [] [] [] [] [] | + xkeyboard-config | [] | + xpad | [] [] [] | + +----------------------------------------------------+ + af am ar az be bg bs ca cs cy da de el en en_GB eo + 6 0 2 1 8 26 2 40 48 2 56 88 15 1 15 18 + + es et eu fa fi fr ga gl gu he hi hr hu id is it + +--------------------------------------------------+ + Compendium | [] [] [] [] [] | + a2ps | [] [] [] () | + aegis | | + ant-phone | [] | + anubis | [] | + ap-utils | [] [] | + aspell | [] [] [] | + bash | [] | + bfd | [] [] | + bibshelf | [] [] [] | + binutils | [] [] [] | + bison | [] [] [] [] [] [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] | + cflow | [] | + clisp | [] [] | + console-tools | | + coreutils | [] [] [] [] [] [] | + cpio | [] [] [] | + cpplib | [] [] | + cryptonit | [] | + dialog | [] [] [] | + diffutils | [] [] [] [] [] [] [] [] [] | + doodle | [] [] | + e2fsprogs | [] [] [] | + enscript | [] [] [] | + fetchmail | [] | + findutils | [] [] [] | + findutils_stable | [] [] [] [] | + flex | [] [] [] | + fslint | | + gas | [] [] | + gawk | [] [] [] [] () | + gcal | [] [] | + gcc | [] | + gettext-examples | [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] | + gettext-tools | [] [] [] [] | + gip | [] [] [] [] | + gliv | () | + glunarclock | [] [] [] | + gmult | [] [] [] | + gnubiff | () () | + gnucash | () () () | + gnuedu | [] | + gnulib | [] [] [] | + gnunet | | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] [] | + gpe-beam | [] [] | + gpe-calendar | | + gpe-clock | [] [] [] [] | + gpe-conf | [] | + gpe-contacts | [] [] | + gpe-edit | [] [] [] [] | + gpe-filemanager | [] | + gpe-go | [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] [] [] [] | + gpe-package | [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] [] | + gpe-taskmanager | [] [] [] | + gpe-timesheet | [] [] [] [] | + gpe-today | [] [] [] [] | + gpe-todo | [] | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] [] [] | + gpsdrive | [] | + gramadoir | [] [] | + grep | [] [] [] | + gretl | [] [] [] () | + gsasl | [] [] | + gss | [] [] | + gst-plugins-bad | [] [] [] [] | + gst-plugins-base | [] [] [] [] | + gst-plugins-good | [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] | + gstreamer | [] [] [] | + gtick | [] [] [] | + gtkam | [] [] [] [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] [] [] [] [] [] [] | + herrie | [] | + hylafax | | + idutils | [] [] [] [] [] | + indent | [] [] [] [] [] [] [] [] [] [] | + iso_15924 | [] | + iso_3166 | [] [] [] [] [] [] [] [] [] [] [] [] [] | + iso_3166_2 | [] | + iso_4217 | [] [] [] [] [] [] | + iso_639 | [] [] [] [] [] [] | + jpilot | [] [] | + jtag | [] | + jwhois | [] [] [] [] [] | + kbd | [] [] | + keytouch | [] [] [] | + keytouch-editor | [] | + keytouch-keyboa... | [] [] | + latrine | [] [] | + ld | [] [] [] [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] | + libgpewidget | [] [] [] [] [] | + libgpg-error | [] | + libgphoto2 | [] [] [] | + libgphoto2_port | [] [] | + libgsasl | [] [] | + libiconv | [] [] [] | + libidn | [] [] | + lifelines | () | + lilypond | [] [] [] | + lingoteach | [] [] [] | + lprng | | + lynx | [] [] [] | + m4 | [] [] [] [] | + mailfromd | | + mailutils | [] [] | + make | [] [] [] [] [] [] [] [] | + man-db | [] | + minicom | [] [] [] [] | + nano | [] [] [] [] [] [] [] | + opcodes | [] [] [] [] | + parted | [] [] [] | + pilot-qof | | + popt | [] [] [] [] | + psmisc | [] [] | + pwdutils | | + qof | [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] [] | + rpm | [] [] | + screem | | + scrollkeeper | [] [] [] | + sed | [] [] [] [] [] | + shared-mime-info | [] [] [] [] [] [] | + sharutils | [] [] [] [] [] [] [] [] | + shishi | [] | + skencil | [] [] | + solfege | [] | + soundtracker | [] [] [] | + sp | [] | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] [] [] [] [] | + texinfo | [] [] [] | + tin | [] () | + tuxpaint | [] [] | + unicode-han-tra... | | + unicode-transla... | [] [] | + util-linux | [] [] [] [] [] [] [] | + util-linux-ng | [] [] [] [] [] [] [] | + vorbis-tools | | + wastesedge | () | + wdiff | [] [] [] [] [] [] [] [] | + wget | [] [] [] [] [] [] [] [] | + xchat | [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] | + xpad | [] [] [] | + +--------------------------------------------------+ + es et eu fa fi fr ga gl gu he hi hr hu id is it + 85 22 14 2 48 101 61 12 2 8 2 6 53 29 1 52 + + ja ka ko ku ky lg lt lv mk mn ms mt nb ne nl nn + +--------------------------------------------------+ + Compendium | [] | + a2ps | () [] [] | + aegis | () | + ant-phone | [] | + anubis | [] [] [] | + ap-utils | [] | + aspell | [] [] | + bash | [] | + bfd | | + bibshelf | [] | + binutils | | + bison | [] [] [] | + bison-runtime | [] [] [] | + bluez-pin | [] [] [] | + cflow | | + clisp | [] | + console-tools | | + coreutils | [] | + cpio | [] | + cpplib | [] | + cryptonit | [] | + dialog | [] [] | + diffutils | [] [] [] | + doodle | | + e2fsprogs | [] | + enscript | [] | + fetchmail | [] [] | + findutils | [] | + findutils_stable | [] | + flex | [] [] | + fslint | | + gas | | + gawk | [] [] | + gcal | | + gcc | | + gettext-examples | [] [] [] | + gettext-runtime | [] [] [] | + gettext-tools | [] [] | + gip | [] [] | + gliv | [] | + glunarclock | [] [] | + gmult | [] [] [] | + gnubiff | | + gnucash | () () () | + gnuedu | | + gnulib | [] [] | + gnunet | | + gnunet-gtk | | + gnutls | [] | + gpe-aerial | [] | + gpe-beam | [] | + gpe-calendar | [] | + gpe-clock | [] [] [] | + gpe-conf | [] [] [] | + gpe-contacts | [] | + gpe-edit | [] [] [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] | + gpe-taskmanager | [] [] [] [] | + gpe-timesheet | [] | + gpe-today | [] [] | + gpe-todo | [] | + gphoto2 | [] [] | + gprof | [] | + gpsdrive | [] | + gramadoir | () | + grep | [] [] | + gretl | | + gsasl | [] | + gss | | + gst-plugins-bad | [] | + gst-plugins-base | [] | + gst-plugins-good | [] | + gst-plugins-ugly | [] | + gstreamer | [] | + gtick | [] | + gtkam | [] [] | + gtkorphan | [] | + gtkspell | [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] | + herrie | [] | + hylafax | | + idutils | [] | + indent | [] [] | + iso_15924 | [] | + iso_3166 | [] [] [] [] [] [] [] [] | + iso_3166_2 | [] | + iso_4217 | [] [] [] | + iso_639 | [] [] [] [] | + jpilot | () () | + jtag | | + jwhois | [] | + kbd | [] | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | | + latrine | [] | + ld | | + leafpad | [] [] | + libc | [] [] [] | + libexif | | + libextractor | | + libgpewidget | [] | + libgpg-error | | + libgphoto2 | [] | + libgphoto2_port | [] | + libgsasl | [] | + libiconv | [] | + libidn | [] [] | + lifelines | [] | + lilypond | [] | + lingoteach | [] | + lprng | | + lynx | [] [] | + m4 | [] [] | + mailfromd | | + mailutils | | + make | [] [] [] | + man-db | | + minicom | [] | + nano | [] [] [] | + opcodes | [] | + parted | [] [] | + pilot-qof | | + popt | [] [] [] | + psmisc | [] [] [] | + pwdutils | | + qof | | + radius | | + recode | [] | + rpm | [] [] | + screem | [] | + scrollkeeper | [] [] [] [] | + sed | [] [] | + shared-mime-info | [] [] [] [] [] [] [] | + sharutils | [] [] | + shishi | | + skencil | | + solfege | () () | + soundtracker | | + sp | () | + system-tools-ba... | [] [] [] [] | + tar | [] [] [] | + texinfo | [] [] | + tin | | + tuxpaint | () [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] | + util-linux-ng | [] [] | + vorbis-tools | | + wastesedge | [] | + wdiff | [] [] | + wget | [] [] | + xchat | [] [] [] [] | + xkeyboard-config | [] [] [] | + xpad | [] [] [] | + +--------------------------------------------------+ + ja ka ko ku ky lg lt lv mk mn ms mt nb ne nl nn + 51 2 25 3 2 0 6 0 2 2 20 0 11 1 103 6 + + or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta + +--------------------------------------------------+ + Compendium | [] [] [] [] [] | + a2ps | () [] [] [] [] [] [] | + aegis | () () | + ant-phone | [] [] | + anubis | [] [] [] | + ap-utils | () | + aspell | [] [] [] | + bash | [] [] | + bfd | | + bibshelf | [] | + binutils | [] [] | + bison | [] [] [] [] [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] [] [] [] [] | + cflow | [] | + clisp | [] | + console-tools | [] | + coreutils | [] [] [] [] | + cpio | [] [] [] | + cpplib | [] | + cryptonit | [] [] | + dialog | [] | + diffutils | [] [] [] [] [] [] | + doodle | [] [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] [] | + fetchmail | [] [] [] | + findutils | [] [] [] | + findutils_stable | [] [] [] [] [] [] | + flex | [] [] [] [] [] | + fslint | [] | + gas | | + gawk | [] [] [] [] | + gcal | [] | + gcc | [] [] | + gettext-examples | [] [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] [] [] | + gettext-tools | [] [] [] [] [] [] [] | + gip | [] [] [] [] | + gliv | [] [] [] [] [] [] | + glunarclock | [] [] [] [] [] [] | + gmult | [] [] [] [] | + gnubiff | () [] | + gnucash | () [] | + gnuedu | | + gnulib | [] [] [] | + gnunet | | + gnunet-gtk | [] | + gnutls | [] [] | + gpe-aerial | [] [] [] [] [] [] [] | + gpe-beam | [] [] [] [] [] [] [] | + gpe-calendar | [] [] [] [] | + gpe-clock | [] [] [] [] [] [] [] [] | + gpe-conf | [] [] [] [] [] [] [] | + gpe-contacts | [] [] [] [] [] | + gpe-edit | [] [] [] [] [] [] [] [] [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] [] [] [] [] [] | + gpe-login | [] [] [] [] [] [] [] [] | + gpe-ownerinfo | [] [] [] [] [] [] [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] [] [] [] [] [] [] | + gpe-su | [] [] [] [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] [] [] [] [] | + gpe-timesheet | [] [] [] [] [] [] [] [] | + gpe-today | [] [] [] [] [] [] [] [] | + gpe-todo | [] [] [] [] | + gphoto2 | [] [] [] [] [] [] | + gprof | [] [] [] | + gpsdrive | [] [] | + gramadoir | [] [] | + grep | [] [] [] [] | + gretl | [] [] [] | + gsasl | [] [] [] | + gss | [] [] [] [] | + gst-plugins-bad | [] [] [] | + gst-plugins-base | [] [] | + gst-plugins-good | [] [] | + gst-plugins-ugly | [] [] [] | + gstreamer | [] [] [] [] | + gtick | [] | + gtkam | [] [] [] [] [] | + gtkorphan | [] | + gtkspell | [] [] [] [] [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] [] | + herrie | [] [] [] | + hylafax | | + idutils | [] [] [] [] [] | + indent | [] [] [] [] [] [] [] | + iso_15924 | | + iso_3166 | [] [] [] [] [] [] [] [] [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | [] [] [] [] [] [] [] | + iso_639 | [] [] [] [] [] [] [] | + jpilot | | + jtag | [] | + jwhois | [] [] [] [] | + kbd | [] [] [] | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + latrine | | + ld | [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] | + libexif | [] [] | + libextractor | [] [] | + libgpewidget | [] [] [] [] [] [] [] [] | + libgpg-error | [] [] [] | + libgphoto2 | [] | + libgphoto2_port | [] [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] [] | + libidn | [] [] () | + lifelines | [] [] | + lilypond | | + lingoteach | [] | + lprng | [] | + lynx | [] [] [] | + m4 | [] [] [] [] [] | + mailfromd | [] | + mailutils | [] [] [] | + make | [] [] [] [] | + man-db | [] [] [] [] | + minicom | [] [] [] [] [] | + nano | [] [] [] [] | + opcodes | [] [] | + parted | [] | + pilot-qof | | + popt | [] [] [] [] | + psmisc | [] [] | + pwdutils | [] [] | + qof | [] [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] | + rpm | [] [] [] [] | + screem | | + scrollkeeper | [] [] [] [] [] [] [] | + sed | [] [] [] [] [] [] [] [] [] | + shared-mime-info | [] [] [] [] [] [] | + sharutils | [] [] [] [] | + shishi | [] | + skencil | [] [] [] | + solfege | [] | + soundtracker | [] [] | + sp | | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] [] [] [] | + texinfo | [] [] [] [] | + tin | () | + tuxpaint | [] [] [] [] [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] [] [] | + util-linux-ng | [] [] [] [] | + vorbis-tools | [] | + wastesedge | | + wdiff | [] [] [] [] [] [] [] | + wget | [] [] [] [] | + xchat | [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] | + xpad | [] [] [] | + +--------------------------------------------------+ + or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta + 0 5 77 31 53 4 58 72 3 45 46 9 45 122 3 + + tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu + +---------------------------------------------------+ + Compendium | [] [] [] [] | 19 + a2ps | [] [] [] | 19 + aegis | [] | 1 + ant-phone | [] [] | 6 + anubis | [] [] [] | 11 + ap-utils | () [] | 4 + aspell | [] [] [] | 16 + bash | [] | 6 + bfd | | 2 + bibshelf | [] | 7 + binutils | [] [] [] [] | 9 + bison | [] [] [] [] | 20 + bison-runtime | [] [] [] [] | 18 + bluez-pin | [] [] [] [] [] [] | 28 + cflow | [] [] | 5 + clisp | | 9 + console-tools | [] [] | 5 + coreutils | [] [] [] | 18 + cpio | [] [] [] [] | 11 + cpplib | [] [] [] [] [] | 12 + cryptonit | [] | 6 + dialog | [] [] [] | 9 + diffutils | [] [] [] [] [] | 29 + doodle | [] | 6 + e2fsprogs | [] [] | 10 + enscript | [] [] [] | 16 + fetchmail | [] [] | 12 + findutils | [] [] [] | 11 + findutils_stable | [] [] [] [] | 18 + flex | [] [] | 15 + fslint | [] | 2 + gas | [] | 3 + gawk | [] [] [] | 16 + gcal | [] | 5 + gcc | [] [] [] | 7 + gettext-examples | [] [] [] [] [] [] | 29 + gettext-runtime | [] [] [] [] [] [] | 28 + gettext-tools | [] [] [] [] [] | 20 + gip | [] [] | 13 + gliv | [] [] | 11 + glunarclock | [] [] [] | 15 + gmult | [] [] [] [] | 16 + gnubiff | [] | 2 + gnucash | () [] | 5 + gnuedu | [] | 2 + gnulib | [] | 10 + gnunet | | 0 + gnunet-gtk | [] [] | 3 + gnutls | | 4 + gpe-aerial | [] [] | 14 + gpe-beam | [] [] | 14 + gpe-calendar | [] [] | 7 + gpe-clock | [] [] [] [] | 21 + gpe-conf | [] [] [] | 16 + gpe-contacts | [] [] | 10 + gpe-edit | [] [] [] [] [] | 22 + gpe-filemanager | [] [] | 7 + gpe-go | [] [] [] [] | 19 + gpe-login | [] [] [] [] [] | 21 + gpe-ownerinfo | [] [] [] [] | 21 + gpe-package | [] | 6 + gpe-sketchbook | [] [] | 16 + gpe-su | [] [] [] [] | 21 + gpe-taskmanager | [] [] [] [] | 21 + gpe-timesheet | [] [] [] [] | 18 + gpe-today | [] [] [] [] [] | 21 + gpe-todo | [] [] | 8 + gphoto2 | [] [] [] [] | 21 + gprof | [] [] | 13 + gpsdrive | [] | 5 + gramadoir | [] | 7 + grep | [] | 12 + gretl | | 6 + gsasl | [] [] [] | 9 + gss | [] | 7 + gst-plugins-bad | [] [] [] | 13 + gst-plugins-base | [] [] | 11 + gst-plugins-good | [] [] [] [] [] | 16 + gst-plugins-ugly | [] [] [] | 13 + gstreamer | [] [] [] | 18 + gtick | [] [] | 7 + gtkam | [] | 16 + gtkorphan | [] | 7 + gtkspell | [] [] [] [] [] [] | 27 + gutenprint | | 4 + hello | [] [] [] [] [] | 38 + herrie | [] [] | 8 + hylafax | | 0 + idutils | [] [] | 15 + indent | [] [] [] [] [] | 28 + iso_15924 | [] [] | 4 + iso_3166 | [] [] [] [] [] [] [] [] [] | 54 + iso_3166_2 | [] [] | 4 + iso_4217 | [] [] [] [] [] | 24 + iso_639 | [] [] [] [] [] | 26 + jpilot | [] [] [] [] | 7 + jtag | [] | 3 + jwhois | [] [] [] | 13 + kbd | [] [] [] | 13 + keytouch | [] | 8 + keytouch-editor | [] | 5 + keytouch-keyboa... | [] | 5 + latrine | [] [] | 5 + ld | [] [] [] [] | 10 + leafpad | [] [] [] [] [] | 24 + libc | [] [] [] | 19 + libexif | [] | 5 + libextractor | [] | 5 + libgpewidget | [] [] [] | 20 + libgpg-error | [] | 6 + libgphoto2 | [] [] | 9 + libgphoto2_port | [] [] [] | 11 + libgsasl | [] | 8 + libiconv | [] [] | 11 + libidn | [] [] | 11 + lifelines | | 4 + lilypond | [] | 6 + lingoteach | [] | 6 + lprng | [] | 2 + lynx | [] [] [] | 15 + m4 | [] [] [] | 18 + mailfromd | [] [] | 3 + mailutils | [] [] | 8 + make | [] [] [] | 20 + man-db | [] | 9 + minicom | [] | 14 + nano | [] [] [] | 20 + opcodes | [] [] | 10 + parted | [] [] [] | 11 + pilot-qof | [] | 1 + popt | [] [] [] [] | 18 + psmisc | [] [] | 10 + pwdutils | [] | 3 + qof | [] | 4 + radius | [] [] | 7 + recode | [] [] [] | 25 + rpm | [] [] [] [] | 13 + screem | [] | 2 + scrollkeeper | [] [] [] [] | 26 + sed | [] [] [] [] | 23 + shared-mime-info | [] [] [] | 29 + sharutils | [] [] [] | 23 + shishi | [] | 3 + skencil | [] | 7 + solfege | [] | 3 + soundtracker | [] [] | 9 + sp | [] | 3 + system-tools-ba... | [] [] [] [] [] [] [] | 38 + tar | [] [] [] | 17 + texinfo | [] [] [] | 15 + tin | | 1 + tuxpaint | [] [] [] | 19 + unicode-han-tra... | | 0 + unicode-transla... | | 2 + util-linux | [] [] [] | 20 + util-linux-ng | [] [] [] | 20 + vorbis-tools | [] [] | 4 + wastesedge | | 1 + wdiff | [] [] | 23 + wget | [] [] [] | 20 + xchat | [] [] [] [] | 29 + xkeyboard-config | [] [] [] | 14 + xpad | [] [] [] | 15 + +---------------------------------------------------+ + 76 teams tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu + 163 domains 0 3 1 74 51 0 143 21 1 57 7 45 0 2036 + + Some counters in the preceding matrix are higher than the number of +visible blocks let us expect. This is because a few extra PO files are +used for implementing regional variants of languages, or language +dialects. + + For a PO file in the matrix above to be effective, the package to +which it applies should also have been internationalized and +distributed as such by its maintainer. There might be an observable +lag between the mere existence a PO file and its wide availability in a +distribution. + + If November 2007 seems to be old, you may fetch a more recent copy +of this `ABOUT-NLS' file on most GNU archive sites. The most +up-to-date matrix with full percentage details can be found at +`http://translationproject.org/extra/matrix.html'. + +1.6 Using `gettext' in new packages +=================================== + +If you are writing a freely available program and want to +internationalize it you are welcome to use GNU `gettext' in your +package. Of course you have to respect the GNU Library General Public +License which covers the use of the GNU `gettext' library. This means +in particular that even non-free programs can use `libintl' as a shared +library, whereas only free software can use `libintl' as a static +library or use modified versions of `libintl'. + + Once the sources are changed appropriately and the setup can handle +the use of `gettext' the only thing missing are the translations. The +Free Translation Project is also available for packages which are not +developed inside the GNU project. Therefore the information given above +applies also for every other Free Software Project. Contact +`coordinator@translationproject.org' to make the `.pot' files available +to the translation teams. + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..50b2095 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,256 @@ +Program: GnuPG +Homepage: https://www.gnupg.org +Download: ftp://ftp.gnupg.org/gcrypt/gnupg/ +Repository: git://git.gnupg.org/gnupg.git +Bug reports: https://bugs.gnupg.org +Security related bug reports: +Maintainer: Werner Koch +License: GPLv3+ + +GnuPG is free software. See the files COPYING for copying conditions. +License copyright years may be listed using range notation, e.g., +2000-2013, indicating that every year in the range, inclusive, is a +copyrightable year that would otherwise be listed individually. + +List of Copyright holders +========================= + + Copyright (C) 1997-2016 Werner Koch + Copyright (C) 1994-2016 Free Software Foundation, Inc. + Copyright (C) 2003-2013,2015-2016 g10 Code GmbH + Copyright (C) 2002 Klarälvdalens Datakonsult AB + Copyright (C) 1995-1997, 2000-2007 Ulrich Drepper + Copyright (C) 1994 X Consortium + Copyright (C) 1998 by The Internet Society. + Copyright (C) 1998-2004 The OpenLDAP Foundation + Copyright (C) 1998-2004 Kurt D. Zeilenga. + Copyright (C) 1998-2004 Net Boolean Incorporated. + Copyright (C) 2001-2004 IBM Corporation. + Copyright (C) 1999-2003 Howard Y.H. Chu. + Copyright (C) 1999-2003 Symas Corporation. + Copyright (C) 1998-2003 Hallvard B. Furuseth. + Copyright (C) 1992-1996 Regents of the University of Michigan. + Copyright (C) 2000 Dimitrios Souflis + Copyright (C) 2008,2009,2010,2012-2016 William Ahern + + +Authors with a FSF copyright assignment +======================================= + +Ales Nyakhaychyk Translations [be] + +Andrey Jivsov Assigns past and future changes for ECC. + (g10/ecdh.c. other changes to support ECC) + +Ben Kibbey Assigns past and future changes. + +Birger Langkjer Translations [da] + +Maxim Britov Translations [ru] + +Daniel Resare Translations [sv] +Per Tunedal Translations [sv] +Daniel Nylander Translations [sv] + +Daiki Ueno Assigns Past and Future Changes. + (changed:passphrase.c and related code) + +David Shaw Assigns past and future changes. + (all in keyserver/, + a lot of changes in g10/ see the ChangeLog, + bug fixes here and there) + +Dokianakis Theofanis Translations [el] + +Edmund GRIMLEY EVANS Translations [eo] + +Florian Weimer Assigns past and future changes + (changed:g10/parse-packet.c, include/iobuf.h, util/iobuf.c) + +g10 Code GmbH Assigns past and future changes + (all work since 2001 as indicated by mail addresses in ChangeLogs) + +Gaël Quéri Translations [fr] + (fixed a lot of typos) + +Gregory Steuck Translations [ru] + +Nagy Ferenc László Translations [hu] + +Ivo Timmermans Translations [nl] + +Jacobo Tarri'o Barreiro Translations [gl] + +Janusz Aleksander Urbanowicz Translations [pl] +Jakub Bogusz Translations [pl] + +Jedi Lin Translations [zh-tw] + +Jouni Hiltunen Translations [fi] +Tommi Vainikainen Translations [fi] + +Laurentiu Buzdugan Translations [ro] + +Magda Procha'zkova' Translations [cs] + +Michael Roth Assigns changes. + (wrote cipher/des.c., changes and bug fixes all over the place) + +Michal Majer Translations [sk] + +Marco d'Itri Translations [it] + +Marcus Brinkmann + (gpgconf and fixes all over the place) + +Matthew Skala Disclaimer + (wrote cipher/twofish.c) + +Moritz Schulte + (ssh support gpg-agent) + +Niklas Hernaeus Disclaimer + (weak key patches) + +Nilgun Belma Buguner Translations [tr] + +Nils Ellmenreich + Assigns past and future changes + (configure.in, cipher/rndlinux.c, FAQ) + +Paul Eggert + (configuration macros for LFS) + +Pavel I. Shajdo Translations [ru] + (man pages) + +Pedro Morais Translations [pt_PT] + +Rémi Guyomarch Assigns past and future changes. + (g10/compress.c, g10/encr-data.c, + g10/free-packet.c, g10/mdfilter.c, g10/plaintext.c, util/iobuf.c) + +Stefan Bellon Assigns past and future changes. + (All patches to support RISC OS) + +Timo Schulz Assigns past and future changes. + (util/w32reg.c, g10/passphrase.c, g10/hkp.c) + +Tedi Heriyanto Translations [id] + +Thiago Jung Bauermann Translations [pt_BR] +Rafael Caetano dos Santos Translations [pt_BR] + +Toomas Soome Translations [et] + +Urko Lusa Translations [es_ES] + +Walter Koch Translations [de] + +Werner Koch Assigns GNU Privacy Guard and future changes. + (started the whole thing, wrote the S/MIME extensions, the + smartcard daemon and the gpg-agent) + +Yosiaki IIDA Translations [ja] + +Yuri Chornoivan, yurchor at ukr dot net: Translations [uk] + +Yutaka Niibe Assigns Past and Future Changes + (scd/) + + +Authors with a DCO +================== + +Andre Heinecke +2014-09-19:4525694.FcpLvWDUFT@esus: + +Andreas Schwier +2014-07-22:53CED1D8.1010306@cardcontact.de: + +Christian Aistleitner +2013-05-26:20130626112332.GA2228@quelltextlich.at: + +Damien Goutte-Gattat +2015-01-17:54BA49AA.2040708@incenp.org: + +Daniel Kahn Gillmor +2014-09-24:87oau6w9q7.fsf@alice.fifthhorseman.net: + +Hans of Guardian +2013-06-26:D84473D7-F3F7-43D5-A9CE-16580B88D574@guardianproject.info: + +Jonas Borgström +2013-08-29:521F1E7A.5080602@borgstrom.se: + +Joshua Rogers +2014-12-22:5497FE75.7010503@internot.info: + +Kyle Butt +2013-05-29:CAAODAYLbCtqOG6msLLL0UTdASKWT6u2ptxsgUQ1JpusBESBoNQ@mail.gmail.com: + +Stefan Tomanek +2014-01-30:20140129234449.GY30808@zirkel.wertarbyte.de: + +Tobias Mueller +2016-11-23:1479937342.11180.3.camel@cryptobitch.de: + +Werner Koch +2013-03-29:87620ahchj.fsf@vigenere.g10code.de: + +Yann E. MORIN +2016-07-10:20160710093202.GA3688@free.fr: + +Arnaud Fontaine +2016-10-17:580484F4.8040806@ssi.gouv.fr: + + +Other authors +============= + +The need for copyright assignments to the FSF has been waived on +2013-03-29; the need for copyright disclaimers for translations +already in December 2012. + +The RPM specs file scripts/gnupg.spec has been contributed by +several people. + +The function build_argv in agent/w32main.c is based on code from +Alexandre Julliard. + +The gpg-zip documentation is based on the manpage for gpg-zip, written +by Colin Tuckley and Daniel Leidert for the GNU/Debian distribution. + +The DNS resolver code is libdns by William Ahern; see COPYING.other. + +The test driver is based on TinySCHEME by Dimitrios Souflis and +available under a permissive license; see COPYING.other. + + +Copyright +========= + +GnuPG is distributed under the GNU General Public License, version 3 +or later. + +Note that some files are under a combination of the GNU Lesser General +Public License, version 3 and the GNU General Public License, version +2. A few files carry an all permissive license note as found at the +bottom of this file. A few files are distributed under permissive +licenses as listed in the file COPYING.other. Some other small files +are distributed under the Creative Commons Zero license (see file +COPYING.CC0) which basically puts them into the public domain. + + +========= + + Copyright 1998-2016 Free Software Foundation, Inc. + Copyright 1997-2016 Werner Koch + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..ccbbaf6 --- /dev/null +++ b/COPYING @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/COPYING.CC0 b/COPYING.CC0 new file mode 100644 index 0000000..02ba366 --- /dev/null +++ b/COPYING.CC0 @@ -0,0 +1,123 @@ +[Note that only a few files are distributed under this license.] + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..4cd6920 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,167 @@ +[Note that only a few files are distributed under this license.] + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/COPYING.other b/COPYING.other new file mode 100644 index 0000000..797f78c --- /dev/null +++ b/COPYING.other @@ -0,0 +1,60 @@ +# COPYING.other -*- org -*- +#+TITLE: List of code with permissive licenses as used by GnuPG. +#+STARTUP: showall + +* DNS resolver (dirmngr/dns.c) + + dns.c - Recursive, Reentrant DNS Resolver. + -------------------------------------------------------------------------- + Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + +* TinySCHEME (tests/gpgscm/LICENSE.TinySCHEME) + + Copyright (c) 2000, Dimitrios Souflis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of Dimitrios Souflis nor the names of the + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..a6f0a79 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,16762 @@ +2016-12-20 Werner Koch + + Release 2.1.17. + + sm: Remove wrong example from gpgsm --help. + * sm/gpgsm.c (opts): Remove group 303. + + dirmngr: New option --resolver-timeout. + * dirmngr/dns-stuff.c (DEFAULT_TIMEOUT): New. + (opt_timeout): New var. + (set_dns_timeout): New. + (libdns_res_open): Set the default timeout. + (libdns_res_wait): Use configurable timeout. + (resolve_name_libdns): Ditto. + + * dirmngr/dirmngr.c (oResolverTimeout): New const. + (opts): New option --resolver-timeout. + (parse_rereadable_options): Set that option. + (main) : Add --nameserver and --resolver-timeout. + * tools/gpgconf-comp.c (gc_options_dirmngr): Add --resolver-timeout + and --nameserver. + + * dirmngr/http.c (connect_server): Fix yesterday introduced bug in + error diagnostic. + +2016-12-19 Werner Koch + + dirmngr: Fix problems with the getsrv function. + * dirmngr/dns-stuff.c (opt_debug, opt_verbose): New vars. + (set_dns_verbose): New func. + (libdns_switch_port_p): Add debug output. + (resolve_dns_name): Ditto. + (get_dns_cert): Ditto. + (get_dns_cname): Ditto. + (getsrv_libdns, getsrv_standard): Change SRVCOUNT to an unsigend int. + (getsrv): Rename to ... + ((get_dns_srv): this. Add arg R_COUNT and return an error. Add debug + output. + * dirmngr/http.c: Adjust for chnaged getsrv(). + * dirmngr/ks-engine-hkp.c (map_host): Ditto. + * dirmngr/t-dns-stuff.c (main): Ditto. Call set_dns_verbose. + * dirmngr/dirmngr.c (parse_rereadable_options): Call set_dns_verbose. + + build: Add target to sign the windows installer. + * build-aux/speedo.mk (w32-sign-installer): New. + (AUTHENTICODE_KEY): New. + (installer-from-source): Use cp instead of mv. Factor code out to ... + (MKSWDB_commands): new macro. + (sign-installer): New. + +2016-12-19 Justus Winter + + tests: Use the common test framework for the migration tests. + * tests/migrations/Makefile.am (reqired_pgms): Add 'gpgscm'. + (TESTS_ENVIRONMENT): Populate. + (TESTS): Rename to 'XTESTS'. + (xcheck): New target. + (EXTRA_DIST): Add new files. + (CLEANFILES): Remove log files. + * tests/migrations/common.scm: Honor 'verbose', fix paths. + * tests/migrations/run-tests.scm: New file. + * tests/migrations/setup.scm: Likewise. + + tests: Use sequential test runner if only one test is given. + * tests/openpgp/run-tests.scm: Use sequential test runner if only one + test is given. + +2016-12-19 Werner Koch + + dirmngr,w32: Hack around a select problem. + * dirmngr/dns.c (FD_SETSIZE): Bump up to 1024. + (dns_poll): Return an error instead of hitting an assertion failure. + +2016-12-19 Neal H. Walfield + + test: Extend TOFU tests to also check the days with signatures. + * tests/openpgp/tofu.scm (GPGTIME): Define the "standard" base time. + (faketime): New function. + (days->seconds): Likewise. + (GPG): Use faketime. + (check-counts): Also check the number of expected days with signatures + and encryptions. Update callers. Extend tests. + +2016-12-19 Justus Winter + + tests: New test for --delete-[secret-]keys. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/defs.scm (keys): New variable. + (have-public-key?): New function. + (have-secret-key?): Likewise. + (have-secret-key-file?): Likewise. + * tests/openpgp/delete-keys.scm: New file. + * tests/openpgp/quick-key-manipulation.scm: Move the accessors to + 'defs.scm'. + + gpgscm: Change associativity of ::. + * tests/gpgscm/scheme.c (mk_atom): Change associativity of the :: + infix-operator. This makes it possible to naturally express accessing + nested structures (e.g. a::b::c). + + gpgscm: Display location when assertions fail. + * tests/gpgscm/lib.scm (assert): Use location information if + available. + + gpgscm: Make exception handling more robust. + * tests/gpgscm/init.scm (throw'): Check that args is a list. + +2016-12-19 Andre Heinecke + + speedo,w32: Use nsExec::ExecToLog to avoid popups. + * build-aux/speedo/w32/inst.nsi: Use ExecToLog instead of + ExecWait. + +2016-12-19 Werner Koch + + Remove unused debug flags and add "dns" and "network". + * g10/options.h (DBG_CARD_IO_VALUE, DBG_CARD_IO): Remove. + * g10/gpg.c (debug_flags): Remove "cardio". + * agent/agent.h (DBG_COMMAND_VALUE, DBG_COMMAND): Remove. + * agent/gpg-agent.c (debug_flags): Remove "command". + * scd/scdaemon.h (DBG_COMMAND_VALUE, DBG_COMMAND): Remove. + * scd/scdaemon.c (debug_flags): Remove "command". + * dirmngr/dirmngr.h (DBG_DNS_VALUE, DBG_DNS): New. + (DBG_NETWORK_VALUE, DNG_NETWORK): New. + * dirmngr/dirmngr.c (debug_flags): Add "dns" and "network". + +2016-12-17 Werner Koch + + dirmngr: Fix setup of libdns for W32. + * configure.ac (DNSLIB) {W32]: Add -liphlpapi. + * dirmngr/dns-stuff.c [W32]: Include iphlpapi.h and define + WIN32_LEAN_AND_MEAN. + (libdns_init) [W32]: Use GetNetworkParams to get the nameserver. + * dirmngr/t-dns-stuff.c (init_sockets): New. + (main): Call it. + +2016-12-16 Werner Koch + + dirmngr: Auto-switch from Tor port to Torbrowser port. + * dirmngr/dns-stuff.c (libdns_tor_port): New var. + (set_dns_nameserver): Clear that var. + (libdns_init): Init var to the default port. + (libdns_switch_port_p): New func. + (resolve_dns_name): Use function to switch the port + (get_dns_cert): Ditto. + (getsrv): Ditto. + (get_dns_cname): Ditto. + + dirmngr: Use one context for all libdns queries. + * dirmngr/dns-stuff.c (libdns_reinit_pending): New var. + (enable_recursive_resolver): Set var. + (set_dns_nameserver): Ditto. + (libdns_init): Avoid double initialization. + (libdns_deinit): New. + (reload_dns_stuff): New. + (libdns_res_open): Act upon LIBDNS_REINIT_PENDING. + * dirmngr/t-dns-stuff.c (main): Call reload_dns_stuff to release + memory. + * dirmngr/dirmngr.c (cleanup): Ditto. + (dirmngr_sighup_action): Call reload_dns_stuff to set + LIBDNS_REINIT_PENDING. + + dirmngr: Pass Tor credentials to libdns. + * dirmngr/dns-stuff.c (tor_credentials): Replace by ... + (tor_socks_user, tor_socks_password): new vars. + (enable_dns_tormode): Set these new vars. + (libdns_res_open): Tell libdns the socks credentials. + + dirmngr: Factor common libdns code out. + * dirmngr/dns-stuff.c (libdns_res_open): New. Replace all libdns_init + and dns-res_open by a call to this func. + (libdns_res_submit): New wrapper. Replace all dns_res_sumbit calls. + (libdns_res_wait): New function. + (resolve_name_libdns): Replace loop by libdns_res_wait. + (get_dns_cert_libdns): Ditto. + (getsrv_libdns): Ditto. + + gpg,sm: A few more option for --gpgconf-list. + * g10/gpg.c (gpgconf_list): Add --compliance and + --default-new-key-algo. + (parse_compliance_option): + * sm/gpgsm.c (main) : Add --enable-crl-checks. + + gpgconf: New command --apply-profile. + * tools/gpgconf.c (aApplyProfile): New. + (opts): New command --apply-profile. + (main): Implement that command. + * tools/gpgconf-comp.c (option_check_validity): Add arg VERBATIM. + (change_options_program): Ditto. + (change_one_value): Ditto. + (gc_component_change_options): Ditto. + (gc_apply_profile): New. + + gpgconf: Fix --apply-defaults. + * tools/gpgconf-comp.c: Skip pinentry also in process_all mode. + +2016-12-16 Justus Winter + + doc: Mention extra information in pinentry status lines. + * doc/DETAILS: Mention that 'PINENTRY_LAUNCHED may carry extra + information. + + sm: Fix agent communication. + * sm/call-agent.c (gpgsm_agent_pksign): Fix passing the control handle + to the callback. + (gpgsm_scd_pksign): Likewise. + (gpgsm_agent_reaedkey): Likewise. + +2016-12-16 Neal H. Walfield + + doc: Fix manual. + * doc/gpg.texi: Remove comment about options being parsed in-order. + They aren't. + + g10: Use total days, not total messages to compute TOFU validity. + * g10/tofu.c (write_stats_status): Use the number of days with + signatures / encryptions to compute the validity, not the total number + of signatures / encryptions. + (BASIC_TRUST_THRESHOLD): Adjust given the new semantics. + (FULL_TRUST_THRESHOLD): Likewise. + + g10: Extend TOFU_STATS to emit and + * doc/DETAILS: Add SIGN-DAYS and ENCRYPT-DAYS to the TOFU_STATS status + line. + * g10/tofu.c (write_stats_status): Take additional parameters + signature_days and encryption_days. Update callers. Include them in + the tfs record and TOFU status lines. + (show_statistics): Compute the number of days on which we saw a + message signed by FINGERPRINT, and the number of days on which we + encrypted a message to it. + +2016-12-16 Justus Winter + + doc: Improve section on unattended key generation. + * doc/gpg.texi: Improve the subsection on unattended key generation by + suggesting the quick key manipulation interface as an alternative, and + by suggesting alternatives to '%pubring' and '%secring'. Simplify + examples accordingly. + + doc: Add documentation for programmatic use of GnuPG. + * doc/gpg.texi: New subsections on programmatic use of GnuPG, + ephemeral home directories, and the quick key manipulation interface. + +2016-12-16 Neal H. Walfield + + g10: On a TOFU conflict, write the conflicting keys to the status fd. + * g10/tofu.c (ask_about_binding): Emit all of the conflicting keys and + their statistics on the status fd. + (get_trust): Likewise, if we don't call ask_about_binding. + (show_statistics): Have the caller pass the policy as returned by + get_policy. Add argument only_status_fd and don't emit any output on + stdout if it is set. Update callers. + + g10: Add missing space. + * g10/tofu.c (tofu_register_encryption): Add missing space. + +2016-12-15 Justus Winter + + g10: Avoid translating simple error messages. + * g10/gpg.c (main): Avoid translating arguments to 'wrong_args'. + + g10: Rework the --quick-* interface. + * g10/gpg.c (opts): Rename options. + (main): Update errors. + * doc/gpg.texi: Update accordingly. + + g10: Rename 'card-edit' to 'edit-card'. + * g10/gpg.c (opts): Rename option. + * g10/call-agent.c (agent_scd_learn): Update comment. + * doc/gpg.texi: Update accordingly. + + g10: Spell out --desig-revoke. + * g10/gpg.c (opts): Rename option. + * doc/gpg.texi: Update accordingly. + + g10: Shorten unreasonably long option. + * g10/gpg.c (opts): Rename 'generate-revocation-certificate' to + 'generate-revocation'. + * doc/gpg.texi: Update accordingly. + * po: Update translations. + + doc: Add aliases of all changed options. + * doc/gpg.texi: Add the old version of every option that was updated + with the last change set. + * doc/gpgsm.texi: Likewise. + +2016-12-15 Werner Koch + + dirmngr: First patch to re-enable Tor support. + * dirmngr/dns-stuff.c (SOCKS_PORT, TOR_PORT, TOR_PORT2): New + constants. + (libdns_init): Start adding tor support. + (resolve_name_libdns): Pass socks hosts to dns_res_open. + (get_dns_cert_libdns): Ditto. + (getsrv_libdns): Ditto. + (get_dns_cname_libdns): Ditto. + +2016-12-15 Justus Winter + + build: Fix distcheck. + * tests/gpgme/Makefile.am (CLEANFILES): New variable, clean test logs. + +2016-12-14 Justus Winter + + tests: Reuse GPGME's tests. + * configure.ac (AC_CONFIG_FILES): Add new Makefile. + * tests/Makefile.am (SUBDIRS): Add new directory. + * tests/gpgme/Makefile.am: New file. + * tests/gpgme/gpgme-defs.scm: Likewise. + * tests/gpgme/run-tests.scm: Likewise. + * tests/gpgme/setup.scm: Likewise. + * tests/gpgme/wrap.scm: Likewise. + + common: Support locating components in the build tree. + * common/homedir.c (gnupg_build_directory): New variable. + (gnupg_module_name_called): Likewise. + (gnupg_set_builddir): New function. + (gnupg_set_builddir_from_env): Likewise. + (gnupg_module_name): Support locating components in the build tree. + * common/util.h (gnupg_set_builddir): New prototype. + * tests/openpgp/defs.scm (tools): Drop 'gpg and 'gpg-agent. + (tool): Rename to 'tool-hardcoded. + (gpg-conf): New function, with accessors for the results. + (gpg-components): New variable. + (tool): New function. + * tools/gpgconf.c (enum cmd_and_opt_values): New key. + (opts): New option '--build-prefix'. + (main): Handle new option. + + tests: Rework check for trust models. + * tests/openpgp/defs.scm (gpg-has-option?): New function. + (have-opt-always-trust): Use a simpler test for that option. This way + that is less distracting when we run the tests with verbose=3. + +2016-12-14 Werner Koch + + dirmngr: New configure option --disable-libdns. + * configure.ac: Add option --disable-libdns + (USE_LIBDNS): New ac_subst and am_conditional. + (USE_C99_CFLAGS): Set only if libdns is used. + * dirmngr/Makefile.am (dirmngr_SOURCES): Move dns.c and dns.h to ... + (dirmngr_SOURCES) [USE_LIBDNS0: here. + (t_common_src): Ditto. + * dirmngr/dirmngr.c (oRecursiveResolver): New constant. + (opts): New option "--recursive-resolver". + (parse_rereadable_options): Set option. + * dirmngr/t-dns-stuff.c (main): Add option --recursive-resolver. + * dirmngr/server.c (cmd_getinfo): Depend output of "dnsinfo" on the + new variables. + * dirmngr/dns-stuff.c: Include dns.h only if USE_DNSLIB is defined. + Also build and call dnslib functions only if USE_DNSLIB is defined. + (recursive_resolver): New var. + (enable_recursive_resolver): New func. + (recursive_resolver_p): New func. + + dirmngr: Implement CERT record lookup via libdns. + * dirmngr/dns-stuff.c (get_dns_cert_libdns): New. + (get_dns_cert_standard): Fix URL malloc checking. + + dirmngr: Implement CNAME and SRV record lookup via libdns. + * dirmngr/dns-stuff.c (dns_free): New macro. + (libdns): Move var to the top. + (libdns_error_to_gpg_error): Map error codes to the new gpg-error + codes. + (resolve_name_libdns): Restructure code. + (getsrv_libdns): New. + (get_dns_cname_libdns): New. + + dirmngr: Fix bugs in the standard resolver code. + * dirmngr/dns-stuff.c: Include dirmngr-err.h to set the correct error + source. + (get_h_errno_as_gpg_error): New. + (get_dns_cert_libdns): Fix error code. + (getsrv_libdns): Add arg R_COUNT and return an error code. + (getsrv_standard): Ditto. Fix handling of res_query errors and + provide the correct size for the return buffer. + (getsrv): Adjust for changed worker functions. + (get_dns_cname_standard): Fix handling of res_query errors and provide + the correct size for the return buffer. + + dirmngr: Require a c99 compiler. + * configure.ac (USE_C99_CFLAGS): New ac_subst. Set to -std=gnu99 for + gcc. + * dirmngr/Makefile.am (AM_CFLAGS): Add USE_C99_CFLAGS. + (t_http_CFLAGS): Ditto. + (t_ldap_parse_uri_CFLAGS): Ditto. + (t_dns_stuff_CFLAGS): Ditto. + + doc: Add license notes for libdns. + * COPYING.other: New. + * Makefile.am (EXTRA_DIST): Add it. + * AUTHORS: Add info on libdns. + * build-aux/speedo/w32/pkg-copyright.txt: Add license terms. + + common: Add replacements for error codes from gpg-error 1.26. + +2016-12-14 Justus Winter + + dirmngr: New libdns snapshot. + + dirmngr: Add basic libdns support. + * dirmngr/dns.c: New file. + * dirmngr/dns.h: New file. + * dirmngr/Makefile.am (dirmngr_SOURCES): Add new files. + * dirmngr/dns-stuff.c: Include dns.h.xxx use libdns + (libdns): New global var for the libdns state. + (libdns_error_to_gpg_error): New. + (libdns_init): New. + (resolve_name_libdns): New. + (get_dns_cert_libdns): New stub. + (getsrv_libdns): New stub. + (get_dns_cname_libdns): New stub. + + dirmngr,build: Remove support for ADNS. + * autogen.rc: Remove '--with-adns' argument. + * configure.ac: Remove check for ADNS. + * dirmngr/dns-stuff.c: Remove all code that uses ADNS. + * dirmngr/server.c (cmd_getinfo): Update status line. + * doc/dirmngr.texi: Do not mention ADNS. + +2016-12-14 NIIBE Yutaka + + dirmngr: Improve ntbtls support. + * dirmngr/http.c [HTTP_USE_NTBTLS] (close_tls_session): Release. + (send_request): Call ntbtls_set_transport. + (cookie_read, cookie_write): Implement. + (cookie_close): Add initial implementation for ntbtls. + +2016-12-13 Justus Winter + + g10,sm: Spell out --passwd. + * g10/gpg.c (opts): Spell out option. + * sm/gpgsm.c (opts): Likewise. + * doc/gpg.texi: Update accordingly. + * doc/gpgsm.texi: Likewise. + + g10: Spell out --gen-revoke. + * g10/gpg.c (opts): Spell out option. + * doc/gpg.texi: Update accordingly. + * po: Update translations. + + g10: Spell out --full-gen-key. + * g10/gpg.c (opts): Spell out option. + (main): Likewise. + * g10/keygen.c (generate_keypair): Likewise. + * doc/gpg.texi: Update accordingly. + + g10,sm: Spell out --gen-key. + * g10/gpg.c (opts): Spell out option. + * sm/gpgsm.c (opts): Likewise. + * doc/gpg.texi: Update accordingly. + + g10,sm: Spell out --check-sigs. + * g10/gpg.c (opts): Spell out option. + * sm/gpgsm.c (opts): Likewise. + * doc/gpg.texi: Update accordingly. + + g10,sm: Spell out --list-sigs. + * g10/gpg.c (opts): Spell out option. + * sm/gpgsm.c (opts): Likewise. + * doc/gpg.texi: Update accordingly. + + g10: Hyphenate --clearsign. + * g10/gpg.c (opts): Hyphenate option. + * doc/gpg.texi: Update accordingly. + * po: Update translations. + * tests/openpgp: Update tests. + + g10: Spell out --recv-keys. + * g10/gpg.c (opts): Spell out option. + * doc/gpg.texi: Update accordingly. + + g10: Create expiring keys in quick key generation mode. + * doc/gpg.texi: Document that fact. + * g10/keygen.c (quick_generate_keypair): Use a default value. + * tests/openpgp/quick-key-manipulation.scm: Test that fact. + + gpgscm: Print failed and skipped tests. + * tests/gpgscm/tests.scm (test-pool::report): Print failed and skipped + tests at the end. + + gpgscm: Generalize the test runner. + * tests/gpgscm/tests.scm (test::scm) Add explicit name argument. + (test::binary): Likewise. Also, add missing unquote. + * tests/openpgp/run-tests.scm: Adapt accordingly. + + gpgscm: Move the test runner to the Scheme library. + * tests/openpgp/run-tests.scm: Move most of the code... + * tests/gpgscm/tests.scm: ... here. + + tests: Refactor test runner. + * tests/openpgp/run-tests.scm (locate-test): New function. + (test): Factor-out the code starting the child process. + (test::binary): New function. + + gpgscm: Improve library functions. + * tests/gpgscm/tests.scm (absolute-path?): New function. + (canonical-path): Use the new function. + * tests/gpgscm/lib.scm (string-split-pln): New function. + (string-indexp, string-splitp): Likewise. + (string-splitn): Express using the above function. + (string-ltrim, string-rtrim): Fix corner case. + (list->string-reversed): New function. + (read-line): Fix performance. + +2016-12-12 Werner Koch + + gpg: Fix memory leak in ecc key generation. + * g10/keygen.c (ecckey_from_sexp): Release curve. + + gpg: Do not use a fixed string for --gpgconf-list:default_pubkey_algo. + * g10/keygen.c (get_default_pubkey_algo): New. + (parse_key_parameter_string): Use it. + * g10/gpg.c (gpgconf_list): Take value from new function. + + gpg: Fix algo string parsing of --quick-addkey. + * g10/keygen.c (parse_key_parameter_string): Fix handling of PART==1. + (parse_key_parameter_part): Use default key size if only "rsa", "dsa", + or "elg" is given. + +2016-12-09 Justus Winter + + g10: Create keys that expire in simple key generation mode. + * g10/keygen.c (default_expiration_interval): New variable. + (generate_keypair): Use the new default. + + tests: Add a test for '--quick-addkey'. + * tests/openpgp/quick-key-manipulation.scm: Test '--quick-addkey'. + + tests: New test using all available compression algorithms. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/compression.scm: New file. + * tests/openpgp/defs.scm (all-compression-algos): New variable. + + g10: List compression algorithms using human-readable names. + * g10/gpg.c (list_config): List all enabled compression algorithms + under the key 'compressname'. + + g10: Fix memory leak. + * g10/sign.c (do_sign): Release old signature data. + +2016-12-08 Werner Koch + + common: Skip the Byte Order Mark in conf files. + * common/argparse.c (optfile_parse): Detect and skip the UTF-8 BOM. + + Fix 2 compiler warnings. + * dirmngr/loadswdb.c: Set ERR on malloc failure. + * g10/passphrase.c (passphrase_to_dek): Initialize all fields of + HELP_S2K. + + wks: New option --status-fd for gpg-wks-client. + * tools/wks-util.c: Include status.h. + (statusfp): New global var. + (wks_set_status_fd): New func. + (wks_write_status): New func. + * tools/gpg-wks-client.c: Include status.h. + (oStatusFD): New constant. + (opts): New option --status-fd. + (parse_arguments): Handle that option. + (main): Return STATUS_SUCCESS or STATUS_FAILURE. + +2016-12-08 Justus Winter + + gpgscm: Better error reporting. + * tests/gpgscm/ffi.scm: Move the customized exception handling and + atexit logic... + * tests/gpgscm/init.scm: ... here. + (throw): Record the current history. + (throw'): New function that is history-aware. + (rethrow): New function. + (*error-hook*): Use the new throw'. + * tests/gpgscm/main.c (load): Fix error handling. + (main): Save and use the 'sc->retcode' as exit code. + * tests/gpgscm/repl.scm (repl): Print call history. + * tests/gpgscm/scheme.c (_Error_1): Make a snapshot of the history, + use it to provide a accurate location of the expression causing the + error at runtime, and hand the history trace to the '*error-hook*'. + (opexe_5): Tag all lists at parse time with the current location. + * tests/gpgscm/tests.scm: Update calls to 'throw', use 'rethrow'. + + gpgscm: Keep a history of calls for error messages. + * tests/gpgscm/init.scm (vm-history-print): New function. + * tests/gpgscm/opdefines.h: New opcodes 'CALLSTACK_POP', 'APPLY_CODE', + and 'VM_HISTORY'. + * tests/gpgscm/scheme-private.h (struct history): New definition. + (struct scheme): New field 'history'. + * tests/gpgscm/scheme.c (gc): Mark objects in the history. + (history_free): New function. + (history_init): Likewise. + (history_mark): Likewise. + (add_mod): New macro. + (sub_mod): Likewise. + (tailstack_clear): New function. + (callstack_pop): Likewise. + (callstack_push): Likewise. + (tailstack_push): Likewise. + (tailstack_flatten): Likewise. + (callstack_flatten): Likewise. + (history_flatten): Likewise. + (opexe_0): New variable 'callsite', keep track of the expression if it + is a call, implement the new opcodes, record function applications in + the history. + (opexe_6): Implement new opcode. + (scheme_init_custom_alloc): Initialize history. + (scheme_deinit): Free history. + * tests/gpgscm/scheme.h (USE_HISTORY): New macro. + + gpgscm: Add flag TAIL_CONTEXT. + * tests/gpgscm/scheme.c (S_FLAG_TAIL_CONTEXT): New macro. This flag + indicates that the interpreter is evaluating an expression in a tail + context (see R5RS, section 3.5). + (opexe_0): Clear and set the flag according to the rules layed out in + R5RS, section 3.5. + (opexe_1): Likewise. + + gpgscm: Add flags to the interpreter. + * tests/gpgscm/scheme-private.h (struct scheme): Add field 'flags'. + * tests/gpgscm/scheme.c (S_OP_MASK): New macro. + (S_FLAG_MASK, s_set_flag, s_clear_flag, s_get_flag): Likewise. + (_s_return): Unpack the encoded opcode and flags. + (s_save): Encode the flags along with the opcode. Use normal + integers to encode the result. + (scheme_init_custom_alloc): Initialize 'op' and 'flags'. + + gpgscm: Implement tags. + * tests/gpgscm/opdefines.h: Add opcodes to create and retrieve tags. + * tests/gpgscm/scheme.c (T_TAGGED): New macro. + (mk_tagged_value): New function. + (has_tag): Likewise. + (get_tag): Likewise. + (mark): Mark tag. + (opexe_4): Implement new opcodes. + * tests/gpgscm/scheme.h (USE_TAGS): New macro. + +2016-12-08 Werner Koch + + gpg: Fix the fix out-of-bounds access. + * g10/tofu.c (build_conflict_set): Revert to int* and fix calloc. + + wks: New option --check for gpg-wks-client. + * tools/call-dirmngr.c (wkd_get_key): New. + * tools/gpg-wks-client.c (aCheck): New constant. + (opts): New option "--check". + (main): Call command_check. + (command_check): New. + + tools: Move a function from gpg-wks-server to wks-util.c. + * tools/gpg-wks-server.c (list_key_status_cb): Remove. + (list_key): Move to ... + * tools/wks-util.c (wks_list_key): here and rename. Add new args + R_FPR and R_MBOXES and remove the CTX. + (list_key_status_cb): New. + * tools/wks-util.c: Include ccparray.h, exectool.h, and mbox-util.h. + * tools/gpg-wks-server.c (process_new_key): Replace list_key by + wks_list_key. + (check_and_publish): Ditto. + +2016-12-08 Justus Winter + + gpgscm: Generalize 'for-each-p'. + * tests/gpgscm/tests.scm (for-each-p): Generalize to N lists like + for-each. + (for-each-p'): Likewise. + + g10: Fix out-of-bounds access. + * g10/tofu.c (build_conflict_set): Use 'char'. + +2016-12-08 Werner Koch + + tools: Fix use of uninitialized var in mime-maker. + * tools/mime-maker.c (ensure_part): Make sure to set R_PARENT on + error. + (add_missing_headers): Ensure that ERR is set on success. + + * tools/wks-util.c (wks_parse_policy): Fix indentation. + + tools: Fix memleak in gpgconf. + * tools/gpgconf.c (main): Free SOCKETDIR. + + gpg: Fix portability problem. + * g10/tofu.c (build_conflict_set): Replace variable dynamic array. + +2016-12-07 Justus Winter + + tests: Add test for '--quick-set-expire'. + * tests/openpgp/quick-key-manipulation.scm: Test '--quick-set-expire'. + + tests: Improve quick key manipulation test. + * tests/openpgp/quick-key-manipulation.scm: Do not update the trust + database, rather be more specific when filtering the user ids. + +2016-12-06 Daniel Kahn Gillmor + + agent: Respect --enable-large-secmem. + * agent/gpg-agent.c (main): Initialize secmem to the configured buffer + size. + +2016-12-06 Justus Winter + + tests: Add test importing a revocation certificate. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/import-revocation-certificate.scm: New file. + * tests/openpgp/samplemsgs/revoke-2D727CC768697734.asc: Likewise. + + tests: Rename 'error' to 'fail'. + * tests/gpgscm/tests.scm (error): Rename to 'fail'. 'error' is a + primitive function (an opcode) of the TinySCHEME vm, and 'error' is + also defined by R6RS. Better avoid redefining that. Fix all call + sites. + * tests/openpgp/4gb-packet.scm: Adapt. + * tests/openpgp/decrypt-multifile.scm: Likewise. + * tests/openpgp/ecc.scm: Likewise. + * tests/openpgp/export.scm: Likewise. + * tests/openpgp/gpgtar.scm: Likewise. + * tests/openpgp/gpgv-forged-keyring.scm: Likewise. + * tests/openpgp/import.scm: Likewise. + * tests/openpgp/issue2015.scm: Likewise. + * tests/openpgp/issue2346.scm: Likewise. + * tests/openpgp/issue2419.scm: Likewise. + * tests/openpgp/key-selection.scm: Likewise. + * tests/openpgp/mds.scm: Likewise. + * tests/openpgp/multisig.scm: Likewise. + * tests/openpgp/setup.scm: Likewise. + * tests/openpgp/signencrypt.scm: Likewise. + * tests/openpgp/ssh-import.scm: Likewise. + * tests/openpgp/tofu.scm: Likewise. + * tests/openpgp/verify.scm: Likewise. + + tests: Remove debugging display. + * tests/openpgp/tofu.scm: Remove debugging display. + +2016-12-06 Neal H. Walfield + + tests: Update distributed files. + * tests/openpgp/Makefile.am (TEST_FILES): Remove tofu-keys.asc, + tofu-keys-secret.asc, tofu-2183839A-1.txt, tofu-BC15C85A-1.txt and + tofu-EE37CF96-1.txt. Add tofu/conflicting/1C005AF3.gpg, + tofu/conflicting/1C005AF3-secret.gpg, tofu/conflicting/1C005AF3-1.txt, + tofu/conflicting/1C005AF3-2.txt, tofu/conflicting/1C005AF3-3.txt, + tofu/conflicting/1C005AF3-4.txt, tofu/conflicting/1C005AF3-5.txt, + tofu/conflicting/B662E42F.gpg, tofu/conflicting/B662E42F-secret.gpg, + tofu/conflicting/B662E42F-1.txt, tofu/conflicting/B662E42F-2.txt, + tofu/conflicting/B662E42F-3.txt, tofu/conflicting/B662E42F-4.txt, + tofu/conflicting/B662E42F-5.txt, tofu/conflicting/BE04EB2B.gpg, + tofu/conflicting/BE04EB2B-secret.gpg, tofu/conflicting/BE04EB2B-1.txt, + tofu/conflicting/BE04EB2B-2.txt, tofu/conflicting/BE04EB2B-3.txt, + tofu/conflicting/BE04EB2B-4.txt, tofu/conflicting/BE04EB2B-5.txt and + tofu/conflicting/README. + + doc: Improve the text in the gpg manual. + * doc/gpg.texi: Improve the text. + + g10: Avoid a memory leak. + * g10/gpg.c (main): Free KB when we're done with it. + + tests: Change (interactive-shell) to start an interactive shell. + * tests/gpgscm/tests.scm (interactive-shell): Start an interactive + shell. + + tests: Check the signature count in the TOFU TFS record. + * tests/openpgp/tofu.scm: Check the signature count in the TOFU TFS + record. + + tests: Replace data used by the TOFU conflict test. + * tests/openpgp/tofu-2183839A-1.txt: Remove file. + * tests/openpgp/tofu-BC15C85A-1.txt: Remove file. + * tests/openpgp/tofu-EE37CF96-1.txt: Remove file. + * tests/openpgp/tofu-keys-secret.asc: Remove file. + * tests/openpgp/tofu-keys.asc: Remove file. + * tests/openpgp/tofu/conflicting/1C005AF3.gpg: New file. + * tests/openpgp/tofu/conflicting/1C005AF3-secret.gpg: New file. + * tests/openpgp/tofu/conflicting/1C005AF3-1.txt: New file. + * tests/openpgp/tofu/conflicting/1C005AF3-2.txt: New file. + * tests/openpgp/tofu/conflicting/1C005AF3-3.txt: New file. + * tests/openpgp/tofu/conflicting/1C005AF3-4.txt: New file. + * tests/openpgp/tofu/conflicting/1C005AF3-5.txt: New file. + * tests/openpgp/tofu/conflicting/B662E42F.gpg: New file. + * tests/openpgp/tofu/conflicting/B662E42F-secret.gpg: New file. + * tests/openpgp/tofu/conflicting/B662E42F-1.txt: New file. + * tests/openpgp/tofu/conflicting/B662E42F-2.txt: New file. + * tests/openpgp/tofu/conflicting/B662E42F-3.txt: New file. + * tests/openpgp/tofu/conflicting/B662E42F-4.txt: New file. + * tests/openpgp/tofu/conflicting/B662E42F-5.txt: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B.gpg: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B-secret.gpg: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B-1.txt: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B-2.txt: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B-3.txt: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B-4.txt: New file. + * tests/openpgp/tofu/conflicting/BE04EB2B-5.txt: New file. + * tests/openpgp/tofu/conflicting/README: New file. + * tests/openpgp/tofu.scm: Update accordingly. + + g10: Remove dead code. + * g10/tofu.c (tofu_set_policy_by_keyid): Remove function. + +2016-12-05 Werner Koch + + gpg: New option --quick-set-expire. + * g10/gpg.c (aQuickSetExpire): New. + (opts): New option --quick-set-expire. + (main): Implement option. + * g10/keyedit.c (menu_expire): Add args FORCE_MAINKEY and + NEWEXPIRATION. Change semantics of the return value. Change caller. + (keyedit_quick_set_expire): New. + +2016-12-05 Justus Winter + + tests: New test for '--enarmor' and '--dearmor'. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/enarmor.scm: New file. + +2016-12-03 Werner Koch + + gpg: Fix error code arg in ERRSIG status line. + * g10/mainproc.c (check_sig_and_print): Use gpg_err_code to return an + error code in ERRSIG. + +2016-12-02 Werner Koch + + gpg: New option --default-new-key-algo. + * common/openpgp-oid.c (openpgp_is_curve_supported): Add optional arg + R_ALGO and change all callers. + * common/util.h (GPG_ERR_UNKNOWN_FLAG): New error code. + * g10/options.h (struct opt): Add field DEF_NEW_KEY_ALGO. + * g10/gpg.c (oDefaultNewKeyAlgo): New enum. + (opts): New option "--default-new-key-algo". + (main): Set the option. + * g10/keygen.c: Remove DEFAULT_STD_ FUTURE_STD_ constants and replace + them by ... + (DEFAULT_STD_KEY_PARAM, FUTURE_STD_KEY_PARAM): new string constants. + (get_keysize_range): Remove arg R_DEF and return that value instead. + Change all callers. + (gen_rsa): Use get_keysize_range instead of the removed + DEFAULT_STD_KEYSIZE. + (parse_key_parameter_part): New function. + (parse_key_parameter_string): New function. + (quick_generate_keypair): Refactor using parse_key_parameter_string. + (generate_keypair): Ditto. + (parse_algo_usage_expire): Ditto. + +2016-12-02 Neal H. Walfield + + g10: Improve debugging output. + * g10/tofu.c (string_to_long): Improve debugging output. + (string_to_ulong): Likewise. + +2016-12-01 Neal H. Walfield + + g10: In the TOFU module, make strings easier to translate. + * g10/tofu.c: Remove dead code. + (time_ago_str): Simplify implementation since we only want the most + significant unit. + (format_conflict_msg_part1): Use ngettext. + (ask_about_binding): Likewise and only emit full sentences. + (show_statistics): Likewise. + +2016-12-01 Werner Koch + + dirmngr: Add option --standard-resolver. + * dirmngr/dirmngr.c (oStandardResolver): New constant. + (opts): New option --standard-resolver. + (parse_rereadable_options): Set option. + * dirmngr/dns-stuff.c: Refactor all code to support the new option. + (standard_resolver): New var. + (enable_standard_resolver, standard_resolver_p): New func. + * dirmngr/http.c (connect_server): Remove USE_DNS_SRV build + conditional. + * dirmngr/ks-engine-hkp.c (map_host): Ditto. + * dirmngr/server.c (cmd_getinfo) : Take care of new option + * configure.ac (HAVE_ADNS_IF_TORMODE): Remove var ADNSLIB. ac_define + USE_ADNS in the adns checking code. Remove options --disable-dns-srv + and --disable-dns-cert. Always look for the system resolver. Print + warning if no system resolver was found. + (USE_DNS_CERT, USE_DNS_SRV): Remove ac_defines. + (HAVE_SYSTEM_RESOLVER): New ac_define. + (USE_DNS_SRV): Remove am_conditional; not used anyway. + + gpg: Let only Dirmngr decide whether CERT is supported. + * g10/getkey.c (parse_auto_key_locate): Do not build parts depending + on USE_DNS_CERT. + +2016-12-01 Justus Winter + + tests,build: Fix distcheck. + * tests/openpgp/Makefile.am (sample_msgs): Add messages required for + the new test 'verify-multifile.scm'. + + tests: Add test for '--verify --multifile'. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/verify-multifile.scm: New file. + +2016-11-30 Justus Winter + + tests: Add test for '--encrypt --multifile'. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/encrypt-multifile.scm: New file. + +2016-11-29 Werner Koch + + agent,dirmngr: Tiny restructuring. + * agent/gpg-agent.c (handle_connections): Add a comment. + * dirmngr/dirmngr.c (main): Move assuan_sock_close of the listening + socket to ... + (handle_connections): here. Add a comment why we keep the + listening socket open during a shutdown. + + agent,dirmngr: Handle corner case in shutdown mode. + * agent/gpg-agent.c (handle_connections): Keep on selecting on the + inotify fd even when a shutdown is pending. + * dirmngr/dirmngr.c (handle_connections): Ditto. Also simplifyy the + use of the HAVE_INOTIFY_INIT cpp conditional. + + gpgsm: Allow decryption with a card returning a PKCS#1 stripped key. + * sm/decrypt.c (prepare_decryption): Handle a 16 byte session key. + + agent,w32: Initialize nPth in server mode. + * agent/gpg-agent.c (main) [W32]: Call initialize_modules in server + mode. + + gpg: Make --decrypt with output '-&nnnn' work. + * g10/plaintext.c (get_output_file): Check and open special filename + before falling back to stdout. + + gpg,sm: Merge the two versions of check_special_filename. + * sm/gpgsm.c (check_special_filename): Move to .. + * common/sysutils.c (check_special_filename): here. Add arg + NOTRANSLATE. + (allow_special_filenames): New local var. + (enable_special_filenames): New public functions. + * sm/gpgsm.c (allow_special_filenames): Remove var. + (main): Call enable_special_filenames instead of setting the var. + (open_read, open_es_fread, open_es_fwrite): Call + check_special_filename with 0 for NOTRANSLATE. + * common/iobuf.c (special_names_enabled): Remove var. + (iobuf_enable_special_filenames): Remove func. + (check_special_filename): Remove func. + (iobuf_is_pipe_filename): Call new version of the function with + NOTRANSLATE set. + (do_open): Ditto. + * g10/gpg.c (main): Call enable_special_filenames instead of + iobuf_enable_special_filenames. + * g10/gpgv.c (main): Ditto. + +2016-11-29 Justus Winter + + g10: Fix memory leak. + * g10/decrypt.c (decrypt_messages): Properly decrease the reference + count of the armor filters after pushing them. + + tools,build: Build WKS tools against libintl. + * tools/Makefile.am (gpg_wks_server_LDADD): Link against libintl. + (gpg_wks_client_LDADD): Likewise. + +2016-11-29 Neal H. Walfield + + Improve some comments. + + g10: Extend TOFU_STATS to always show the validity. + * doc/DETAILS (TOFU_STATS): Rename the VALIDITY field to SUMMARY. Add + a new field called VALIDITY. + * g10/tofu.c (write_stats_status): Update output accordingly. + +2016-11-29 Justus Winter + + tests: Add test for '--decrypt --multifile'. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/decrypt-multifile.scm: New file. + + gpgscm: Avoid truncating pointers. + * tests/gpgscm/scheme.c (_alloc_cellseg): Avoid truncating pointers on + systems where sizeof(unsigned long) < sizeof(void *). + +2016-11-29 Daniel Kahn Gillmor + + dirmngr: Lazily launch ldap reaper thread. + * dirmngr/dirmngr.c (main): Avoid calling ldap_wrapper_launch_thread() + Before we need it. + * dirmngr/ldap-wrapper.c (ldap_wrapper): Call + ldap_wrapper_launch_thread() just in time (before any attempt to use + an ldap subprocess). + +2016-11-29 Werner Koch + + build: Remove more keywords from the generated ChangeLog. + * build-aux/gitlog-to-changelog (parse_amend_file): Generalize keyword + removal. + +2016-11-28 Justus Winter + + tests: Add test for the ssh key export. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + (sample_keys): Add new files. + * tests/openpgp/samplekeys/authenticate-only.pub.asc: New file. + * tests/openpgp/samplekeys/authenticate-only.sec.asc: Likewise. + * tests/openpgp/ssh-export.scm: Likewise. + + g10: Fix iteration over getkey results. + * g10/getkey.c (getkey_next): Only ask 'lookup' for the exact match if + our caller requested the key. Fixes a crash in 'lookup'. + + tests: Rename ssh test. + * tests/openpgp/ssh.scm: Rename to 'ssh-import.scm'. + * tests/openpgp/Makefile (XTESTS): Likewise. + +2016-11-25 NIIBE Yutaka + + scd: Support OpenPGP card V3 for RSA. + * scd/app-openpgp.c (struct app_local_s): Remove max_cmd_data and + max_rsp_data fields as Extended Capabilities bits are different. + (get_cached_data) Use extcap.max_certlen_3. + (get_one_do): Don't use exmode=1. + (determine_rsa_response): New. + (get_public_key, do_genkey): Call determine_rsa_response. + (do_sign): Use keyattr[0].rsa.n_bits / 8, instead of max_rsp_data. + (do_auth): Use keyattr[2].rsa.n_bits / 8, instead of max_rsp_data. + (do_decipher): Likewise with Use keyattr[1].rsa.n_bits / 8. + (show_caps): Remove max_cmd_data and max_rsp_data. + (app_select_openpgp): Likewise. + +2016-11-23 Justus Winter + + gpgscm: Make 'reverse' compatible with 'reverse_in_place'. + * tests/gpgscm/scheme.c (reverse): Update prototype, add terminator + argument. + (opexe_4): Update callsite. + + gpgscm: Clean sweeped cells. + * tests/gpgscm/scheme.c (gc): Zero typeflag and car of free cells. + + gpgscm: Fix initialization of 'sink'. + * tests/gpgscm/scheme.c (scheme_init_custom_alloc): Also initialize + cdr. + +2016-11-23 Neal H. Walfield + + g10: Avoid gratuitously loading a keyblock when it is already available. + * g10/trust.c (get_validity): Add new, optional parameter KB. Only + load the keyblock if KB is NULL. Update callers. + (get_validity): Likewise. + * g10/trustdb.c (tdb_get_validity_core): Likewise. + +2016-11-22 Neal H. Walfield + + g10: Use es_fopen instead of open. + * g10/tofu.c: Don't include , or . + (busy_handler): Replace use of open with es_fopen. + + g10: If the set of UTKs changes, invalidate any changed policies. + * g10/trustdb.c (tdb_utks): New function. + * g10/tofu.c (check_utks): New function. + (initdb): Call it. + * tests/openpgp/tofu.scm: Modify test to check the effective policy of + keys whose effective policy changes when we change the set of UTKs. + +2016-11-22 NIIBE Yutaka + + scd: Fix receive buffer size. + * scd/apdu.c (send_le): Fix the size, adding two for status + bytes to Le. + +2016-11-22 Justus Winter + + gpgscm: Refactor. + * tests/gpgscm/scheme.c (opexe_0): Reduce code duplication. + + gpgscm: Fix property lists. + * tests/gpgscm/opdefines.h (put, get): Check arguments. Also rename + to 'set-symbol-property' and 'symbol-property', the names used by + Guile, because put and get are too unspecific. + * tests/gpgscm/scheme.c (hasprop): Only symbols have property lists. + (get_property): New function. + (set_property): Likewise. + (opexe_4): Use the new functions. + + gpgscm: Fix installation of error handler. + * tests/gpgscm/ffi.scm: Set '*error-hook*' again so that the + interpreter will use our function. + + gpgscm: Use a static pool of cells for small integers. + * tests/gpgscm/scheme-private.h (struct scheme): New fields for the + static integer cells. + * tests/gpgscm/scheme.c (_alloc_cellseg): New function. + (alloc_cellseg): Use the new function. + (MAX_SMALL_INTEGER): New macro. + (initialize_small_integers): New function. + (mk_small_integer): Likewise. + (mk_integer): Return a small integer if possible. + (_s_return): Do not free 'op' if it is a small integer. + (s_save): Use a small integer to box the opcode. + (scheme_init_custom_alloc): Initialize small integers. + (scheme_deinit): Free chunk of small integers. + * tests/gpgscm/scheme.h (USE_SMALL_INTEGERS): New macro. + + tests: Delay querying the avaliable algorithms. + * tests/openpgp/defs.scm: Set verbosity earlier, turn 'all-*-algos' + into promises. + * tests/openpgp/conventional-mdc.scm: Force the promises. + * tests/openpgp/conventional.scm: Likewise. + * tests/openpgp/encrypt-dsa.scm: Likewise. + * tests/openpgp/encrypt.scm: Likewise. + * tests/openpgp/gpgtar.scm: Likewise. + * tests/openpgp/sigs.scm: Likewise. + + g10: Fix memory leak. + * g10/tofu.c (tofu_notice_key_changed): Remove spurious duplicate call + to 'hexfingerprint'. + +2016-11-21 Neal H. Walfield + + g10: Cache the effective policy. Recompute it when required. + * g10/tofu.c (initdb): Add column effective_policy to the bindings + table. + (record_binding): New parameters effective_policy and set_conflict. + Save the effective policy. If SET_CONFLICT is set, then set conflict + according to CONFLICT. Otherwise, preserve the current value of + conflict. Update callers. + (get_trust): Don't compute the effective policy here... + (get_policy): ... do it here, if it was not cached. Take new + parameters, PK, the public key, and NOW, the time that the operation + started. Update callers. + (show_statistics): New parameter PK. Pass it to get_policy. Update + callers. + (tofu_notice_key_changed): New function. + * g10/gpgv.c (tofu_notice_key_changed): New stub. + * g10/import.c (import_revoke_cert): Take additional argument CTRL. + Pass it to keydb_update_keyblock. + * g10/keydb.c (keydb_update_keyblock): Take additional argument CTRL. + Update callers. + [USE_TOFU]: Call tofu_notice_key_changed. + * g10/test-stubs.c (tofu_notice_key_changed): New stub. + * tests/openpgp/tofu.scm: Assume that manually setting a binding's + policy to auto does not cause the tofu engine to forget about any + conflict. + + g10: Correctly parameterize ngettext. + * g10/tofu.c (ask_about_binding): Correctly parameterize ngettext. + + g10: Don't use the same variable for multiple SQL compiled statements. + * g10/tofu.c (struct tofu_dbs_s): Remove unused field + record_binding_update2. Replace register_insert with + register_signature and register_encryption. + (tofu_register_signature): Don't use dbs->s.register_insert, but + dbs->s.register_signature. + (tofu_register_encryption): Don't use dbs->s.register_insert, but + dbs->s.register_encryption. + + g10: Add a convenience function for checking if a key is a primary key. + * g10/keydb.h (pk_is_primary): New function. + * g10/tofu.c (get_trust): Use it. + (tofu_register_signature): Likewise. + (tofu_register_encryption): Likewise. + (tofu_set_policy): Likewise. + (tofu_get_policy): Likewise. + +2016-11-21 Daniel Kahn Gillmor + + doc: Ship example gpg-agent-browser.socket in examples/systemd-user/. + * doc/Makefile.am: Ship gpg-agent-browser.socket alongside the other + systemd user service example files. + +2016-11-21 NIIBE Yutaka + + agent: Fix npth + daemon mode problem. + * agent/gpg-agent.c (main): Remove duplicated initialization in daemon + mode. + +2016-11-18 Werner Koch + + Release 2.1.16. + +2016-11-18 Ineiev + + po: Update Russian translation. + +2016-11-18 NIIBE Yutaka + + g10: Fix flags to open for lock of ToFU. + * g10/tofu.c (busy_handler): Fix the flags and utime is not needed. + +2016-11-18 Werner Koch + + dirmngr: Use a longer timer tick interval. + * dirmngr/dirmngr.c (TIMERTICK_INTERVAL): Always use 60 seconds like + we did for WindowsCE. + +2016-11-18 Daniel Kahn Gillmor + + dirmngr: More w32 system daemon cleanup. + * dirmngr/dirmngr.c (handle_tick): Remove w32 tests for + shutdown_pending; no longer needed. + +2016-11-18 NIIBE Yutaka + + g10: Fix creating a lock for ToFU. + * g10/tofu.c (busy_handler): Add third argument which is mandatory for + O_CREATE flag. + + scd: Don't limit to ST-2xxx for PC/SC. + * scd/apdu.c (pcsc_vendor_specific_init): Only check vender ID. + +2016-11-17 Daniel Kahn Gillmor + + dirmngr: Use a default keyserver if none is explicitly set. + * configure.ac: Define DIRMNGR_DEFAULT_KEYSERVER. + * dirmngr/server.c (ensure_keyserver): Use it if no keyservers are set. + * doc/dirmngr.texi: Document this behavior. + + dirmngr: Add system CAs if no hkp-cacert is given. + * dirmngr/dirmngr.c (http_session_new): If the user isn't talking to + the HKPS pool, and they have not specified any hkp-cacert, then we + should default to the system CAs, rather than nothing. + * doc/dirmngr.texi: Document choice of CAs. + + dirmngr: Register hkp-cacert even if the file doesn't exist yet. + * dirmngr/dirmngr.c (parse_readable_options): If we're unable to turn + an argument for hkp-cacert into an absolute filename, terminate + completely. + * dirmngr/http.c (http_register_tls_ca): Show a warning if file is not + immediately accessible, but register it anyway. + +2016-11-17 Justus Winter + + gpgscm: Re-enable the garbage collector in case of errors. + * tests/gpgscm/scheme.c (opexe_0): Enable gc before calling 'Error_1'. + + gpgscm: Fix string. + * tests/gpgscm/scheme.c (type_to_string): Fix string. + +2016-11-17 Werner Koch + + dirmngr: Auto-sownload the swdb.lst. + * dirmngr/dirmngr.h (struct opt): Add field allow_version_check. + * dirmngr/dirmngr.c (oAllowVersionCheck): New. + (opts): Add --allow-version-check. + (network_activity_seen): New variable. + (parse_rereadable_options): Set opt.allow_version_check. + (main) : Do not anymore set the no change flag for + Windows. Add allow-version-check. + (netactivity_action): Set network_activity_seen. + (housekeeping_thread): Call dirmngr_load_swdb. + * tools/gpgconf-comp.c (gc_options_dirmngr): Add allow-version-check. + Make "use-tor" available at Basic level. + + dirmngr: Improve downloading of swdb.lst. + * dirmngr/loadswdb.c (time_of_saved_swdb): Aslo return the "verified" + timestamp. + (dirmngr_load_swdb): Avoid unnecessary disk or network access witout + FORCE. Do not update swdb.lst if it did not change. + + gpgconf: Change the displayed names of the components. + +2016-11-16 Werner Koch + + dirmngr: Add command to only load the swdb. + * dirmngr/loadswdb.c: New. + * dirmngr/Makefile.am (dirmngr_SOURCES): Add that file. + * dirmngr/server.c: Remove includes cpparray.h and exectool.h. + (cmd_loadswdb): New. + (parse_version_number,parse_version_string): Remove. + (my_mktmpdir, cmp_version): Remove. + (fetch_into_tmpdir): Remove. + (struct verify_swdb_parm_s): Remove. + (verify_swdb_status_cb): Remove. + (cmd_versioncheck): Remove. + (register_commands): Register LOADSWDB. Remove VERSIONCHECK. + + scd,dirmngr: Keep the standard fds when daemonizing. + * dirmngr/dirmngr.c (main): Before calling setsid do not close the + standard fds but connect them to /dev/null. + * scd/scdaemon.c (main): Ditto. Noet that the old test for a log + stream was even reverted. + + common: Rename keybox_file_rename to gnupg_rename_file. + * kbx/keybox-util.c (keybox_file_rename): Rename to ... + * common/sysutils.c (gnupg_rename_file): this. Change all callers. + + wks: Always build gpg-wks-client. + * tools/Makefile.am (gpg_wks_client): Remove macro. + (libexec_PROGRAMS): Add gpg-wks-client. + + gpg: New option --override-session-key-fd. + * g10/gpg.c (oOverrideSessionKeyFD): New. + (opts): Add option --override-session-key-fd. + (main): Handle that option. + (read_sessionkey_from_fd): New. + +2016-11-15 Werner Koch + + gpgv: New option --enable-special-filenames. + * g10/gpgv.c (oEnableSpecialFilenames): New. + (opts): Add option --enable-special-filenames. + (main): Implement that option. + + gpg: Add new compliance mode "de-vs". + * g10/options.h (CO_DE_VS): New. + (GNUPG): Also allow CO_DE_VS. + * g10/gpg.c (oDE_VS): New. + (parse_compliance_option): Add "de-vs". + (set_compliance_option): Set "de-vs". + * g10/misc.c (compliance_option_string): Return a description string. + (compliance_failure): Ditto. + * g10/keygen.c (ask_algo): Take care of CO_DE_VS. + (get_keysize_range): Ditto. + (ask_curve): Add new field to CURVES and trun flags into bit flags. + Allow only Brainpool curves in CO_DE_VS mode. + + gpg: Use usual free semantics for packet structure free functions. + * g10/free-packet.c (free_attributes): Turn function into a nop for a + NULL arg. + (free_user_id): Ditto. + (free_compressed): Ditto. + (free_encrypted): Ditto. + (free_plaintext): Ditto. + (release_public_key_parts): Avoid extra check for NULL. + * g10/getkey.c (get_best_pubkey_byname): Ditto. + +2016-11-15 Justus Winter + + g10: Optimize key iteration. + * g10/getkey.c (get_best_pubkey_byname): Use the node returned by + 'getkey_next' instead of doing another lookup. + + g10: Fix memory leak. + * g10/getkey.c (finish_lookup): Clarify that we do not return a + reference. + (lookup): Clarify the relation between RET_KEYBLOCK and RET_FOUND_KEY. + Check arguments. Actually release the node if it is not returned. + + g10: Fix iteration over getkey results. + * g10/getkey.c (getkey_next): Fix invocation of 'lookup'. If we want + to use RET_FOUND_KEY, RET_KEYBLOCK must be valid. + + g10: Fix use-after-free. + * g10/getkey.c (pubkey_cmp): Make a copy of the user id. + (get_best_pubkey_byname): Free the user ids. + +2016-11-15 Werner Koch + + sm: New stub option --compliance. + * sm/gpgsm.c (oCompliance): New. + (opts): Add "--compliance". + (main): Implement as stub. + +2016-11-15 NIIBE Yutaka + + g10: Fix memory leak. + * g10/keyedit.c (menu_adduid): Don't copy 'sig'. + +2016-11-15 Werner Koch + + gpg: New option --compliance. + * g10/gpg.c (oCompliance): New. + (opts): Add "--compliance". + (parse_tofu_policy): Use a generic description string for "help". + (parse_compliance_option): New. + (main): Add option oCompliance. Factor out code for compliance + setting to ... + (set_compliance_option): new. + +2016-11-15 Justus Winter + + g10: Fix memory leak. + * g10/keyedit.c (menu_adduid): Deallocate 'sig'. + + gpgscm: Mark cells requiring finalization. + * tests/gpgscm/scheme.c (T_FINALIZE): New macro. + (mk_port): Use the new macro. + (mk_foreign_object): Likewise. + (mk_counted_string): Likewise. + (mk_empty_string): Likewise. + (gc): Only call 'finalize_cell' for cells with the new flag. + + gpgscm: Recover more cells. + * tests/gpgscm/scheme.c (_s_return): Recover the cell holding the + opcode. + +2016-11-14 Justus Winter + + g10: Fix memory leak. + * g10/mainproc.c (check_sig_and_print): Free 'pk'. + + gpgscm: Avoid cell allocation overhead. + * tests/gpgscm/scheme-private.h (struct scheme): New fields + 'inhibit_gc', 'reserved_cells', and 'reserved_lineno'. + * tests/gpgscm/scheme.c (GC_ENABLED): New macro. + (USE_GC_LOCKING): Likewise. + (gc_reservations): Likewise. + (gc_reservation_failure): New function. + (_gc_disable): Likewise. + (gc_disable): New macro. + (gc_enable): Likewise. + (gc_enabled): Likewise. + (gc_consume): Likewise. + (get_cell_x): Consume reserved cell if garbage collection is disabled. + (_get_cell): Assert that gc is enabled. + (get_cell): Only record cell in the list of recently allocated cells + if gc is enabled. + (get_vector_object): Likewise. + (gc): Assert that gc is enabled. + (s_return): Add comment, adjust call to '_s_return'. + (s_return_enable_gc): New macro. + (_s_return): Add flag 'enable_gc' and re-enable gc if set. + (oblist_add_by_name): Use the new facilities to protect the + allocations. + (new_frame_in_env): Likewise. + (new_slot_spec_in_env): Likewise. + (s_save): Likewise. + (opexe_0): Likewise. + (opexe_1): Likewise. + (opexe_2): Likewise. + (opexe_5): Likewise. + (opexe_6): Likewise. + (scheme_init_custom_alloc): Initialize the new fields. + +2016-11-14 NIIBE Yutaka + + scd: Fix status info encoding. + * scd/command.c (send_status_info): Do percent plus encoding correctly. + +2016-11-12 Werner Koch + + agent: Improve concurrency when Libgcrypt 1.8 is used. + * agent/gpg-agent.c (thread_init_once): Tell Libgcrypt to reinit the + system call clamp. + (agent_libgcrypt_progress_cb): Do not sleep if Libgcrypt is recent + enough. + +2016-11-11 Werner Koch + + agent: Kludge to mitigate blocking calls in Libgcrypt. + * agent/gpg-agent.c (agent_libgcrypt_progress_cb): Sleep for 100ms on + "need_entropy". + + dirmngr: Prepare to trigger jobs by network activity. + * dirmngr/http.c (netactivity_cb): New. + (http_register_netactivity_cb): New. + (notify_netactivity): New. + (connect_server): Call that function. + * dirmngr/dirmngr.c (main): Call http_register_netactivity_cb. + (netactivity_action): New stub handler. + +2016-11-11 Daniel Kahn Gillmor + + agent: Clean up comments. + * agent/agent.h: Clean up comments. + +2016-11-10 Werner Koch + + gpg,sm: Add STATUS_ERROR keydb_search and keydb_add-resource. + * g10/keydb.c (keydb_add_resource): Make ANY_REGISTERED + file-global. Write a STATUS_ERROR. + (maybe_create_keyring_or_box): Check for non-accessible but existant + file. + (keydb_search): Write a STATUS_ERROR if no keyring has been registered + but continue to return NOT_FOUND. + * sm/keydb.c (keydb_add_resource): Rename ANY_PUBLIC to ANY_REGISTERED + and make file-global. Write a STATUS_ERROR. + (keydb_search): Write a STATUS_ERROR if no keyring has been registered + but continue to return NOT_FOUND. Also add new arg CTRL and change + all callers to pass it down. + + sm: Remove unused arg SECRET from keydb functions. + * sm/keydb.c (struct resource_item): Remove field 'secret'. + (keydb_add_resource): Remove arg 'secret' and change all callers. + (keydb_new): Ditto. + +2016-11-10 Justus Winter + + gpgscm: Recover cells from the list of recently allocated cells. + * tests/gpgscm/scheme.c (ok_to_freely_gc): Recover cells. + + gpgscm: Recover cells used to maintain interpreter state. + * tests/gpgscm/scheme.c (free_cell): New function. + (free_cons): Likewise. + (_s_return): Use the new function to recover cells used to save the + state of the interpreter in 's_save'. This reduces the need to do a + garbage collection considerably. + + gpgscm: Reduce opcode dispatch overhead. + * tests/gpgscm/scheme.c (s_thread_to): New macro. + (CASE): Likewise. + (opexe_[0-6]): Use 'CASE' instead of 'case' statements, replace + 's_goto' with 's_thread_to' where applicable. + + gpgscm: Make the compile-hook configurable. + * tests/gpgscm/scheme-private.h (struct scheme): Make field + 'COMPILE_HOOK' optional. + * tests/gpgscm/scheme.c (opexe_0): Fix guard. + (scheme_init_custom_alloc): Conditionally initialize 'COMPILE_HOOK'. + * tests/gpgscm/scheme.h (USE_COMPILE_HOOK): Define to 1 by default. + + gpgscm: Drop obsolete commented-out code. + * tests/gpgscm/scheme.c (opexe_5): Drop obsolete code. + + gpgscm: Remove dubious stack implementation. + * tests/gpgscm/scheme-private.h (struct scheme): Remove related fields. + * tests/gpgscm/scheme.c: Drop all !USE_SCHEME_STACK code. + * tests/gpgscm/scheme.h (USE_SCHEME_STACK): Remove macro. + +2016-11-10 Werner Koch + + gpg: Improve error message for --quick-gen-key. + * g10/keygen.c (parse_algo_usage_expire): Use a different error + message for an unknown algorithm name. + + dirmngr: Improve concurrency in the non-adns case. + * dirmngr/dns-stuff.c (map_adns_status_to_gpg_error): New. + (resolve_name_adns, get_dns_cert, get_dns_cname): Use that function. + (getsrv) [!USE_ADNS]: Call res_query outside of nPth. + +2016-11-08 Justus Winter + + tests: Fix environment setup. + * tests/openpgp/defs.scm (setup-legacy-environment): Do not call + 'setup-environment' because that will start the agent, and hence + register the atexit function twice. + + Fixes: a55393cb5f4b331cb3a715c7d9a8b91f7606f337 + + tests: Log and display output from tests when run in parallel. + * tests/openpgp/run-tests.scm (test): Add field 'logfd'. + (test::new, test::set-*): Adapt accordingly. + (test::set-logfd): New function. + (test::open-log-file): Likewise. + (test::run-sync): Use the new function. + (test::run-async): Likewise. + (test::report): Replay the log. + (run-tests-parallel): Reverse the results to restore the original + order. + + tests: Simplify test. + * tests/openpgp/issue2417.scm: Simplify. + + gpgscm: Expose seek and associated constants. + * tests/gpgscm/ffi.c (do_seek): New function. + (ffi_init): Expose 'seek' and 'SEEK_{SET,CUR,END}'. + * tests/gpgscm/lib.scm: Document the new function. + + gpgscm: Fix error message. + * tests/gpgscm/ffi.c (do_wait_processes): Fix and improve error + messages. + + tests,w32: Make cleanup more robust. + * tests/openpgp/run-tests.scm (run-tests-parallel): Catch errors when + removing the working directory. On Windows this can fail if there is + still a process using one of the files there. + (run-tests-sequential): Likewise. + + common,w32: Simplify locking. + * common/asshelp.c (lock_spawning): Use the same code on Windows that + we use on all other platforms. + (unlock_spawning): Likewise. + +2016-11-07 Justus Winter + + tests: Write a log file for each test. + * tests/openpgp/Makefile.am (CLEANFILES): Delete logs. + * tests/openpgp/run-tests.scm (test::run-sync): Write logs. + + gpgscm: Generalize splice to write to multiple sinks. + * tests/gpgscm/ffi.c (ordinal_suffix): New function. + (do_splice): Generalize splice to write to multiple sinks. + * tests/gpgscm/lib.scm (splice): Document this fact. + + gpgscm: Drop 'len' argument from splice. + * tests/gpgscm/ffi.c (do_splice): Drop 'len' argument, no-one uses it. + * tests/gpgscm/lib.scm (splice): Document foreign function. + + tests: Move environment creation and teardown into each test. + * tests/gpgscm/tests.scm (log): New function. + * tests/openpgp/run-tests.scm (run-tests-parallel): Do not run the + startup and teardown scripts. + (run-tests-sequential): Likewise. + * tests/openpgp/setup.scm: Move all functions... + * tests/openpgp/defs.scm: ... here and make them less verbose. + (setup-environment): New function. + (setup-legacy-environment): Likewise. + (start-agent): Make less verbose, run 'stop-agent' at interpreter + exit. + (stop-agent): Make less verbose. + * tests/openpgp/finish.scm: Drop file. + * tests/openpgp/Makefile.am (EXTRA_DIST): Drop removed file. + * tests/openpgp/4gb-packet.scm: Use 'setup-environment' or + 'setup-legacy-environment' as appropriate. + * tests/openpgp/armdetach.scm: Likewise. + * tests/openpgp/armdetachm.scm: Likewise. + * tests/openpgp/armencrypt.scm: Likewise. + * tests/openpgp/armencryptp.scm: Likewise. + * tests/openpgp/armor.scm: Likewise. + * tests/openpgp/armsignencrypt.scm: Likewise. + * tests/openpgp/armsigs.scm: Likewise. + * tests/openpgp/clearsig.scm: Likewise. + * tests/openpgp/conventional-mdc.scm: Likewise. + * tests/openpgp/conventional.scm: Likewise. + * tests/openpgp/decrypt-dsa.scm: Likewise. + * tests/openpgp/decrypt.scm: Likewise. + * tests/openpgp/default-key.scm: Likewise. + * tests/openpgp/detach.scm: Likewise. + * tests/openpgp/detachm.scm: Likewise. + * tests/openpgp/ecc.scm: Likewise. + * tests/openpgp/encrypt-dsa.scm: Likewise. + * tests/openpgp/encrypt.scm: Likewise. + * tests/openpgp/encryptp.scm: Likewise. + * tests/openpgp/export.scm: Likewise. + * tests/openpgp/finish.scm: Likewise. + * tests/openpgp/genkey1024.scm: Likewise. + * tests/openpgp/gpgtar.scm: Likewise. + * tests/openpgp/gpgv-forged-keyring.scm: Likewise. + * tests/openpgp/import.scm: Likewise. + * tests/openpgp/issue2015.scm: Likewise. + * tests/openpgp/issue2417.scm: Likewise. + * tests/openpgp/issue2419.scm: Likewise. + * tests/openpgp/key-selection.scm: Likewise. + * tests/openpgp/mds.scm: Likewise. + * tests/openpgp/multisig.scm: Likewise. + * tests/openpgp/quick-key-manipulation.scm: Likewise. + * tests/openpgp/seat.scm: Likewise. + * tests/openpgp/shell.scm: Likewise. + * tests/openpgp/signencrypt-dsa.scm: Likewise. + * tests/openpgp/signencrypt.scm: Likewise. + * tests/openpgp/sigs-dsa.scm: Likewise. + * tests/openpgp/sigs.scm: Likewise. + * tests/openpgp/ssh.scm: Likewise. + * tests/openpgp/tofu.scm: Likewise. + * tests/openpgp/use-exact-key.scm: Likewise. + * tests/openpgp/verify.scm: Likewise. + * tests/openpgp/version.scm: Likewise. + * tests/openpgp/issue2346.scm: Likewise and simplify. + + tests: Do not allow tests to be run in a shared environment. + * tests/openpgp/README: Update. + * tests/openpgp/run-tests.scm (run-tests-parallel-shared): Drop + function. + (run-tests-parallel-isolated): Rename to 'run-tests-parallel'. + (run-tests-sequential-shared): Drop function. + (run-tests-sequential-isolated): Rename to 'run-tests-sequential'. + + tests: Fix build. + * tests/openpgp/Makefile.am: Drop dependency on 'mk-tdata'. + + Fixes: 70215ff470c82d144e872057dfa5a478cc9195f2 + +2016-11-07 Werner Koch + + wks: Encrypt all client mails also the target key, + * tools/gpg-wks-client.c (encrypt_response): Add arg FINGERPRINT. + (send_confirmation_response): Ditto. + (process_confirmation_request): Parse out fingerprint and pass + send_confirmation_response. + +2016-11-07 Justus Winter + + tests,tools: Reimplement 'mk-tdata' in Scheme. + * tests/openpgp/defs.scm (tools): Drop 'mk-tdata'. + * tests/openpgp/setup.scm (make-test-data): New function. + * tests/openpgp/verify.scm: Avoid 'mk-tdata'. + * tools/Makefile.am (noinst_PROGRAMS): Drop 'mk-tdata'. + * tools/mk-tdata.c: Drop file. + + gpgscm,w32: Provide schemish file handling for binary files. + * tests/gpgscm/lib.scm (call-with-binary-input-file): New function. + (call-with-binary-output-file): Likewise. + + gpgscm: Add support for pseudo-random numbers. + * tests/gpgscm/ffi.c (do_getpid): New function. + (do_srandom): Likewise. + (random_scaled): Likewise. + (do_random): Likewise. + (do_make_random_string): Likewise. + (ffi_init): Expose the new functions. + * tests/gpgscm/lib.scm: Document the new functions. + + g10: Fix crash. + * g10/getkey.c (get_best_pubkey_byname): If 'get_pubkey_byname' does + not return a getkey context, then it can return at most one key, + therefore there is nothing to rank. Also, always initialize '*retctx' + to be on the safe side. + + Fixes: ab89164be02012f1bf159c971853b8610e966301 + +2016-11-04 Justus Winter + + gpgscm: Fix printing strings containing zero bytes. + * tests/gpgscm/scheme.c (atom2str): Fix computing the length of Scheme + strings. Scheme strings can contain zero bytes. + + gpgscm: Implement 'atexit'. + * tests/gpgscm/ffi.scm (throw): Run *run-atexit-handlers* when + terminating the interpreter. + (*atexit-handlers*): New variable. + (*run-atexit-handlers*): New function. + (atexit): Likewise. + * tests/gpgscm/main.c (main): Run *run-atexit-handlers* at normal + interpreter shutdown. + +2016-11-04 NIIBE Yutaka + + scd: Fix length error for READKEY. + * scd/app-openpgp.c (do_readkey): Decrement the length. + + scd: Add --advanced option for READKEY. + * scd/command.c (cmd_readkey) : Support ADVANCED arg. + * scd/app.c (app_readcert): Add ADVANCED arg. + * scd/app-openpgp.c (do_readkey): Implement ADVANCED arg. + * scd/app-nks.c (do_readkey): Error return with GPG_ERR_NOT_SUPPORTED. + +2016-11-03 Werner Koch + + agent: Extend the PINENTRY_LAUNCHED inquiry and status. + * agent/call-pinentry.c (start_pinentry): Get flavor and version and + pass it to agent_inq_pinentry_launched. + * agent/command.c (agent_inq_pinentry_launched): Add arg EXTRA. + * g10/server.c (gpg_proxy_pinentry_notify): Print a new diagnostic. + +2016-11-03 Justus Winter + + g10: Improve and unify key selection for -r and --locate-keys. + * g10/getkey.c (struct pubkey_cmp_cookie): New type. + (key_is_ok, uid_is_ok, subkey_is_ok): New functions. + (pubkey_cmp): Likewise. + (get_best_pubkey_byname): Likewise. + * g10/keydb.h (get_best_pubkey_byname): New prototype. + * g10/keylist.c (locate_one): Use the new function. + * g10/pkclist.c (find_and_check_key): Likewise. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + (TEST_FILES): Add new files. + * tests/openpgp/key-selection.scm: New file. + * tests/openpgp/key-selection/0.asc: Likewise. + * tests/openpgp/key-selection/1.asc: Likewise. + * tests/openpgp/key-selection/2.asc: Likewise. + * tests/openpgp/key-selection/3.asc: Likewise. + * tests/openpgp/key-selection/4.asc: Likewise. + + gpgscm,tests: Add new functions to the test environment. + * tests/gpgscm/lib.scm (first, last, powerset): New functions. + * tests/gpgscm/tests.scm (interactive-shell): New function. + * tests/openpgp/Makefile.am (EXTRA_DIST): Add new file. + * tests/openpgp/README: Document 'interactive-shell'. + * tests/openpgp/shell.scm: New file. + +2016-11-03 Werner Koch + + gpgconf: Add a new field to the --query-swdb output. + * tools/gpgconf.c (query_swdb): Insert new field with the installed + version. Check that the supplied version does not contain a colon. + +2016-11-02 Werner Koch + + gpgconf: Add command --query-swdb. + * tools/gpgconf.c (aQuerySWDB): New. + (opts): Add --query-swdb. + (valid_swdb_name_p): New. + (query_swdb): New. + (main): Implement command --query-swdb. + + common: Improve compare_string_versions. + * common/stringhelp.c: Include limits.h. + (compare_version_strings): Change semantics to behave like strcmp. + Include the patch lebel in the comparison. Allow checking a single + version string. + * common/t-stringhelp.c (test_compare_version_strings): Adjust test + vectors and a few new vectors. + * g10/call-agent.c (warn_version_mismatch): Adjust to new sematics. + * g10/call-dirmngr.c (warn_version_mismatch): Ditto. + * sm/call-agent.c (warn_version_mismatch): Ditto. + * sm/call-dirmngr.c (warn_version_mismatch): Ditto. + +2016-11-02 Justus Winter + + gpgscm: Fix inclusion of readline header. + * tests/gpgscm/ffi.c: Define magic macro to prevent the completion + function from redefined. + +2016-11-02 Daniel Kahn Gillmor + + build: Fix misspelled dirmngr. + + Spelling: correct spelling of "passphrase". + There were several different variant spellings of "passphrase". This + should fix them all for all English text. + + I did notice that po/it.po contains multiple instances of + "passhprase", which also looks suspect to me, but i do not know + Italian, so i did not try to correct it. + +2016-11-02 Justus Winter + + g10,w32: Fix build on Windows. + * g10/tofu.c (begin_transaction): Use the new 'gnupg_usleep'. + +2016-10-31 Werner Koch + + common: New function gnupg_usleep. + * configure.ac (HAVE_NANOSLEEP): Test for nanosleep. + * common/sysutils.c: Always include time.h. + (gnupg_usleep): New. + +2016-10-31 Andre Heinecke + + w32: Fix PKG_CONFIG_LIBDIR in --build-w32. + * autogen.sh: Point pkg-config to the right location. + +2016-10-31 Neal H. Walfield + + g10: Avoid gratuitious SQLite aborts and starving writers. + * g10/tofu.c: Include , , and . + (tofu_dbs_s): Add fields want_lock_file and want_lock_file_ctime. + (begin_transaction): Only yield if DBS->WANT_LOCK_FILE_CTIME has + changed since we took the lock. Don't use gpgrt_yield to yield, but + sleep for 100ms. After taking the batch lock, update + DBS->WANT_LOCK_FILE_CTIME. Also take the batch lock the first time we + take the real lock. When taking the real lock, use immediate not + deferred mode to avoid gratuitious aborts. + (end_transaction): When dropping the outermost real lock, drop the + batch lock. + (busy_handler): New function. + (opendbs): Set the busy handler to it when opening the DB. Initialize + CTRL->TOFU.DBS->WANT_LOCK_FILE. + (tofu_closedbs): Free DBS->WANT_LOCK_FILE. + +2016-10-30 Neal H. Walfield + + g10: Avoid reading in keys when possible. + * g10/tofu.c (build_conflict_set): If CONFLICT_SET contains a single + element, don't bother to check for cross sigs. Add parameter PK. + Update callers. + + g10: Fix bit setting. + * g10/tofu.c (build_conflict_set): Fix bit setting. + +2016-10-28 Werner Koch + + gpg: Enable the Issuer Fingerprint from rfc4880bis. + * g10/build-packet.c (build_sig_subpkt_from_sig): Always write the new + Issuer Fingerprint sub-packet. + * g10/mainproc.c (check_sig_and_print): Always consider that + sub-packet. + +2016-10-27 Werner Koch + + dirmngr: Fix signature checking. + * dirmngr/server.c: Include cpparray.h. + (verify_swdb_parm_s): New. + (verify_swdb_status_cb): New. + (cmd_versioncheck): Use gpgv to correclty verify the signature. + Rename some variable to comply with GNU standards. + + gpg: Verify multiple detached signatures with different hash algos. + * g10/mainproc.c (proc_tree): Loose check. Enable all algos. + + common: Add GNUPG_MODULE_NAME_GPGV. + * common/util.h (GNUPG_MODULE_NAME_GPGV): New. + * common/homedir.c (gnupg_module_name): Implement. + +2016-10-27 Justus Winter + + g10: Fix iteration over getkey results. + * g10/getkey.c (getkey_next): Return the public key in PK even if + RET_KEYBLOCK is NULL. + + g10: Assert preconditions. + * g10/getkey.c (get_pubkey_byname): Assert preconditions. + +2016-10-27 Werner Koch + + dirmngr: Do not implement --supervised in Windows. + * dirmngr/dirmngr.c (opts) [W32]: Remove --supervised. + (main) [W32]: Ditto. + + common: Remove debug output from gnupg_get_socket_name. + * common/sysutils.c (gnupg_get_socket_name): Remove debug message and + use my_error_from_syserror. + +2016-10-27 NIIBE Yutaka + + dirmngr: ADNS error handling fix. + * dirmngr/dns-stuff.c (resolve_name_adns, get_dns_cert, get_dns_cname): + Use gpg_error and gpg_err_code_from_errno to compose the error value. + +2016-10-27 Werner Koch + + gpg: Convey --quick option to dirmngr for --auto-key-retrieve. + * g10/call-dirmngr.c (gpg_dirmngr_ks_get): Add arg 'quick'. + (gpg_dirmngr_wkd_get): Ditto. + * g10/keyserver.c (keyserver_get): Add arg 'quick'. + (keyserver_get_chunk): Add arg 'quick'. + (keyserver_import_fprint): Ditto. Change callers to pass 0 for it. + (keyserver_import_keyid): Ditto. + (keyserver_import_wkd): Ditto. + * g10/mainproc.c (check_sig_and_print): Call the 3 fucntions with + QUICK set. + +2016-10-27 NIIBE Yutaka + + common: Fix gnupg_inotify_has_name, differently. + * common/sysutils.c (gnupg_inotify_has_name): Use void * to stop the + warning. + + dirmngr: More ADNS error fix. + * dirmngr/dns-stuff.c (get_dns_cert, getsrv, get_dns_cname): Fix return + value. + + dirmngr: Fix error return for ADNS. + * dirmngr/dns-stuff.c (resolve_name_adns): Use RET for return value. + + g10: Fix ECDH, clarifying the format. + * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Returns error when + it's short. Clarify the format. Handle other prefixes correctly. + + scd: Add 0x41 prefix for x-coordinate only result. + * scd/app-openpgp.c (do_decipher): When it's x-coordinate only, add the + prefix 0x41. + +2016-10-27 Arnaud Fontaine + + g10: ECDH shared point format. + * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Improve handling of + ECDH shared point format. + +2016-10-27 Daniel Kahn Gillmor + + dirmngr: Implement --supervised command (for systemd, etc). + * dirmngr/dirmngr.c (main): Add new --supervised command, which is a + mode designed for running under a process supervision system like + systemd or runit. + * doc/dirmngr.texi: document --supervised option. + + agent,common: move get_socket_name() into common. + * agent/gpg-agent.c (get_socket_name): move to ... + * common/sysutils.c (gnupg_get_socket_name): ... here. + + dirmngr: report actual socket name. + * dirmngr/dirmngr.[ch] (dirmngr_get_current_socket_name): new function + to report known socket name. + * dirmngr/server.c (cmd_getinfo): use dirmngr_get_current_socket_name + to report correct socket name. + +2016-10-27 NIIBE Yutaka + + common: Fix gnupg_inotify_has_name. + * common/sysutils.c (gnupg_inotify_has_name): Take care of the + alignment. + + dirmngr: Fix help string and argument. + * dirmngr/server.c (hlp_versioncheck): Add a newline. + (cmd_versioncheck): Fix argument. + +2016-10-26 Werner Koch + + dirmngr: Fix hang due to deferred thread initialization. + * dirmngr/dirmngr.c (main): Call ldap_wrapper_launch_thread after + thread_init. + + agent: Avoid double error message. + * agent/gpg-agent.c (map_supervised_sockets): Shorten error message. + Remove unneeded diagnostic. + + common: Use GPG_ERR_INV_VALUE instead of GPG_ERR_EINVAL. + * common/sysutils.c (gnupg_inotify_watch_socket): Return + GPG_ERR_INV_VALUE for a missing socket name and set proper error + source. + + tests: Improve portability of fake-pinentry. + * tests/openpgp/fake-pinentry.c: Make all functions static. + (get_passphrase): s/unlink/remove/ because that is standard C. + (spacep): Rename to whitespace and change all callers. + (main): Move macro out of if-then chain. + +2016-10-26 Daniel Kahn Gillmor + + agent: --supervised mode improvements. + * agent/gpg-agent.c (map_supervised_socket): if the agent is running + in --supervised mode and is not actually given LISTEN_FDNAMES + directives, require at least fd 3 to be open for listening. + + common: avoid segfault. + * common/sysutils.c (gnupg_inotify_watch_socket): return EINVAL if + socket_name is NULL, rather than segfaulting + +2016-10-25 Justus Winter + + agent,tests,w32: Fix relaying pinentry user data, fix fake-pinentry. + * agent/call-pinentry.c (start_pinentry): Also send the user data + using an Assuan 'OPTION' command. + * tests/openpgp/fake-pinentry.c (get_passphrase): Fix updating + passphrase file. + (spacep): Include newline characters. + (rstrip): New function. + (main): Handle Windows line endings. Handle the userdata option, and + restart with the new options. + + tests: Do not autostart gpg-agents on teardown. + * tests/openpgp/defs.c (stop-agent): Use '--no-autostart' when calling + gpg-connect-agent. + +2016-10-25 Werner Koch + + dirmngr: Allow command VERSIONCHECK to handle 3 part version numbers. + * dirmngr/server.c (parse_version_string): Add arg MICRO and set it. + (cmp_version): Extend to handle the MICRO part. + (confucius_mktmpdir): Rename to my_mktmpdir. + (my_mktmpdir): xstrconcat does not fail; use strconcat. + (fetch_into_tmpdir): Improve error checking. + + common: Use strconcat in gnupg_setenv. + * common/sysutils.c (gnupg_setenv): Replace malloc+stpcpy by + strconcat. Indent cpp conditionals. + (gnupg_unsetenv): Indent cpp conditionals. + +2016-10-24 Werner Koch + + gpg: Replace two sprintf calls. + * g10/keygen.c (print_status_key_created): Use snprintf for now. + (ask_expire_interval): Replace xmalloc and sprintf by xasprintf. + + agent: Minor cleanup for recent change in findkey.c. + * agent/findkey.c (agent_write_private_key): Avoid label name error. + + agent: Slightly change structure of cmd_readkey. + * agent/command.c (cmd_readkey): Avoid a leave label in the middle of + the code. Remove the special return. + +2016-10-24 Kai Michaelis + + dirmngr: Fix segfault in VERSIONCHECK. + * dirmngr/server.c (cmd_versioncheck): The VERSIONCHECK command crashes + when called without program version. + +2016-10-24 NIIBE Yutaka + + scd: Use canonical curve name of libgcrypt. + * scd/app-openpgp.c (send_key_attr): Use curve instead of OID. + (ecdh_params): New. + (ecc_read_pubkey): Use ecdh_params. Use curve name. + (ecc_writekey): Likewise. + (ecc_curve): Rename from ecc_oid. + (parse_algorithm_attribute): Use ecc_curve. + * g10/call-agent.c (learn_status_cb): Use openpgp_is_curve_supported to + intern the curve name string. + * g10/card-util.c (card_status): Conver curve name to alias for print. + + common: Fix openpgp_is_curve_supported. + * common/openpgp-oid.c (openpgp_is_curve_supported): Support both of + canonical name of the curve and alias. + + g10: Fix card keygen for decryption. + * g10/keygen.c (do_generate_keypair): Fix arguments. + +2016-10-22 NIIBE Yutaka + + g10: More card key generation change. + * g10/keygen.c (gen_card_key): Add back ALGO as the second argument. + Don't get ALGO by KEY-ATTR by this function. It's caller to provide + ALGO. Don't do that by both of caller and callee. + (generate_keypair): Only put paramerters needed. Use parameters + for ALGO to call gen_card_key. + (generate_card_subkeypair): Get ALGO and call gen_card_key with it. + +2016-10-21 Andre Heinecke + + g10: Write first keybox record in binary mode. + * g10/keydb.c (maybe_create_keyring_or_box): Open in binary mode. + +2016-10-21 NIIBE Yutaka + + g10,scd: Fix ECC keygen. + * g10/keygen.c (generate_keypair): For card key generation, fill + parameters by KEY-ATTR. + + * scd/app-openpgp.c (ecc_read_pubkey): OID should be freed at last, + after its reference by OIDBUF is finished. + (ecc_writekey): Likewise. + + scd: Fix segfault changing key attr. + * asc/app-openpgp.c (change_keyattr_from_string): Release after + allocated. + +2016-10-21 NIIBE Yutaka + Arnaud Fontaine + + g10: Don't ask keysize for for non-RSA card. + * g10/card-util.c (card_status): Bug fix for keyno. + (ask_card_rsa_keysize, do_change_rsa_keysize): Rename. + (generate_card_keys): Only ask keysize when RSA. + (card_generate_subkey): Likewise. + + g10: Support ECC for gen_card_key. + * g10/keygen.c (gen_card_key): Remove the first argument of ALGO. + (do_generate_keypair, generate_card_subkeypair): Follow the change. + +2016-10-21 NIIBE Yutaka + + Fix use cases of snprintf. + * agent/call-pinentry.c, agent/call-scd.c, agent/command.c, + build-aux/speedo/w32/g4wihelp.c, common/get-passphrase.c, + dirmngr/dirmngr.c, g10/call-agent.c, g10/cpr.c, g10/keygen.c, + g10/openfile.c, g10/passphrase.c, scd/app-openpgp.c, scd/scdaemon.c, + sm/call-agent.c, sm/call-dirmngr.c, sm/certreqgen.c: Fix assuming C99. + + agent: Fix saving with FORCE=1. + * agent/findkey.c (agent_write_private_key): Recover from an error of + GPG_ERR_ENOENT when FORCE=1 and it is opened with "rb+". + +2016-10-20 Justus Winter + + tests: Simplify test. + * tests/openpgp/quick-key-manipulation.scm: Avoid creating a temporary + home directory, just make the uids unique. + + tests: Flush stdout in the fake pinentry. + * tests/openpgp/fake-pinentry.c (reply): Flush stdout. + + common,w32: Fix setting environment variables on Windows. + * common/sysutils.c (gnupg_setenv): Also update the environment block + maintained by the C runtime. + (gnupg_unsetenv): Likewise. + * tests/gpgscm/ffi.c (do_setenv): Fix error handling. + + tests,w32: Cope with Windows line endings. + * tests/openpgp/issue2015.scm: Rstrip line before comparison. + + tests: Create and remove socket directories. + * tests/openpgp/defs.scm (start-agent): Move function here and create + the socket directory prior to starting the agent. + (stop-agent): Move function here and remove the socket directory. + * tests/openpgp/finish.scm: Adapt. + * tests/openpgp/setup.scm: Likewise. + +2016-10-20 NIIBE Yutaka + + agent, g10: Fix keygen. + * agent/command.c (cmd_readkey): Get length after card_readkey. + * g10/keygen.c (gen_card_key): Fix off-by-one error. + + scd: GENKEY updates the public key in APP. + * scd/app-openpgp.c (rsa_read_pubkey, ecc_read_pubkey): New. + (read_public_key): New. + (get_public_key, do_genkey): Use read_public_key. + + g10: smartcard keygen change. + * g10/call-agent.c (scd_genkey_cb_append_savedbytes): Remove. + (scd_genkey_cb): Only handle KEY-CREATED-AT and PROGRESS. + (agent_scd_genkey): Remove INFO argument. CREATETIME is now in/out + argument. + (agent_readkey): Use READKEY --card instead of SCD READKEY. + * g10/keygen.c (gen_card_key): Use READKEY --card command of the agent + to retrieve public key information from card and let the agent make + a file for private key with shadow info. + + agent: Add --card option for READKEY. + * agent/findkey.c (agent_write_shadow_key): New. + * agent/command-ssh.c (card_key_available): Use agent_write_shadow_key. + * agent/learncard.c (agent_handle_learn): Likewise. + * agent/command.c (cmd_readkey): Add --card option. + +2016-10-19 Kai Michaelis + + dirmngr: improve VERSIONCHECK. + Replace strtok_r() and code formatting. Use code from libgpg-error for + version comparison. + +2016-10-18 Justus Winter + + common: Fix copying data to estreams. + * common/exectool.c (copy_buffer_do_copy): Correctly account for + partially written data in the event of errors. + + common,w32: Communicate with child in non-blocking mode. + * common/exechelp-w32.c (gnupg_spawn_process): Open streams in + non-blocking mode if requested. + + common,w32: Extend gnupg_create_inbound_pipe et al. + * common/exechelp-w32.c (do_create_pipe): Rename, add arguments, and + create a stream if reqested. + (gnupg_create_inbound_pipe): Use the extended function to open the + stream if requested. + (gnupg_create_outbound_pipe): Likewise. + (gnupg_create_pipe): Update call site. + + common,w32: Make use of default_errsource in exechelp. + * common/exechelp-posix.c (my_error_from_syserror, my_error): New. + Use them instead of gpg_error and gpg_error_from_syserror. + +2016-10-18 NIIBE Yutaka + Arnaud Fontaine + + scd: Support ECC key generation. + * scd/app-openpgp.c (get_public_key): Fix a message. + (change_keyattr_from_string, ecc_writekey): Call mpi_release sooner. + (do_genkey): Add ECC support. + +2016-10-18 NIIBE Yutaka + + scd: minor cleanup to merge other works. + * scd/iso7816.c (do_generate_keypair): Use const char * for DATA. + (iso7816_generate_keypair, iso7816_read_public_key): Likewise. + * scd/app-openpgp.c (get_public_key): Follow the change. + (do_genkey): Ditto. Use ERR instead of RC. Use u32 for CREATED_AT. + +2016-10-17 Justus Winter + + gpgscm: Initialize nesting stack. + * tests/gpgscm/scheme.c (scheme_init_custom_alloc): Initialize nesting + stack. + +2016-10-17 Daniel Kahn Gillmor + + doc: Document how to manually shut down gpg-agent. + * doc/gpg-agent.texi: document "gpgconf --kill gpg-agent" for manual + agent termination. + + This was requested in a side-comment in https://bugs.debian.org/840669 + + doc: Point gpg-agent(1) at the right gpg manpage in SEE ALSO. + * doc/gpg-agent.texi (SEE ALSO): refer to @gpgname, instead of + hard-coding "gpg2". + +2016-10-17 NIIBE Yutaka + + scd: Fix keytocard for ECC. + * scd/app-openpgp.c (build_ecc_privkey_template): Size can be greater + than 128 when it comes with public key for curve of larger field. + + gpgconf: Fix for --homedir. + * tools/gpgconf-comp.c (gpg_agent_runtime_change, + scdaemon_runtime_change, dirmngr_runtime_change): Provide the homedir + arguments by --homedir when it's not default. + +2016-10-16 Werner Koch + + agent: Use straightforward names for the default socket names. + * configure.ac (GPG_AGENT_SOCK_NAME): Change name to *.extra. + (GPG_AGENT_EXTRA_SOCK_NAME): Change name to *browser. + +2016-10-15 Werner Koch + + agent: Move inotify code to common and improve it. + * common/sysutils.c: Include sys/inotify.h. + (my_error_from_syserror, my_error): New. + (gnupg_inotify_watch_socket): New. + (gnupg_inotify_has_name): New. + * agent/gpg-agent.c: Do not include sys/inotify.h. + (my_inotify_is_name): Remove. + (handle_connections): Remove HAVE_INOTIFY_INIT protected code and use + the new functions. + +2016-10-14 Kai Michaelis + + dirmngr: use gnupg_mkdtemp instead of mkstemp. + MinGW on debian does not support mkstemp. + + dirmngr: add VERSIONCHECK command. + Given an application name and version VERSIONCHECK fetches the software + version list from version.gnupg.org, verifies the signature and returns + whenever the given version is older (UPDATE), current (CURRENT) or newer + (ROLLBACK). + +2016-10-13 Neal H. Walfield + + tests: Use shorter filenames. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.gpg: Rename from this... + * tests/openpgp/tofu/cross-sigs/EC38277E-1.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.txt: Rename from this... + * tests/openpgp/tofu/cross-sigs/EC38277E-1.txt: .. to this. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.gpg: Rename from this... + * tests/openpgp/tofu/cross-sigs/EC38277E-2.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.txt: Rename from this... + * tests/openpgp/tofu/cross-sigs/EC38277E-2.txt: .. to this. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-3.txt: Rename from this... + * tests/openpgp/tofu/cross-sigs/EC38277E-3.txt: .. to this. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-secret.gpg: Rename from + this... + * tests/openpgp/tofu/cross-sigs/EC38277E-secret.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.gpg: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-1.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.txt: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-1.txt: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.gpg: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-2.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.txt: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-2.txt: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.gpg: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-3.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.txt: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-3.txt: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-4.gpg: Rename from this... + * tests/openpgp/tofu/cross-sigs/871C2247-4.gpg: .. to this. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-secret.gpg: Rename from + this... + * tests/openpgp/tofu/cross-sigs/871C2247-secret.gpg: .. to this. + * tests/openpgp/Makefile.am (TEST_FILES): Update accordingly. + + g10: Be more careful when checking if a binding is signed by a UTK. + * g10/tofu.c (signed_by_utk): When checking if a key is signed by an + ultimately trusted key, only consider the signatures on the specified + user id. + * tests/openpgp/tofu.scm: Add test for the above. + + tests: Add test data to TEST_FILES. + * tests/openpgp/Makefile.am (TEST_FILES): Add new test data. + + g10: Be more careful when checking cross signatures. + * g10/tofu.c (cross_sigs): When checking cross signatures, only + consider the signatures on the specified user id. + * tests/openpgp/tofu.scm: Add test for the above. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.gpg: + New file. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-1.txt: New file. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.gpg: New file. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-2.txt: New file. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-3.txt: New file. + * tests/openpgp/tofu/cross-sigs/ + 1938C3A0E4674B6C217AC0B987DB2814EC38277E-secret.gpg: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.gpg: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-1.txt: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.gpg: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-2.txt: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.gpg: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-3.txt: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-4.gpg: New file. + * tests/openpgp/tofu/cross-sigs/ + DC463A16E42F03240D76E8BA8B48C6BD871C2247-secret.gpg: New file. + * tests/openpgp/tofu/cross-sigs/README: New file. + + g10: Still check if the key is an UTK or cross signed in batch mode. + * g10/tofu.c (get_trust): If POLICY is ask, but we can't ask, don't + bail immediately. Instead, check if the key in question is an + ultimately trusted key or cross signed. + + g10: If an sqlite operation fails, map the error code to GPG_ERR_GENERAL + * g10/tofu.c (get_policy): If an sqlite operation fails, map the error + code to GPG_ERR_GENERAL. + (ask_about_binding): Likewise. + (build_conflict_set): Likewise. + (get_trust): Likewise. + (show_statistics): Likewise. + (tofu_register_signature): Likewise. + (tofu_register_encryption): Likewise. + + tests: Remove support for deprecated functionality. + * tests/openpgp/tofu.scm: Don't remove tofu.d. It's deprecated. + +2016-10-12 Neal H. Walfield + + g10: When changing a TOFU binding's policy, update the conflict info. + * g10/tofu.c (record_binding): Take an additional argument, CONFLICT. + Set the binding's conflict accordingly. Update callers. + + g10: Make a singular string singular. + * g10/tofu.c (ask_about_binding): Make the singular string singular. + + g10: Correctly determine whether a binding has a conflict. + * g10/tofu.c (build_conflict_set): A binding has a conflict is + conflict is *not* NULL, not if it is NULL. + + g10: Fix a column's type in TOFU DB. + * g10/tofu.c (initdb): Change policy from a boolean to an integer. + +2016-10-07 Justus Winter + + tests: Rework test environment setup. + * tests/openpgp/setup.scm: Import one keyring at a time. This works + around a yet to be investigated hang on Windows. It is also much + prettier. + + tests: Improve handling of Windows newlines. + * tests/gpgscm/lib.scm (string-split-newlines): New function. + * tests/openpgp/default-key.scm: Use new function. + * tests/openpgp/defs.scm: Likewise. + * tests/openpgp/export.scm: Likewise. + * tests/openpgp/import.scm: Likewise. + + gpgscm: Improve test of low-level functions. + * tests/gpgscm/t-child.c: Print large amounts of data. + * tests/gpgscm/t-child.scm: Test that this works. + + gpgscm: Improve path handling. + * tests/gpgscm/ffi.c (ffi_init): New Scheme variable '*win32*'. + * tests/gpgscm/tests.scm (canonical-path): Correctly handle paths with + drive letter on Windows. Use 'path-join'. + (path-expand): Use 'path-join'. + + tools: Fix error handling. + * tools/gpgtar-create.c (gpgtar_create): Do not crash if opening the + tarball failed. + +2016-10-07 NIIBE Yutaka + + agent: Fix get_socket_name. + * agent/gpg-agent.c (get_socket_name): Fix the size of copying. + +2016-10-07 Werner Koch + + gpg: Put extra parens around bit tests. + * g10/options.h (DBG_MPI): New. + * g10/gpg.c (set_debug): Use macro or extra parens for binary operator. + * g10/parse-packet.c (set_packet_list_mode): Use dbg macro. + +2016-10-07 NIIBE Yutaka + + agent, dirmngr, scd: Fix init_common_subsystems. + * common/init.c (_init_common_subsystems): Don't call + gpgrt_set_syscall_clamp in this function. + * agent/gpg-agent.c, dirmngr/dirmngr.c, scd/scdaemon.c: Call + gpgrt_set_syscall_clamp after npth_init. + +2016-10-06 Justus Winter + + common: Avoid pointer arithmetic on string literals. + * common/gettime.c (rfctimestamp): Use indexing instead. + * common/signal.c (got_fatal_signal): Likewise. + + g10: Fix singular term. + * g10/tofu.c (ask_about_binding): Fix singular message. + + g10: Use appropriate variant of 'abs'. + * g10/tofu.c (ask_about_binding): Use 'labs' instead of 'abs'. + + sm: Remove statement without effect. + * sm/call-dirmngr.c (gpgsm_dirmngr_isvalid): Remove statement without + effect. + + g10: Fix testing for debug flag. + * g10/parse-packet.c (set_packet_list_mode): Fix testing for debug + flag. + + tools: Improve error handling. + * tools/gpg-wks-server.c (copy_key_as_binary): Initialize 'argv'. + + gpgscm: Update callsite of 'gnupg_spawn_process'. + * tests/gpgscm/ffi.c (do_spawn_process): Adapt to the changes to + 'gnupg_spawn_process'. + +2016-10-05 Werner Koch + + wks: Send key encrypted as required by draft -02. + * tools/gpg-wks-client.c (get_key): Encrypt. + (encrypt_response): Take care of --fake-submission-addr. + + wks: Add option --fake-submission-addr to gpg-wks-client. + * tools/gpg-wks-client.c (oFakeSubmissionAddr): New. + (opts): Add option --fake-submission-addr. + (fake_submission_addr): New variable. + (parse_arguments): Set it. + (command_send): Use --fake-submission-addr. + + agent: Another minor fix to map_supervised_sockets. + * agent/gpg-agent.c (map_supervised_sockets): Remove debug message. + Provide correct fd in the second error case. + + agent: Fix npth + supervised mode problem. + * agent/gpg-agent.c (main): Initialize modules in supervised mode. + +2016-10-05 Daniel Kahn Gillmor + + agent: Fix error handling in map_supervised_sockets. + * agent/gpg-agent.c (map_supervised_sockets): the file descriptor to + close on error is fd, not i. + +2016-10-04 Werner Koch + + agent: Streamline the supervised mode code. + * agent/gpg-agent.c (get_socket_path): Rename to ... + (get_socket_name): this. This is to comply with the GNU coding guide. + Use xtrymalloc instead of malloc. Do not build for W32. + (map_supervised_sockets): Use strtokenize and set the the socket names + here. + (main): Adjust for above change. Do not close the socket. + + agent: Adjust cleanup for supervised mode. Fix for W32. + * agent/gpg-agent.c (opts) [W32]: Remove option --supervised. + (is_supervised): Move from main() to global. + (inhibit_socket_removal): New. + (cleanup): Take care of supervise mode and INHIBIT_SOCKET_REMOVAL. + (check_own_socket_thread): Set INHIBIT_SOCKET_REMOVAL instead of + seting the socket names to empty. + + agent: Adjust supervised mode for the new default socket names. + * agent/gpg-agent.c (main): In supervised mode do not provide default + socket names. Unset DISPLAY and INSIDE_EMACS. Use log_error and + agent_exit. + +2016-10-04 Daniel Kahn Gillmor + + agent: Implement --supervised command (for systemd, etc). + * agent/gpg-agent.c (get_socket_path): New function for POSIX systems + to return the path for a provided unix-domain socket. + (map_supervised_sockets): New function to inspect $LISTEN_FDS and + $LISTEN_FDNAMES and map them to the specific functionality offered by + the agent. + (main): Add --supervised command. When used, listen on already-open + file descriptors instead of opening our own. + * doc/gpg-agent.texi: Document --supervised option. + +2016-10-04 Justus Winter + + build,w32: Unconditionally build tests. + * configure.ac (run_tests, RUN_TESTS, RUN_GPG_TESTS): Remove + variables. They are misleadingly named, as they inhibit building the + tests. There is no reason not to build the tests even when + cross-compiling, as they are only run if one does 'make check'. + * Makefile: Adapt accordingly. + * tests/Makefile.am: Adapt accordingly. Avoid building 'asschk' on + Windows as it uses non-portable functions. + + tests,w32: Do not expose 'glob' to gpgscm. + * tests/gpgscm/ffi.c (do_glob): Remove function. + (ffi_init): Likewise. + + tests,w32: Avoid using 'glob'. + * tests/openpgp/setup.scm: Avoid 'glob' which is not available on + mingw. + + tools: Ignore existing directories in gpgtar. + * tools/gpgtar-extract.c (extract_directory): Ignore existing + directories now that we have '--directory'. + +2016-10-04 NIIBE Yutaka + + agent, dirmngr, scd: npth_init must be after fork. + * agent/gpg-agent.c (thread_init_once, initialize_modules): New. + (main): Make sure no daemonizing-fork call after npth_init, and no npth + calls before npth_init, with care of npth calls by assuan hooks. + * dirmngr/dirmngr.c (thread_init): New. + (main): Make sure npth_init must not be called before daemonizing fork. + * scd/scdaemon.c (main): Likewise. + +2016-09-30 Werner Koch + + agent: Remove the warning for the GKR hijacking. + * g10/call-agent.c (check_hijacking): Remove. + (start_agent): Remove call. + + agent: Create the extra sockets in the standard socket dir. + * agent/gpg-agent.c (main): Take the socketdir in account for the + default sockets. + * tools/gpgconf.c (list_dirs): Add "agent-extra-socket" and + "agent-browser-socket". + + agent: Kludge to allow disabling of the extra sockets. + * agent/gpg-agent.c (main): Check for special socket names. + + wks: Avoid long trustdb checks. + * tools/wks-receive.c (verify_signature): Use --always-trust. + +2016-09-30 Justus Winter + + build: Fix build against libiconv. + * agent/Makefile.am: Add INCICONV and LIBICONV. + * common/Makefile.am: Likewise. + * tools/Makefile.am: Likewise. + + agent: Enable restricted, browser, and ssh socket by default. + * agent/gpg-agent.c (main): Provide defaults for 'extra-socket' and + 'browser-socket', enable ssh socket by default, but do not emit the + 'SSH_AUTH_SOCK' variable unless it has been explicitly requested. + * configure.ac (GPG_AGENT_{EXTRA,BROWSER}_SOCK_NAME): New definitions. + * doc/gpg-agent.texi: Update documentation. + + w32: Fix STARTTLS on LDAP connections. + * dirmngr/ks-engine-ldap.c (my_ldap_connect): Fix build against + . + +2016-09-29 Werner Koch + + wks: Partly implement draft-koch-openpgp-webkey-service-02. + * tools/gpg-wks.h (WKS_RECEIVE_DRAFT2): New. + * tools/wks-receive.c: Include rfc822parse.h. + (struct receive_ctx_s): Add fields PARSER, DRAFT_VERSION_2, and + MULTIPART_MIXED_SEEN. + (decrypt_data): Add --no-options. + (verify_signature): Ditto. + (new_part): Check for Wks-Draft-Version header. Take care of text + parts. + (wks_receive): Set Parser and pass a flag value to RESULT_CB. + * tools/gpg-wks-client.c (read_confirmation_request): New. + (main) : Call read_confirmation_request instead of + process_confirmation_request. + (command_receive_cb): Ditto. Add arg FLAGS.. + (decrypt_stream_status_cb, decrypt_stream): New. + (command_send): Set header Wks-Draft-Version. + * tools/gpg-wks-server.c (struct server_ctx_s): Add field + DRAFT_VERSION_2. + (sign_stream_status_cb, sign_stream): New. + (command_receive_cb): Set draft flag. + (send_confirmation_request): Rework to implement protocol draft + version 2. + + * tools/gpg-wks.h (DBG_MIME_VALUE, DBG_PARSER_VALUE): New. + (DBG_MIME, DBG_PARSER, DBG_CRYPTO): New. Use instead of a plain + opt.debug where useful. + * tools/gpg-wks-client.c (debug_flags): Add "mime" and "parser". + * tools/gpg-wks-server.c (debug_flags): Ditto. + + tools: Convey signeddata also to the part_data callback in mime-parser. + * tools/mime-parser.c (mime_parser_parse): Factor some code out to ... + (process_part_data): new. + ((mime_parser_parse): Also call process_part_data for signed data. + + tools: Allow retrieval of signed data from mime-maker. + * tools/mime-maker.c (find_part): New. + (mime_maker_get_part): New. + + tools: Change mime-maker to write out CR,LF. + * tools/mime-maker.c (struct part_s): Add field PARTID. + (struct mime_maker_context_s): Add field PARTID_COUNTER. + (dump_parts): Print part ids. + (mime_maker_add_header): Assign PARTID. + (mime_maker_add_container): Ditto. + (mime_maker_get_partid): New. + (write_ct_with_boundary): Remove. + (add_header): Strip trailing white spaces. + (write_header): Remove trailing spaces trimming. Add arg BOUNDARY. + Handle emdedded LFs. + (write_gap, write_boundary, write_body): New. + (write_tree): Use new functions. + + tools: Simplify the mime-maker container creation. + * tools/mime-maker.c (struct part_s): Remove field MEDIATYPE. + (release_parts): Ditto. + (dump_parts): Print a body line only if tehre is a body. + (mime_maker_add_header): Check for body or container. + (mime_maker_add_container): Remove arg MEDIATYPE. Change all callers. + (mime_maker_end_container): New. + + tools: Give mime parser callbacks access to the rfc822 parser. + * tools/mime-parser.c (mime_parser_context_s): Add field MSG. + (parse_message_cb): Set it. + (mime_parser_rfc822parser): New. + * tools/mime-parser.h: Declare rfc822parse_t for the new prototype. + +2016-09-29 Justus Winter + + dirmngr: Fix STARTTLS on LDAP connections. + * dirmngr/ks-engine-ldap.c (my_ldap_connect): Fix unfortunate typo. + +2016-09-28 Werner Koch + + gpg: Improve WKD by importing only the requested UID. + * g10/keyserver.c: Include mbox-util.h. + (keyserver_import_wkd): Do not use the global import options but + employ an import filter. + + gpg: Reject import if an import filter removed all user ids. + * g10/import.c (any_uid_left): New. + (import_one): Check that a UID is left. + + gpg: Make import filter data object more flexible. + * g10/main.h (import_filter_t): New. + * g10/import.c (struct import_filter_s): Declare struct. + (import_keep_uid, import_drop_sig): Replace by ... + (import_filter): new. Adjust all users. + (cleanup_import_globals): Move code to ... + (release_import_filter): new. + (save_and_clear_import_filter): New. + (restore_import_filter): New. + + gpg: Make sure that internal key import is done with a binary stream. + * g10/import.c (import_keys_internal): Open stream in binary mode. + +2016-09-27 Justus Winter + + build: Do not link gpg-connect-agent against npth. + * tools/Makefile.am: Do not link gpg-connect-agent against npth. + + build: Fix check for resolver library on macOS. + * configure.ac: Check for the mangled name of 'dn_skipname' first. + + common: Correctly handle modules relying on npth. + * common/Makefile.am (common_sources): Drop 'call-gpg.{c,h}'. + (with_npth_sources): New variable. + (libcommonpth_a_SOURCES): Use the new variable. + +2016-09-27 NIIBE Yutaka + + agent, sm: Set CTX after start_agent. + * g10/call-agent.c (agent_keytocard): Assign parm.ctx after start_agent. + * sm/call-agent.c (gpgsm_agent_pksign, gpgsm_scd_pksign) + (gpgsm_agent_readkey, gpgsm_agent_scd_serialno) + (gpgsm_agent_scd_keypairinfo, gpgsm_agent_marktrusted) + (gpgsm_agent_passwd, gpgsm_agent_get_confirmation) + (gpgsm_agent_ask_passphrase, gpgsm_agent_keywrap_key) + (gpgsm_agent_export_key): Likewise. + + dirmngr: Removal of no-libgcrypt.o. + * dirmngr/Makefile.am (dirmngr_ldap_LDADD): Remove no-libgcrypt.o. + + agent: Allow only specific digest size for ECDSA. + * agent/pksign.c (do_encode_dsa): Fix validation of digest size. + +2016-09-22 Neal H. Walfield + + g10: When adding a user id, make sure the keyblock has been prepared. + * g10/keyedit.c (keyedit_quick_adduid): Call merge_keys_and_selfsig on + KEYBLOCK before adding the user id. + * tests/openpgp/quick-key-manipulation.scm: Make sure that the key + capabilities don't change when adding a user id. + (key-data): New function. + +2016-09-20 Justus Winter + + tests: Add documentation, make interactive debugging possible. + * tests/openpgp/README: Add documentation about debugging and + interfacing with GnuPG. + * tests/openpgp/run-tests.scm (test::run-sync): Hand stdin to the + child so that we can use a repl in the tests. + + tests: Port the quick key manipulation test to Scheme. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/quick-key-manipulation.scm: New file. + + tests: Remove list of tests from the test runner. + * tests/openpgp/run-tests.scm: Drop hardcoded list. + + tests: Reduce runtime of excessive test. + * tests/openpgp/conventional-mdc.scm: Use only two plaintexts when + iterating over all cipher algorithms. + + dirmngr: Fix type. + * dirmngr/dns-stuff.c (get_dns_cert): Fix type in fallback code. + +2016-09-20 Andre Heinecke + + dirmngr: Open file CRL's in binary mode. + * dirmngr/crlcache.c (crl_cache_load): Open file in binary mode. + +2016-09-20 NIIBE Yutaka + + doc: Fix a xref usage. + +2016-09-20 Ineiev + + doc: Do not end section names with "." + +2016-09-20 NIIBE Yutaka + + doc: minor fix for @xref. + * doc/yat2m.c (proc_texi_cmd): Captalize "see" for xref. + +2016-09-20 Justus Winter + + doc: Implement simple '@ref'erences. + * doc/yat2m.c (proc_texi_cmd): Handle '@ref'. + +2016-09-20 Ineiev + + doc: Fix full stops. + * doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi, + doc/instguide.texi, doc/scdaemon.texi, doc/specify-user-id.texi, + doc/tools.texi: Fix. + + doc: Fix spacings. + * doc/debugging.texi, doc/dirmngr.texi, doc/gpg-agent.texi, + doc/gpg.texi, doc/tools.texi: Fix. + + doc: Improve markup. + * doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi, + doc/howto-create-a-server-cert.texi, doc/scdaemon.texi, + doc/specify-user-id.texi, doc/tools.texi: Fix. + + doc: Replace rfc0123 with RFC-0123. + * doc/gpg.texi, doc/gpgsm.texi, doc/specify-user-id.texi: Fix. + + doc: Add missing description of datafile. + * doc/gpg.texi: Fix. + + doc: Replace UTF8 with UTF-8. + * doc/gpg.texi: Fix. + + doc: Fix mistakes. + * doc/dirmngr.texi, doc/gpg.texi, doc/gpgsm.texi, + doc/howto-create-a-server-cert.texi, + doc/scdaemon.texi, doc/tools.texi: Fix. + + doc: Eliminate inconsistent UK English. + * doc/dirmngr.texi, doc/gpg-agent.texi, doc/scdaemon.texi, + doc/tools.texi: Fix. + + doc: Use the right reference commands. + * doc/debugging.texi, doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi, + doc/tools.texi: Fix. + + doc: Fix "Not(e) that you can(not) abbreviate". + * doc/dirmngr.texi, doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi, + doc/scdaemon.texi, doc/tools.texi: Fix. + + doc: Fix typos. + * doc/debugging.texi, doc/dirmngr.texi, doc/glossary.texi + * doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi + * doc/instguide.texi, doc/opt-homedir.texi, doc/scdaemon.texi + * doc/specify-user-id.texi, doc/tools.texi: Fix. + + doc: Fix Martin Hellman's name. + * doc/contrib.texi: Fix. + +2016-09-19 Justus Winter + + tests: Refine the repl function. + * tests/gpgscm/repl.scm (repl): Add an argument 'environment'. + (interactive-repl): Add an optional argument 'environment'. + + tests: Implement interpreter shutdown using exceptions. + * tests/gpgscm/ffi.c (ffi_init): Rename 'exit' to '_exit'. + * tests/gpgscm/ffi.scm (*interpreter-exit*): New variable. + (throw): New function. + (exit): New function. + + tests: Correctly handle exceptions in resource handling macros. + * tests/gpgscm/tests.scm (letfd): Correctly release resources when an + exception is thrown. + (with-working-directory): Likewise. + (with-temporary-working-directory): Likewise. + (lettmp): Likewise. + + tests: Refine exception handling. + * tests/gpgscm/init.scm (catch): Bind all arguments to '*error*' in + the error handler, update and fix comment. + (*error-hook*): Revert to original definition. + * tests/gpgscm/tests.scm (tr:do): Adapt accordingly. + * tests/openpgp/issue2419.scm: Likewise. + + tests: Use descriptive temporary file names. + * tests/gpgscm/ffi.c (do_get_isotime): New function. + (ffi_init): Add parameter 'scriptname', bind new function and + scriptname. + * tests/gpgscm/ffi.h (ffi_init): Update prototype. + * tests/gpgscm/main.c (main): Hand in the script name. + * tests/gpgscm/tests.scm (mkdtemp): Use current time and script name + for the names of temporary directories. + +2016-09-19 Werner Koch + + gpg: Fix regression in fingerprint printing. + * g10/keylist.c (list_keyblock_print): Do not depend calling + print_fingerprint on opt.keyid_format. + + dirmngr: Silence diagnostics about starting housekeeping. + * dirmngr/dirmngr.c (housekeeping_thread): Print info only in very + verbose mode. + +2016-09-19 Justus Winter + + g10: Fix memory leak. + * g10/tofu.c (build_conflict_set): Free 'kb_all'. + +2016-09-19 Werner Koch + + doc: Update license information. + * tests/fake-pinentries/COPYING: Rename to ... + * COPYING.CC0: this. Add a note on the scope of this license. + * COPYING.LIB: Add a note on the scope of this license. + * AUTHORS (License): Mention CC) license. + + gpgscm: Fix gcrypt version check. + * tests/gpgscm/main.c (main): Check against required and not installed + version. + + gpg: Avoid malloc failure due to no key signatures. + * g10/keyedit.c (check_all_keysigs): Check early for no key + signatures. Use xtrycalloc. + +2016-09-17 NIIBE Yutaka + + Fix comment and format. + * agent/protect-tool.c (main): Fix comment. + * doc/DETAILS (colon listings): Fix list. + * tests/openpgp/multisig.test: Fix comment. + +2016-09-17 Daniel Kahn Gillmor + + Fix more spelling. + * NEWS, acinclude.m4, agent/command-ssh.c, agent/command.c, + agent/gpg-agent.c, agent/keyformat.txt, agent/protect-tool.c, + common/asshelp.c, common/b64enc.c, common/recsel.c, doc/DETAILS, + doc/HACKING, doc/Notes, doc/TRANSLATE, doc/dirmngr.texi, + doc/faq.org, doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi, + doc/instguide.texi, g10/armor.c, g10/gpg.c, g10/keyedit.c, + g10/mainproc.c, g10/pkclist.c, g10/tofu.c, g13/sh-cmd.c, + g13/sh-dmcrypt.c, kbx/keybox-init.c, m4/pkg.m4, sm/call-dirmngr.c, + sm/gpgsm.c, tests/Makefile.am, tests/gpgscm/Manual.txt, + tests/gpgscm/scheme.c, tests/openpgp/gpgv-forged-keyring.scm, + tests/openpgp/multisig.test, tests/openpgp/verify.scm, + tests/pkits/README, tools/applygnupgdefaults, + tools/gpg-connect-agent.c, tools/mime-maker.c, tools/mime-parser.c: + minor spelling cleanup. + + move some file encodings to UTF-8. + * dirmgnr/cdblib.c: comment used unnecesary hyphenation + * dirmngr/crlcache.h: comment was iso-8859-1 + * doc/contrib.text: list contributors using UTF-8 (now we can + acknowledge many more people using their preferred orthography) + + At least one other files remains in a non-UTF-8 encoding, which i'm + not sure what to do with: + + - build-aux/speedo/w32/inst.nsi is ISO-8859-1, but maybe Windows needs + it that way? + +2016-09-16 Neal H. Walfield + + g10: On failure, propagate the return code. + * g10/tofu.c (tofu_register_encryption): If get_trust fails, set RC. + + g10: Don't ignore failure. On failure, rollback. + * g10/tofu.c (tofu_set_policy): If record_binding fails, fail. If the + function fails, rollback the transaction. + + g10: Load the key block if the supplied user id list is NULL. + * g10/tofu.c (tofu_register_encryption): Load the key block if + USER_ID_LIST is NULL. + + g10: Use the accessor functions for accessing and comparing key ids. + * g10/tofu.c (get_trust): Use the pk_main_keyid accessor function. + (tofu_register_signature): Likewise. + (tofu_register_encryption): Likewise. + (tofu_set_policy): Likewise and also use pk_keyid and keyid_cmp. + +2016-09-16 Daniel Kahn Gillmor + + po: convert localizations to UTF-8. + * po/{it,et,pl,ro,gl,es,el,sk,pt,eo,hu}.po: convert to UTF-8 + + This was an automated conversion process, using: + + for x in po/{it,et,pl,ro,gl,es,el,sk,pt,eo,hu}.po; do + cs=$(grep charset= $x | cut -f2 -d= | cut -f1 -d\\) + iconv -f $cs -t UTF-8 < $x >$x.tmp + sed "s/$cs/UTF-8/" < $x.tmp > $x + rm -f $x.tmp + done + +2016-09-16 NIIBE Yutaka + + scd: Add support of ECC pubkey attribute. + * scd/app-openpgp.c (ECC_FLAG_PUBKEY): New. + (send_key_attr, get_public_key, ecc_writekey, do_auth, do_decipher) + (parse_algorithm_attribute): Check ECC_FLAG_DJB_TWEAK. + (build_ecc_privkey_template): Add ECC_Q and ECC_Q_LEN. + Support offering public key when ECC_FLAG_PUBKEY sets. + (ecc_writekey): Supply ECC_Q and ECC_Q_LEN. + (parse_algorithm_attribute): Parse pubkey-required byte. + +2016-09-15 Justus Winter + + g10: Add missing header. + * g10/trustdb.c: Include 'mbox-util.h'. + +2016-09-15 Neal H. Walfield + + g10: Only consider bindings matching the signer's user id. + * g10/trustdb.c (tdb_get_validity_core): If the signer's user id + subpacket is present, only consider matching user ids. + + g10: Don't include the signature when printing a binding's validity. + * g10/mainproc.c (check_sig_and_print): When printing information + about a binding don't include the current signature. + +2016-09-15 Daniel Kahn Gillmor + + tests/fake-pinentries: fake pinentries for downstream developers. + * tests/fake-pinentries/README.txt and + tests/fake-pinentries/fake-pinentry.{sh,py,pl,php}}: New public + domain (CC0) files to encourage better test suite practices from + downstream developers. + * tests/fake-pinentries/COPYING (new): a copy of + https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt + + spelling: conenction should be connection. + * dirmngr/server.c, sm/server.c: s/conenction/connection/ + + spelling: correct achived to achieved. + +2016-09-15 NIIBE Yutaka + + tests/gpgscm: Fix use of pointer. + * tests/gpgscm/scheme-private.h (struct scheme): Use (void *) for + alloc_seg. + * tests/gpgscm/scheme.c (alloc_cellseg): Use (void *) for cp. Use + (void *) for coercion of address calculation. + +2016-09-14 Neal H. Walfield + + g10: Fix whitespace. + * g10/tofu.c (show_statistics): Fix whitespace. + + g10: Correctly compute the euclidean distance. + * g10/tofu.c (write_stats_status): Correctly compute the euclidean + distance. + (show_statistics): Likewise. + + g10: Change the default TOFU policy for UTKs to good. + * g10/tofu.c (get_trust): Change the default TOFU policy for UTKs to + good. + + g10: Add missing static qualifier. + * g10/tofu.c (cross_sigs): Add missing static qualifier. + + g10: Default to the "good" TOFU policy for keys signed by a UTK. + * g10/tofu.c (signed_by_utk): New function. + (get_trust): If a key is signed by an ultimately trusted key, then + set any bindings to good. + +2016-09-14 Werner Koch + + gpg: Emit a new error status line in --quick-adduid. + * g10/keyedit.c (menu_adduid): Emit an ERROR status for an existsing + user id. + + gpg: Allow use of "default" algo for--quick-addkey. + * g10/keygen.c (quick_generate_keypair): Write a status error. + (parse_algo_usage_expire): Set a default curve. + +2016-09-13 Werner Koch + + gpg: Improve usability of --quick-gen-key. + * g10/keygen.c (FUTURE_STD_): New constants. + (parse_expire_string): Handle special keywords. + (parse_algo_usage_expire): Allow "future-default". Simplify call to + parse_expire_string. + (quick_generate_keypair): Always allow an expiration date. Replace + former "test-default" by "future-default". + +2016-09-12 Werner Koch + + gpg: Avoid mixing up status and colon line output. + * g10/keylist.c (list_keyblock_colon): Avoid calling functions which + trigger a status line output before having printed a LF. + +2016-09-12 Justus Winter + + tests: Simplify tofu test. + * tests/openpgp/tofu.scm: Simplify now that we only have one db + format. + +2016-09-10 Ben Kibbey + + Portability build fix. + * kbx/Makefile.am: Add NETLIBS. + * dirmngr/Makefile.am: Ditto for dirmngr_ldap. + + Fix symbol conflict. + * g10/gpgcompose.c: Rename struct siginfo to signinfo. + +2016-09-09 Daniel Kahn Gillmor + + gpg: print fingerprint regardless of keyid-format. + * g10/keylist.c (print_fingerprint): use compact format independent of + keyid-format; (print_key_line): always print the fingerprint + +2016-09-08 Werner Koch + + gpg: Remove option --yes from gpgv. + * g10/gpgv.c (opts): Remove --yes. + (main): Always set opt.ANSWER_YES. + + gpg: Add options --output and --yes to gpgv. + * g10/gpgv.c (oOutput, oAnswerYes): New. + (opts): Add --output and --yes. + (main): Implement options. + + gpg: Make --output work with --verify. + * g10/mainproc.c (proc_plaintext): Handle opt.output. + +2016-09-07 Werner Koch + + dirmngr: Terminate on deletion of the socket file (Linux only). + * dirmngr/dirmngr.c [HAVE_INOTIFY_INIT]: Include sys/inotify.h. + (oDisableCheckOwnSocket): New. + (opts): Add --disable-check-own-socket. + (disable_check_own_socket): New var. + (parse_rereadable_options): Set that var. + (my_inotify_is_name) [HAVE_INOTIFY_INIT]: New. + (handle_connections) [HAVE_INOTIFY_INIT]: New. + +2016-09-07 Neal H. Walfield + + g10: Use the time a signature was seen, not the embedded time, for stats + * g10/tofu.c (ask_about_binding): Use the time that a signature was + seen, not allegedly generated, when generating statistics. + + tests: Don't use --tofu-db-format. + * tests/openpgp/tofu.scm: Remove use of --tofu-db-format, which is + deprecated. + + g10: Check for a new binding a bit later. + * g10/tofu.c (build_conflict_set): Check for the current key after + looking for conflicts and removing any '!'. + + g10: Change TOFU code to respect --faked-system-time. + * g10/tofu.c (record_binding): New parameter now. Update callers. + Don't use SQLite's strftime('%s','now') to get the current time, use + NOW. + (ask_about_binding): Likewise. + (get_trust): New parameter now. Update callers. + (show_statistics): Likewise. + (tofu_register_signature): Don't use SQLite's strftime('%s','now') to + get the current time, use gnupg_get_time(). + (tofu_register_encryption): Likewise. + + g10: Use the correct conversion function. + * g10/tofu.c (show_statistics): Use string_to_ulong, not + string_to_long. + +2016-09-07 Werner Koch + + gpg: Fix format string issues in tofu. + * g10/tofu.c (write_stats_status): Use ulong for MESSSAGES. Fix + format strings. Simplify by using the new write_status_printf. + +2016-09-06 Neal H. Walfield + + g10: Make sure some functions are passed a primary key. + * g10/tofu.c (get_trust): Make sure the caller provides a primary key. + (tofu_register_signature): Likewise. + + g10: Tweak TOFU's verbosity. + * g10/tofu.c (time_ago_str): Only show the most significant unit. + * g10/tofu.c (show_statistics): Tweak the output. + + g10: Only show the TOFU warning once per key. + * g10/tofu.c (show_statistics): Return whether to call show_warning. + Move the warning from here... + (show_warning): ... to this new function. + (tofu_get_validity): If show_statistics returns a non-zero value, call + show_warning. + + g10: Record and show statistics for encrypted messages when using TOFU. + * g10/tofu.c: Include "sqrtu32.h". + (struct tofu_dbs_s.s): Rename get_trust_gather_other_keys to + get_trust_gather_signature_stats. Add new field + get_trust_gather_encryption_stats. + (initdb): Create the encryptions table. + (ask_about_binding): Show the encryption statistics too. + (tofu_register): Rename from this... + (tofu_register_signature): ... to this and update callers. + (tofu_register_encryption): New function. + (write_stats_status): Add parameters encryption_count, + encryption_first_done and encryption_most_recent. Update callers. + Compute the trust using the euclidean distance of the signature and + signature count. Compare with twice the threshold. Include + encryption count information in the TFS and TOFU_STATS lines. + (show_statistics): Also get information about the encrypted messages. + * g10/trustdb.c (tdb_get_validity_core): Use it. + + g10: Simplify the binding statistics shown for a TOFU conflict. + * g10/tofu.c (ask_about_binding): Simplify binding statistics. + +2016-09-06 Justus Winter + + gpgscm: Fix detection of unbalanced parenthesis. + * tests/gpgscm/main.c (load): Print error message. + * tests/gpgscm/scheme.c (opexe_0): Correctly report nesting level when + loading files. + + tests: Fix test. + * tests/openpgp/multisig.scm: Add missing parenthesis. + +2016-09-06 Werner Koch + + agent: Terminate on deletion of the socket file (Linux only). + * configure.ac (AC_CHECK_FUNCS): Chec for inotify_init. + * agent/gpg-agent.c [HAVE_INOTIFY_INIT]: Include sys/inotify.h. + (my_inotify_is_name) [HAVE_INOTIFY_INIT]: New. + (handle_connections) [HAVE_INOTIFY_INIT]: New. + +2016-09-05 Justus Winter + + tests: Speed up the test suite. + * tests/openpgp/run-tests.scm (test::run-sync): Pass additional + arguments to the test. + (test::run-sync-quiet): Likewise. + (test::run-async): Likewise. + (run-tests-{parallel,sequential}-isolated): Create a tarball of the + gnupghome, then extract it for each test. + * tests/openpgp/setup.scm: Refactor into functions, add an interface + to tar-up the created environment, and untar it multiple times. + + common: Restore a simpler variant of 'gnupg_wait_process'. + * common/exechelp-posix.c (gnupg_wait_process): Use the code prior to + 5ba4f604. + + common: Fix error handling. + * common/exechelp-posix.c (store_result): Use xtrymalloc. + (gnupg_wait_processes): Likewise, and check result. + +2016-09-05 Neal H. Walfield + + g10: Don't add user attributes to the TOFU DB. + * g10/trustdb.c (tdb_get_validity_core): Skip user attributes. + +2016-09-05 Werner Koch + + agent: Silence --debug IPC output for connections from self. + * agent/command.c (server_local_s): Add fields 'greeting_seen' and + 'connect_from_self'. + (io_monitor): Do not log connections from self. + (start_command_handler): Set flag 'connect_from_self'. + * agent/gpg-agent.c (check_own_socket_thread): Disable logging. + (do_start_connection_thread): Do not log conection start and + termination if IPC debugging is enabled. + + agent: Small improvement of the server's local state. + * agent/command.c (sserver_local_s): Change flags to use only one bit. + (option_handler): Make an atoi return 1 or 0. + +2016-09-05 Neal H. Walfield + + g10: Refactor cross sig check code. + * g10/tofu.c (BINDING_NEW): New enum value. + (BINDING_CONFLICT): Likewise. + (BINDING_EXPIRED): Likewise. + (BINDING_REVOKED): Likewise. + (ask_about_binding): Move cross sig check from here... + (get_trust): ... and the conflict set building from here... + (build_conflict_set): ... to this new function. + (format_conflict_msg_part1): Replace parameter conflict with + conflict_set. Drop parameter fingerprint. Update callers. + (ask_about_binding): Drop unused parameter conflict and redundant + parameter bindings_with_this_email_count. Rename parameter + bindings_with_this_email to conflict_set. Update callers. + +2016-09-05 Justus Winter + + tests: Update README. + * tests/openpgp/README: Update. + + tests: Pass flags to test driver. + * tests/openpgp/Makefile.am (xcheck): Pass flags to 'run-tests.scm'. + + common: Improve waiting for processes on POSIX. + * common/exechelp-posix.c (struct terminated_child): New definition. + (terminated_children): New variable. + (store_result): New function. + (get_result): Likewise. + (gnupg_wait_process): Store results that were not requested and + consider previously stored results. + + waitpid(2) may return information about terminated children that we + did not yet request, and there is no portable way to wait for a + specific set of children. As a workaround, we store the results of + children for later use. + +2016-09-05 Werner Koch + + dirmngr: Exclude D lines from the IPC debug output. + * dirmngr/dirmngr.h: Include asshelp.h. + * dirmngr/server.c (server_local_s): Add inhibit_dara_logging fields. + (data_line_write): Implement logging inhibit. + (data_line_cookie_close): Print non-logged D lines. + (cmd_wkd_get, cmd_ks_get, cmd_ks_fetch): Do not log D lines. + (dirmngr_assuan_log_monitor): New. + * dirmngr/dirmngr.c (main): Register monitor function. + + common: Add an assuan logging monitor. + * common/asshelp.c (my_log_monitor): New var. + (my_libassuan_log_handler): Run that monitor. + (setup_libassuan_logging): Add arg to set a log monitor and change all + callers. + + gpg: New export filter drop-subkey. + * g10/import.c (impex_filter_getval): Add properties for key packets. + * g10/export.c (export_drop_subkey): New var. + (cleanup_export_globals): Release that var. + (parse_and_set_export_filter): Add filter "drop-subkey". + (apply_drop_subkey_filter): New. + (do_export_stream): Run that filter. + + common: Add string operator gt,ge,le,lt to recsel. + * common/recsel.c (recsel_parse_expr): Add them. + (recsel_dump): Print them. + (recsel_select): Evaluate them. + + gpg: Use a common filter_getval for import and export. + * g10/import.c (filter_getval): Rename to ... + (impex_filter_getval): this. Make global. + (apply_keep_uid_filter, apply_drop_sig_filter): Adjust. + * g10/export.c (filter_getval): Remove. + (apply_drop_sig_filter): Use impex_filter_getval. + +2016-09-03 NIIBE Yutaka + + scd: Fix an action after card removal. + * scd/command.c (update_card_removed): Call apdu_close_reader here. + +2016-09-02 Werner Koch + + wks: Add framework for policy flags. + * tools/call-dirmngr.c (wkd_get_policy_flags): New. + * tools/gpg-wks.h (struct policy_flags_s, policy_flags_t): New. + * tools/wks-util.c (wks_parse_policy): New. + * tools/gpg-wks-client.c (command_send): Get the policy flags to show + a new info line. + * tools/gpg-wks-server.c (get_policy_flags): New. + (process_new_key): get policy flag and add a stub for "auth-submit". + (command_list_domains): Check policy flags. + + dirmngr: Add --policy-flags option to WKD_GET. + * dirmngr/server.c (cmd_wkd_get): Add new option. + + common: Check read errors in name-value.c. + * common/name-value.c: Check for read errors. + +2016-09-02 NIIBE Yutaka + + scd: Release the card reader after card removal. + * scd/command.c (update_reader_status_file): Call apdu_close_reader. + + scd: Clean up unused shutdown method. + * scd/apdu.c (shutdown_ccid_reader, apdu_shutdown_reader): Remove. + (reset_ccid_reader): Don't set shutdown_reader. + * scd/ccid-driver.c (ccid_shutdown_reader): Remove. + + agent: invoke scdaemon with --homedir. + * agent/call-scd.c (start_scd): Supply --homedir option when it's not + default homedir. + + po: Update Japanese translation. + +2016-09-01 Neal H. Walfield + + g10: End transaction earlier. + * g10/tofu.c (ask_about_binding): End the transaction earlier. + + g10: Don't consider cross-signed keys to be in conflict. + * g10/tofu.c (cross_sigs): New function. + (ask_about_binding): If apparently conflicting keys are cross signed, + then don't mark them as conflicting. + +2016-09-01 Werner Koch + + gpg: Avoid homedir creation by --list-config. + * g10/gpg.c (main): Do not register a key for the list config + commands. + + gpg: Simplify code to print VALIDSIG. + * g10/mainproc.c (check_sig_and_print): Use hexfingerprint and + write_status_printf. + + gpg: Add new function write_status_printf. + * g10/cpr.c (write_status_printf): New. + + gpg: Fix printing of pubkey algo in --verbose signature verify. + * g10/sig-check.c (check_signature2): Replace arg PK by R_PK and + change the semantics. Also clear the other R_ args on function entry, + use gpg_error() and change retturn type to gpg_error_t. + * g10/mainproc.c (do_check_sig): Add arg R_PK. + (list_node): Pass NULL for new arg. + (check_sig_and_print): Rework to make use of the returned PK. + +2016-09-01 Neal H. Walfield + + g10: When asking about a TOFU binding conflict, default to unknown. + * g10/tofu.c (ask_about_binding): Default to unknown. + + g10: Add support for TRUST_NEVER. + * g10/pkclist.c (do_we_trust): Handle TRUST_NEVER, which can be + returned by the TOFU trust model. + (do_we_trust_pre): Print a different message if TRUSTLEVEL is + TRUST_NEVER. + (check_signatures_trust): Improve comment. + + g10: Improve text. + * g10/tofu.c (show_statistics): Improve the text (key and user id, not + just key). + + g10: Remove unused parameter. + * g10/tofu.c (show_statistics): Remove unused parameter sig_exclude. + Update callers. + +2016-09-01 Werner Koch + + gpg: Copy the correct digest for use by TOFU. + * g10/mainproc.c (do_check_sig): Use the current digest algo. + +2016-09-01 Neal H. Walfield + + g10: Be careful to not be in a transaction during long operations. + * g10/tofu.c (begin_transaction): New parameter only_batch. If set, + only start a batch transaction if there is none and one has been + requested. Update callers. + (tofu_suspend_batch_transaction): New function. + (tofu_resume_batch_transaction): Likewise. + (ask_about_binding): Take a ctrl_t, not a tofu_dbs_t. Update + callers. Gather statistics within a transaction. Suspend any batch + transaction when getting user input. + (get_trust): Take a ctrl_t, not a tofu_dbs_t. Update callers. + Enclose in a transaction. + (tofu_get_validity): Use a batch transaction, not a normal + transaction. + +2016-09-01 Werner Koch + + tests: Run test requiring the network only in maintainer-mode. + * dirmngr/Makefile.am (noinst_PROGRAMS, TESTS): Add module_net_tests. + (module_tests): Move t-dns-test to ... + (module_net_tests): here. + +2016-08-31 Werner Koch + + wks: Send a final message to the user. + * tools/gpg-wks-server.c (send_congratulation_message): New. + (check_and_publish): Call it. + + wks: Relax permission check for the top directory. + * tools/gpg-wks-server.c: Allow S_IXOTH for the top directory. + +2016-08-31 Neal H. Walfield + + g10: On a TOFU conflict, show whether the uids are expired or revoked. + * g10/tofu.c (struct signature_stats): Add fields is_expired and + is_revoked. + (signature_stats_prepend): Clear *stats when allocating it. + (ask_about_binding): Also show whether the user ids are expired or + revoked. + + doc: Add a help text for tofu.conflict. + * doc/help.txt (.gpg.tofu.conflict): New help text. + + g10: Always trust ultimately trusted keys. + * g10/tofu.c (get_trust): Always return TRUST_ULTIMATE for ultimately + trusted keys. + + g10: Fix error detection. + * g10/tofu.c: first_seen == 0 is not an error. + + g10: Update a key's TOFU policy in a transaction. + * g10/tofu.c (tofu_set_policy): Do the update in a transaction. + * g10/gpg.c (main): Do a TOFU policy update in a batch transaction. + + g10: Fix the show old policy functionality when changing a TOFU policy. + * g10/tofu.c (record_binding): Fix the show old policy functionality. + + g10: Drop unused argument. + * g10/tofu.c (begin_transaction): Remove unused option only_batch. + + gpg: Move state local to tofu.c to a private structure. + * g10/gpg.h (struct server_control_s.tofu): Move fields in_transaction + and batch_update_started from here... + * g10/tofu.c (struct tofu_dbs_s): ... to here. + + gpg: Avoid name spaces clash with future sqlite versions (2). + * g10/gpgsql.h (gpgsql_arg_type): Rename SQLITE_ARG_END to + GPGSQL_ARG_END, SQLITE_ARG_INT to GPGSQL_ARG_INT, SQLITE_ARG_LONG_LONG + to GPGSQL_ARG_LONG_LONG, SQLITE_ARG_STRING to GPGSQL_ARG_STRING, and + SQLITE_ARG_BLOB to GPGSQL_ARG_BLOB. + +2016-08-31 Werner Koch + + gpg: Fix regression in gpgv's printing of the keyid. + * g10/keyid.c (keystr): Take care of KF_NONE != KF_DEFAULT. + +2016-08-30 Neal H. Walfield + + g10: Improve TOFU batch update code. + * g10/gpg.h (tofu): Rename field batch_update_ref to + batch_updated_wanted. + * g10/tofu.c (struct tofu_dbs_s): Rename field batch_update to + in_batch_transaction. + (begin_transaction): Only end an extant batch transaction if we are + not in a normal transaction. When ending a batch transaction, really + end it. Update ctrl->tofu.batch_update_started when starting a batch + transaction. + (end_transaction): Only release a batch transaction if ONLY_BATCH is + true. When releasing a batch transaction, assert that there is no + open normal transaction. Only allow DBS to be NULL if ONLY_BATCH is + true. + (tofu_begin_batch_update): Don't update + ctrl->tofu.batch_update_started. + (opendbs): Call end_transaction unconditionally. + + g10: If a key has no valid user ids, change TOFU to return TRUST_NEVER. + * g10/tofu.c (tofu_get_validity): If a key has no valid (non-expired) + user ids, change TOFU to return TRUST_NEVER. + + g10: Change tofu_register & tofu_get_validity to process multiple uids. + * g10/tofu.c (tofu_register): Take a list of user ids, not a single + user id. Only register the bindings, don't compute the trust. Thus, + change return type to an int and remove the may_ask parameter. Update + callers. + (tofu_get_validity): Take a list of user ids, not a single user id. + Update callers. Observe signatures made by expired user ids, but + don't include them in the trust calculation. + + g10: Support nested transactions on the TOFU DB. + * g10/gpg.h (struct server_control_s): New field in_transaction. + * g10/tofu.c (struct tofu_dbs_s): Remove fields savepoint_inner and + savepoint_inner_commit. + (begin_transaction): Increment CTRL->TOFU.IN_TRANSACTION. Name the + savepoint according to the nesting level. + (end_transaction): Name the savepoint according to the nesting level. + Decrement CTRL->TOFU.IN_TRANSACTION. + (rollback_transaction): Likewise. Only ever rollback a non-batch + transaction. + (opendbs): Assert that there are no outstanding transactions. + + g10: Print the info text in more situations. + * g10/tofu.c (ask_about_binding): Print the info text when the policy + is ask and there are multiple bindings with the email address. + + g10: Print the formatted text. + * g10/tofu.c (ask_about_binding): Print the formatted text, not the + unformatted text. + + g10: When showing a user id's trust, pass the current signature. + * g10/mainproc.c (check_sig_and_print): Consistently pass SIG to + get_validity. + +2016-08-29 Werner Koch + + w32: Fix build regression due to 2aa0701. + * common/logging.c (fun_writer): Always declare 'name_for_err'. + + gpgconf: Print the plain socket directory with --list-dirs. + * tools/gpgconf.c (list_dirs): Add plain socketdir out. + + common: Add a default socket name feature. + * common/logging.c (log_set_socket_dir_cb): New. + (socket_dir_cb): New. + (set_file_fd): Allow "socket://". + (fun_writer): Implement default socket name. + * common/init.c (_init_common_subsystems): Register default socket. + + gpg: Make decryption of -R work w/o --try-secret-key or --default-key. + * g10/getkey.c (enum_secret_keys): At state 3 enumerate the keys in all + cases not just when --try-all-secrets is used. + +2016-08-25 Werner Koch + + gpg: Fix false negatives in Ed25519 signature verification. + * g10/pkglue.c (pk_verify): Fix Ed25519 signatrue values. + * tests/openpgp/verify.scm (msg_ed25519_rshort): New + (msg_ed25519_sshort): New. + ("Checking that a valid Ed25519 signature is verified as such"): New. + + common: Rename an odd named function. + * common/openpgp-oid.c (oid_crv25519): Rename to oid_cv25519. + (openpgp_oid_is_crv25519): Rename to openpgp_oid_is_cv25519. Change + callers. + + gpg: New option --with-tofu-info. + * g10/gpg.c (oWithTofuInfo): New. + (opts): Add --with-tofu-info. + (main): Set opt.with_tofu_info. + * g10/options.h (struct opt): Add field WITH_TOFU_INFO. + * g10/tofu.c (show_statistics): Add optional arg OUTFP and enter + special mode if not NULL. Change all callers. + (tofu_write_tfs_record): New. + * g10/keylist.c (list_keyblock_colon): Do not print the tofu policy as + part of the "uid" record. Print a new "tfs" record if the new option + is set. + * tests/openpgp/tofu.scm (getpolicy): Change from UID to TFS record. + +2016-08-24 Werner Koch + + gpg: Change TOFU_STATS to return timestamps. + * g10/tofu.c (write_stats_status): Add arg FP to print a colon + formated line. Adjust for changed TOFU_STATS interface. + (show_statistics): Let the query return timestamps and use + gnupg_get-time to compute the "time ago" values. + + common: Guarantee that gnupg_get_time does not return an error. + * common/gettime.c (gnupg_get_time): Abor if time() failed. + (gnupg_get_isotime): Remove now useless check. + (make_timestamp): Remove check becuase we already checked this modulo + the faked time thing. + + wks: Add command --supported to gpg-wks-client. + * tools/gpg-wks-client.c (aSupported): New. + (opts): Add --supported. + (parse_arguments): Ditto. + (main): Call command_supported. + (command_supported): New. + +2016-08-22 Werner Koch + + wks: Install gpg-wks-client under libexec. + * tools/Makefile.am (bin_PROGRAMS): Move gpg-wks-client to ... + (libexec_PROGRAMS): ...here. + + common: Remove unused vars in simple-pwquery. + * common/simple-pwquery.c (agent_send_option): Remove unused vars. + (simple_query): Ditto. + (agent_open): Ditto. Return RC on error. + (simple_pwquery): Remove unused vars. Remove shadowing of 'p'. + +2016-08-18 Werner Koch + + Release 2.1.15. + + po: Update German translation. + +2016-08-18 Åka Sikrom + + po: Update Norwegian translation. + +2016-08-18 Ineiev + + po: Update Russian translation. + +2016-08-18 Werner Koch + + gpg: Add import filter "drop-sig". + * g10/import.c (import_drop_sig): New variable. + (cleanup_import_globals): Release that. + (parse_and_set_import_filter): Add filter "drop-sig". + (filter_getval): Implement properties for drop-sig. + (apply_drop_sig_filter): New. + (import_one): Apply that filter. + + dirmngr: Remove all system daemon features. + * dirmngr/dirmngr.h (opts): Remove fields 'system_service' and + 'system_daemon'. + * common/homedir.c (dirmngr_sys_socket_name): Remove. + (dirmngr_user_socket_name): Rename to ... + (dirmngr_socket_name): this. Change call callers. + * common/asshelp.c (start_new_dirmngr): Remove the system socket + feature. + * tools/gpgconf.c (list_dirs): Do not print "dirmngr-sys-socket". + * sm/server.c (gpgsm_server): Adjust for removed system socket feature. + * dirmngr/server.c (cmd_getinfo): Ditto. + (cmd_killdirmngr): Remove check for system daemon. + (cmd_reloaddirmngr): Ditto. + * dirmngr/dirmngr.c (USE_W32_SERVICE): Remove macro. + (aService): Remove. + (opts): Remove --service. + (w32_service_control): Remove. + (real_main, call_real_main) [W32]: Remove wrapper. + (main): Remove Windows system service feature. Remove system dameon + feature. Use only the "~/.gnupg/dirmngr_ldapservers.conf" file. + * dirmngr/certcache.c (load_certs_from_dir): Remove warning in the + system dameon case. + * dirmngr/crlcache.c (DBDIR_D): Always use "~/.gnupg/crls.d". + * dirmngr/ocsp.c (validate_responder_cert): Do not call + validate_cert_chain which was used only in system daemon mode. + * dirmngr/validate.c (validate_cert_chain): Always use the code. + + gpg: New option --sender. + * g10/options.h (struct opt): Add field 'sender_list'. + * g10/gpg.c: Include mbox-util.h. + (oSender): New. + (opts): Add option "--sender". + (main): Parse option. + +2016-08-16 Werner Koch + + agent: Allow import of overly large keys. + * agent/command.c (MAXLEN_KEYDATA): Double the size. + +2016-08-14 Werner Koch + + g13: Allow the use of a g13tab label for --mount. + * g13/mount.c (g13_mount_container): Do not run the first access check + if syshelp is required. + + g13: Implement --umount for dm-crypt. + * g13/g13.c (main): Implement command --umount. + * g13/mount.c (g13_umount_container): use the syshelper if needed. + * g13/backend.c (be_umount_container): New. + * g13/be-dmcrypt.c (be_dmcrypt_umount_container): New. + * g13/call-syshelp.c (call_syshelp_run_umount): New. + * g13/sh-cmd.c (cmd_umount): New. + (register_commands): Register UMOUNT. + * g13/sh-dmcrypt.c (sh_dmcrypt_umount_container): New. + +2016-08-13 Werner Koch + + g13: Fix double free bug. + * g13/sh-cmd.c (cmd_mount, cmd_resume): Do not xfree TIUPLES. + + g13: Consider g13tab for a mount command. + * g13/sh-cmd.c (cmd_getkeyblob): New. + (register_commands): Register it. + * g13/call-syshelp.c (getkeyblob_data_cb): New. + (call_syshelp_get_keyblob): New. + * g13/mount.c: Include callsyshelp.h. + (g13_mount_container): Ask syshelp whether the filename is managed by + g13tab. Call syshelp to get the encrypted keyblob in this case. + + g13: Move some function around. + * g13/keyblob.c (g13_keyblob_decrypt): Move to ... + * g13/server.c: to here. + * g13/suspend.c, g13/mount.c: Include server.h. + * g13/Makefile.am (g13_syshelp_SOURCES): Add keyblob.c + + g13: New command --find-device. + * common/status.h (STATUS_BLOCKDEV: New. + * g13/call-syshelp.c: Include "call-syshelp.h". + (finddevice_status_cb, call_syshelp_find_device): New. + * g13/g13.c (aFindDevice): New. + (opts): Add "--find-device". + (main): Implement --find-device. + * g13/sh-cmd.c (cmd_finddevice): New. + (register_commands): Register new command. + +2016-08-12 Daniel Kahn Gillmor + + Avoid leading ": " in the log output when there are no prefixes. + * common/logging.c (do_logv): When no prefixes have been requested, + omit the ": " separator, since there is nothing on the left-hand + side of it. + + Call log_set_prefix() with human-readable labels. + * agent/preset-passphrase.c, agent/protect-tool.c, dirmngr/dirmngr.c + * dirmngr/t-http.c, g10/gpg.c, g10/gpgv.c, g13/g13-syshelp.c + * g13/g13.c, kbx/kbxutil.c, scd/scdaemon.c, sm/gpgsm.c + * tests/gpgscm/main.c, tools/gpg-check-pattern.c + * tools/gpg-connect-agent.c, tools/gpgconf.c, tools/gpgtar.c + * tools/symcryptrun.c: Invoke log_set_prefix() with + human-readable labels. + +2016-08-11 Werner Koch + + gpg: New option --input-size-hint. + * g10/options.h: Include stdint.h. + (struct opt): Add field 'input_size_hint'. + * g10/gpg.c (oInputSizeHint): New. + (opts): Add --input-size-hint. + (main): Set opt.input_size_hint. + * g10/progress.c (write_status_progress): Use the hint. + + common: New function string_to_u64. + * common/stringhelp.c (string_to_u64): New. + * dirmngr/http.c (longcounter_t): Remove. + (struct cookie_s): Change content_length to uint64_t. + (parse_response): Use string_to_u64. + +2016-08-11 Justus Winter + + common: Remove compatibility code. + * common/Makefile.am: Drop deleted files. + * common/w32-afunix.c: Delete file. + * common/w32-afunix.h: Likewise. + + common: Rework the simple password query module. + * common/simple-pwquery.c (writen, readline): Drop. + (agent_send_option, agent_send_all_options, agent_open): Just use + libassuan. + (simple_pw_set_socket): Simplify. + (default_inq_cb): New function. + (simple_pwquery, simple_query): Just use libassuan. + * agent/Makefile.am (gpg_preset_passphrase_LDADD): Add libassuan. + * tools/Makefile.am (symcryptrun_LDADD): Likewise. + + common: Remove simple password query error codes. + * common/simple-pwquery.h: Remove mapping function. Move all + definitions of status codes... + * common/simple-pwquery.c: ... here, and define them to meaningful gpg + error values. + * agent/preset-passphrase.c (preset_passphrase): Use error code as-is. + (forget_passphrase): Likewise. + * tools/symcryptrun.c (confucius_get_pass): Likewise. + +2016-08-10 Werner Koch + + gpg: Print the signer's UID during verification. + * g10/parse-packet.c (parse_signature): Sanitize the value stored in + SIGNERS_UID. + * g10/mainproc.c (issuer_fpr_string): New. + (check_sig_and_print): Print the signers' UID. Print the issuer + fingerprint in --rfc4880bis mode. + + common: New function try_make_printable_string. + * common/stringhelp.c (sanitize_buffer): Remove. Move code to ... + * common/miscellaneous.c (try_make_printable_string): new. + (make_printable_string): Call try_make_printable_string. + +2016-08-10 Justus Winter + + tests: Fix distcheck. + * tests/openpgp/issue2417.scm: Copy configuration. + +2016-08-10 Werner Koch + + gpg: Remove tofu database format "split". + * g10/options.h (struct opt): Remove field tofu_db_format. + * g10/gpg.h (server_control_s): Add fields tofu.batch_update_ref and + tofu.batch_update_started. + * g10/gpg.c (parse_tofu_db_format): Remove. + (main): Make option --tofu-db-format obsolete. + * g10/tofu.c: Major rework. Remove the pretty complicated and slower + split format and with that all the caching. Use the dbs struct + directly. Move global vars for batch update into CTRL. Change + calling conventions of some function to take CTRL or DBS pointers + instead of the former low-level database pointer. + +2016-08-10 Justus Winter + + g10: Fix opening of trust database. + * g10/tdbio.c (tdbio_set_dbname): This function explicitly checks for + the file size, but handled the case of a zero-sized file incorrectly + by returning success. Fix this by initializing the database in that + case. + * tests/openpgp/Makefile.am (XTESTS): Add new test. + * tests/openpgp/issue2417.scm: New file. + + tests: Fix distcheck. + * tests/openpgp/Makefile.am (EXTRA_DIST): Explicitly add setup and + teardown scripts now that they no longer are included in the list of + tests. + + tests: Improve temporary directory handling. + * tests/gpgscm/ffi.c (ffi_init): Rename 'mkdtemp'. + * tests/gpgscm/tests.scm (mkdtemp): New function that uses a sensible + location and template if no arguments are given. + (with-temporary-working-directory): Simplify accordingly. + (make-temporary-file): Likewise. + * tests/openpgp/run-tests.scm (run-tests-parallel-isolated): Likewise. + (run-tests-sequential-isolated): Likewise. + + gpgscm: Make the name of foreign functions more unique. + * tests/gpgscm/ffi-private.h (ffi_define_function_name): Add another + underscore. + + tests: Run each test in a clean environment. + * tests/openpgp/Makefile.am (TESTS_ENVIRONMENT): Drop obsolete + variables, add 'srcdir', use absolute paths. + (TESTS): Rename to 'XTESTS' to avoid emitting the automake test + runner. Drop 'setup.scm' and 'finish.scm'. + (xcheck): New target that runs 'run-tests.scm', our Scheme test suite + runner. It will run each test in a clean environment, isolated from + the other tests. + (EXTRA_DIST): Adapt accordingly. + * tests/openpgp/README: Likewise. + + tests: Make ssh test more robust. + * tests/openpgp/ssh.scm: Drop the 'MD5:' which was not printed by + previous ssh versions. + +2016-08-10 NIIBE Yutaka + + agent: SSH support fix. + * agent/command-ssh.c (ssh_handler_request_identities): Keep error + message same. + +2016-08-09 Werner Koch + + agent: Fix regression in recent ssh changes. + * agent/command-ssh.c (sexp_key_construct): Lowercase the algo name. + + gpg: Extend the PROGRESS line to give the used unit. + * g10/progress.c (write_status_progress): Print the units parameter. + +2016-08-09 Ben Kibbey + + Cleanup initialization of libgcrypt. + * common/init.c (init_common_subsystems): Initialize libgcrypt. + * dirmngr/Makefile.am (dirmngr_ldap): Link with libgcrypt. + +2016-08-09 NIIBE Yutaka + + agent: SSH support improvement. + * agent/command-ssh.c (ssh_handler_request_identities): Skip a key with + error, not giving up to handle the request itself. + * agent/cvt-openpgp.c (extract_private_key): Support "ecdsa" key. + +2016-08-08 Werner Koch + + gpg: Cleanup of dek_to_passphrase function (part 2). + * g10/passphrase.c (passphrase_get): Remove arg KEYID. Change arg + MODE to NOCACHE. + (passphrase_to_dek): Remove args KEYID and PUBKEY_ALGO. Split arg + MODE into CREATE and NOCACHE. Change all callers and adjust stubs. + (passphrase_clear_cache): Remove args KEYID and ALGO. They are not + used. Change caller. + + gpg: Cleanup of dek_to_passphrase function (part 1). + * g10/passphrase.c (passphrase_to_dek_ext): Remove args CUSTDESC and + CUSTPROMPT. Merge into the passphrase_to_dek wrapper. + (passphrase_get): Remove args CUSTOM_DESCRIPTION and CUSTOM_PROMPT. + +2016-08-08 NIIBE Yutaka + + agent: More clean up of SSH support. + * common/util.h (get_pk_algo_from_key): New. + * common/sexputil.c (get_pk_algo_from_key): The implementation. + * agent/gpg-agent.c: Remove include of openpgpdefs.h. + * agent/command-ssh.c (struct ssh_key_type_spec): Use integer ALGO. + (ssh_key_types): Update with GCRY_PK_*. + (make_cstring, sexp_extract_identifier): Remove. + (sexp_key_construct): Use gcry_pk_algo_name to get ALGO string. + (ssh_key_to_blob): Use cadr to get value list. + (ssh_key_type_lookup): Lookup with integer ALGO. + (ssh_receive_key): Follow the change of ssh_key_type_lookup. + (ssh_send_key_public): Likewise. Use get_pk_algo_from_key to get ALGO. + + tests: Add openpgp/gpgv-forged-keyring.scm. + * tests/openpgp/gpgv-forged-keyring.scm: New. + * tests/openpgp/forged-keyring.gpg: New. + * tests/openpgp/Makefile.am (TESTS): Add gpgv-forged-keyring.scm. + * tests/openpgp/defs.scm (tools): Add GPGV. + (GPGV): New. + +2016-08-06 Werner Koch + + agent: Fix long standing regression tracking the connection count. + * agent/gpg-agent.c (get_agent_active_connection_count): New. + (do_start_connection_thread, start_connection_thread_ssh): Bump + ACTIVE_CONNECTIONS up and down. + * agent/command.c (cmd_getinfo): Add subcommand "connections". + +2016-08-06 NIIBE Yutaka + + agent: Clean up SSH support. + * agent/command-ssh.c (file_to_buffer): Remove. + (ssh_handler_request_identities): Use agent_public_key_from_file. + +2016-08-05 Daniel Kahn Gillmor + + gpg: Avoid publishing the GnuPG version by default. + * g10/gpg.c (main): initialize opt.emit_version to 0 + * doc/gpg.texi: document different default for --emit-version + +2016-08-04 Werner Koch + + gpg: Make sure that keygrips are printed for each subkey. + * g10/keylist.c (list_keyblock_colon): Print an emprty grip in case of + an error. + + gpg: Always print the fingerprint in colons mode. + * g10/keylist.c (list_keyblock_colon): Remove arg FPR. Always print + fingerprint records. For secret keys always print keygrip records. + + tests: Use gpgconf to set the ssh socket envvar. + * tests/openpgp/ssh.scm ("SSH_AUTH_SOCK"): Use gpgconf. + + gpgconf: Add limited support for -0. + * tools/gpgconf.h (opt): Add field 'null'. + * tools/gpgconf.c: Add option --null/-0. + (list_dirs): Use it here. + +2016-08-04 Justus Winter + + tests: Update list of tests in Scheme test runner. + * tests/openpgp/run-tests.scm: Add missing tests. + + tests: Fix path to fake-pinentry. + * tests/openpgp/defs.scm: Correctly compute the path to fake-pinentry. + +2016-08-04 NIIBE Yutaka + + po: Update Japanese translation. + + po: update Japanese translation. + + g10: Fix checking key for signature validation. + * g10/sig-check.c (check_signature2): Not only subkey, but also primary + key should have flags.valid=1. + +2016-08-03 Justus Winter + + kbx: Add missing header file. + * kbx/keybox-update.c: Add missing header file. + +2016-08-03 Daniel Kahn Gillmor + + More cleanup of "allow to". + * README, agent/command.c, agent/keyformat.txt, common/i18n.c, + common/iobuf.c, common/keyserver.h, dirmngr/cdblib.c, + dirmngr/ldap-wrapper.c, doc/DETAILS, doc/TRANSLATE, + doc/announce-2.1.txt, doc/gpg.texi, doc/gpgsm.texi, + doc/scdaemon.texi, doc/tools.texi, doc/whats-new-in-2.1.txt, + g10/export.c, g10/getkey.c, g10/import.c, g10/keyedit.c, m4/ksba.m4, + m4/libgcrypt.m4, m4/ntbtls.m4, po/ca.po, po/cs.po, po/da.po, + po/de.po, po/el.po, po/eo.po, po/es.po, po/et.po, po/fi.po, + po/fr.po, po/gl.po, po/hu.po, po/id.po, po/it.po, po/ja.po, + po/nb.po, po/pl.po, po/pt.po, po/ro.po, po/ru.po, po/sk.po, + po/sv.po, po/tr.po, po/uk.po, po/zh_CN.po, po/zh_TW.po, + scd/app-p15.c, scd/ccid-driver.c, scd/command.c, sm/gpgsm.c, + sm/sign.c, tools/gpgconf-comp.c, tools/gpgtar.h: replace "Allow to" + with clearer text. + + In standard English, the normal construction is "${XXX} allows ${YYY} + to" -- that is, the subject (${XXX}) of the sentence is allowing the + object (${YYY}) to do something. When the object is missing, the + phrasing sounds awkward, even if the object is implied by context. + There's almost always a better construction that isn't as awkward. + + These changes should make the language a bit clearer. + + dirmngr: Emit correct spelling of "superseded". + * dirmngr/crlcache.c (list_one_crl_entry): Spell superseded correctly. + * dirmngr/ocsp.c (ocsp_invalid): Likewise. + + This might break some tools which parse the existing output and expect + misspellings, but i'm not sure there are many such tools, and we + should use standardized orthography going forward. + + Fix spelling and grammar. + * agent/learncard.c: s/coccured/occurred/ + * doc/dirmngr.texi: s/ommitted/omitted/, s/orginally/originally/, + s/reponses/responses/i + * doc/gpg-agent.texi, doc/dirmngr.texi, doc/gpg.texi: Fix "allows + to" to more conventional english usage. + * doc/tools.texi, g10/gpgcommpose.c, tests/openpgp/armor.scm, + tests/openpgp/armor.test: s/occured/occurred/ + * tools/gpgsplit.c: s/calcualting/calculating/ + * sm/server.c: s/formated/formatted/ + +2016-08-03 Werner Koch + + gpg,gpgsm: Block signals during keyring/keybox update. + * kbx/keybox-util.c (keybox_file_rename): Add arg BLOCK_SIGNALS. + * kbx/keybox-update.c (rename_tmp_file): Block all signals when doing + a double rename. + * g10/keyring.c (rename_tmp_file): Block all signals during the double + rename. + + common: New file utilproto.c. + * common/util.h: Factor prototypes from signal.c out to ... + * common/utilproto.h: new. + * common/Makefile.am (common_sources): Add new file. + +2016-08-01 Justus Winter + + gpgsm: Fix machine-readable key listing. + * sm/keylist.c (list_cert_colon): Drop superfluous colon. + + tests: Distribute standalone test runner. + * tests/openpgp/Makefile.am (EXTRA_DIST): Add missing file + 'run-tests.scm'. + +2016-07-28 Justus Winter + + tests: Fix distcheck. + * tests/openpgp/Makefile.am (sample_msgs): New variable. + (EXTRA_DIST): Also ship the sample msgs. + +2016-07-27 Fredrik Fornwall + + build: Fix check for Android. + * configure.ac: Match other Android targets as well. + +2016-07-26 Justus Winter + + common: Fix iobuf_peek corner case. + Previously, iobuf_peek on a file smaller than 'buflen' would hang. + + * common/iobuf.c (underflow): Generalize by adding a target parameter. + (iobuf_peek): Use this to prevent looping here. + * tests/openpgp/Makefile.am (TESTS): Add new test. + * tests/openpgp/setup.scm (dearmor): Move function... + * tests/openpgp/defs.scm (dearmor): ... here. + * tests/openpgp/issue2419.scm: New file. + * tests/openpgp/samplemsgs/issue2419.asc: Likewise. + + gpgscm: Do not shadow common function name in catch macro. + * tests/gpgscm/init.scm (catch): Do not shadow 'exit'. + + tests: Fix distcheck. + * tests/openpgp/Makefile.am (samplekeys): Add missing key. + + gpgscm: Make the verbose setting more useful. + * tests/gpgscm/ffi.c (do_get_verbose): New function. + (do_set_verbose): Likewise. + (ffi_init): Turn *verbose* into a function, add *set-verbose!*. + * tests/gpgscm/tests.scm (call): Adapt accordingly. + (call-with-io): Dump output if *verbose* is high. + (pipe-do): Adapt accordingly. + * tests/openpgp/defs.scm: Set verbosity according to environment. + * tests/openpgp/run-tests.scm (test): Adapt accordingly. + + common: Avoid excessive stack use. + * common/exectool.c (copy_buffer_shred): Make passing NULL a nop. + (gnupg_exec_tool_stream): Allocate copy buffers from the heap. + + common: Rework resource cleanup when handling errors. + * common/exectool.c (gnupg_exec_tool_stream): Rework error handling. + + common: Add unit test for exectool. + * common/Makefile.am: Build new test. + * common/t-exectool.c: New file. + +2016-07-25 Justus Winter + + g10: Fix key import statistics. + 'transfer_secret_keys' collects statistics on a subkey-basis, while + the other code does not. This leads to inflated numbers when + importing secret keys. E.g. 'count' is incremented by the main + parsing loop in 'import', and again in 'transfer_secret_keys', leading + to a total of 3 if one key with two secret subkeys is imported. + + * g10/import.c (import_secret_one): Adjust to the fact that + 'transfer_secret_keys' collects subkey statistics. + * tests/openpgp/Makefile.am (TESTS): Add new test. + * tests/openpgp/issue2346.scm: New file. + * tests/openpgp/samplekeys/issue2346.gpg: Likewise. + +2016-07-22 Justus Winter + + gpgscm: Make function more general. + * tests/gpgscm/tests.scm (in-srcdir): Accept more path fragments. + + g10: Properly ignore legacy keys in the keyring cache. + * g10/keyring.c (keyring_rebuild_cache): Properly ignore legacy keys + in the keyring cache. + * tests/migrations/Makefile.am (TESTS): Add new test. + * tests/migrations/common.scm (GPG-no-batch): New variable. + (run-test): New function. + * tests/migrations/issue2276.scm: New file. + * tests/migrations/issue2276.tar.asc: Likewise. + +2016-07-21 Justus Winter + + g10: Fix error handling. + * g10/tofu.c (show_statistics): Fix error handling, 0 is a valid + duration. + + g10: Drop superfluous begin transaction. + * g10/tofu.c (record_binding): We only need a transaction for the + split format. + + gpgscm: Make assert macro more accurate. + * tests/gpgscm/lib.scm (assert): Print the representation of the + failed expression. + + gpgscm: Make error message more useful. + * tests/gpgscm/scheme.c (opexe_0): Include names of missing function + parameters in the error message. + + g10: Fix crash. + * g10/tofu.c (tofu_closedbs): Fix freeing database handles up to the + cache limit. Previously, this would crash if db_cache_count == count. + +2016-07-20 NIIBE Yutaka + + scd: Fix card removal/reset on multiple contexts. + * scd/app.c (application_notify_card_reset): Add message for debug. + *scd/command.c (update_card_removed): Call release_application and set + SLOT -1 here. + (struct server_local_s): Remove app_ctx_marked_for_release. + (do_reset): Don't mark release but call release_application here. + (open_card): Remove app_ctx_marked_for_release handling. + (update_reader_status_file): Don't set SLOT here, so that it can be + released the APP by application_notify_card_reset in + update_card_removed. + +2016-07-19 Justus Winter + + agent: Add known keys to sshcontrol. + * agent/command-ssh.c (ssh_identity_register): Add a key to sshcontrol + even if it is already in the private key store. + * tests/openpgp/ssh.scm: Test this. + + tests: Add test for ssh support. + * tests/gpgscm/tests.scm (path-expand): New function. + * tests/openpgp/Makefile.am (TESTS): Add new test. + (sample_keys): Add new keys. + (CLEANFILES): Clean ssh socket and control file. + * tests/openpgp/fake-pinentry.c (main): Add a default passphrase. + * tests/openpgp/gpg-agent.conf.tmpl: Enable ssh support. + * tests/openpgp/samplekeys/ssh-dsa.key: New file. + * tests/openpgp/samplekeys/ssh-ecdsa.key: Likewise. + * tests/openpgp/samplekeys/ssh-ed25519.key: Likewise. + * tests/openpgp/samplekeys/ssh-rsa.key: Likewise. + * tests/openpgp/ssh.scm: Likewise. + +2016-07-19 NIIBE Yutaka + + scd: Fix race conditions for release_application. + * scd/command.c (do_reset, cmd_restart): Reset app_ctx before calling + release_application. + +2016-07-18 Justus Winter + + agent: Fix passphrase cache lookups. + CACHE_MODE_ANY is supposed to match any cache mode except + CACHE_MODE_IGNORE, but the code used '==' to compare cache modes. + + * agent/cache.c (cache_mode_equal): New function. + (agent_set_cache): Use the new function to compare cache modes. + (agent_get_cache): Likewise. + * tests/openpgp/Makefile.am (TESTS): Add new test. + * tests/openpgp/issue2015.scm: New file. + +2016-07-15 Justus Winter + + build: Always build gpgtar. + We use gpgtar to unpack test data, hence we always build it. If the + user opts out, we simply don't install it. + + * configure.ac: Add comment. + * tests/migrations/Makefile.am (required_pgms): Make sure gpgtar is + built. + * tools/Makefile.am: Always build gpgtar, but do not install it if the + user used '--disable-gpgtar'. + +2016-07-15 Werner Koch + + wks: Publish as binary file. + * tools/gpg-wks-server.c (copy_key_as_binary): New. + (check_and_publish): Use new function instead of rename. + +2016-07-15 Justus Winter + + gpgscm: Fix linking. + * tests/gpgscm/Makefile.am: Add -lintl. + + g10: Fix building without trust models. + * g10/pkclist.c (write_trust_status): Fall back to the previous + behavior. + + tests: Check for gpgtar. + * tests/migrations/extended-pkf.scm: Skip test if gpgtar is not built. + * tests/migrations/from-classic.scm: Likewise. + * tests/openpgp/gpgtar.scm: Fix check for gpgtar. + +2016-07-14 Werner Koch + + Release 2.1.14. + + po: Update the German translation. + +2016-07-14 Damien Goutte-Gattat + + dirmngr: fix handling of HTTP redirections. + * dirmngr/ks-engine-http.c (ks_http_fetch): Reinitialize HTTP session + when following a HTTP redirection. + +2016-07-14 Werner Koch + + gpg: Remove options --print-dane-records and --print-pka-records. + * g10/gpg.c (main): Remove options but print a dedicated warning. + * g10/options.h (struct opt): Remove fields 'print_dane_records' and + 'print_pka_records'. + * g10/keylist.c (list_keyblock): Do not call list_keyblock_pka. + (list_keyblock_pka): Remove. + +2016-07-14 Åka Sikrom + + po: Complete update of the Norwegian translation. + +2016-07-14 Yuri Chornoivan + + Update Ukrainian translation. + +2016-07-14 Ineiev + + Update Russian translation. + +2016-07-14 Werner Koch + + gpg: Fix regression since 2.1 in --search-key with a fingerprint. + * dirmngr/ks-engine-hkp.c (ks_hkp_search): Prefix fingerprint with 0x. + + gpgscm: Use kludge to avoid improper use of ffi_schemify_name. + * tests/gpgscm/ffi.c (ffi_schemify_name): Use xstrdup instead of + strdup for now. + + build: Require latest released libraries. + * agent/protect.c (OCB_MODE_SUPPORTED): Remove macro. + (do_encryption): Always support OCB. + (do_decryption): Ditto. + (agent_unprotect): Ditto. + * dirmngr/server.c (is_tor_running): Unconditionally build this. + +2016-07-13 Werner Koch + + build: Update config.{guess,sub} to {2016-05-15,2016-06-20}. + * build-aux/config.guess: Update. + * build-aux/config.sub: Update. + + gpg: Fix regression due to the new --mimemode options. + * g10/gpg.c (opts): Re-add oTextmodeShort. + +2016-07-13 Daiki Ueno + + gpg: Make --try-all-secrets work for hidden recipients. + * g10/getkey.c (enum_secret_keys): Really enumerate all secret + keys if --try-all-secrets is specified. + +2016-07-13 Werner Koch + + gpg: Do not print a the short keyid if the high word is zero. + * g10/keyid.c (format_keyid): Always returh long keyid ifor KF_LONG. + + gpg: New option --mimemode. + * g10/gpg.c (oMimemode): New. + (opts): Add --mimemode. + (main): Use --mimemode only in rfc4880bis compliance mode. + * g10/options.h (struct opt): Add field "mimemode". + * g10/build-packet.c (do_plaintext): Allow for mode 'm'. + * g10/encrypt.c (encrypt_simple, encrypt_crypt): Use 'm' if requested. + * g10/plaintext.c (handle_plaintext): Handle 'm' mode. + * g10/sign.c (write_plaintext_packet): Handle 'm' mode. + (sign_file, sign_symencrypt_file): Use 'm' if requested. + + wks: Use correct key for the confirmation. + * tools/gpg-wks-client.c (send_confirmation_response): Actually + encrypt to the recipient. + + wks: New server command --list-domains. + * tools/gpg-wks-server.c (aListDomains): New. + (opts): Add --list-domains. + (parse_arguments): Implement. + (main): Ditto. Use only one final diagnostic message. + (command_list_domains): New. + (check_and_publish): Remove directory creation. + (get_domain_list): New. + (expire_pending_confirmations): Rewrite using a list of directories. + (command_cron): Get domain list and pass to + expire_pending_confirmations. + +2016-07-13 NIIBE Yutaka + + agent: Fix envvars for UPDATESTARTUPTTY. + agent/command.c (cmd_updatestartuptty): Use session_env_list_stdenvnames + to get the list. + +2016-07-12 Werner Koch + + g13: Fix memleak. + * g13/g13tuple.c (create_tupledesc): Init refcount to 1. + + wks: Add --cron command to gpg-wks-server. + * tools/gpg-wks-server.c (PENDING_TTL): New. + (expire_one_domain, expire_pending_confirmations): New. + (command_cron): New. + (main): Implement --cron. + + wks: Try to send an encrypted confirmation back. + * tools/gpg-wks-client.c (encrypt_response_status_cb): New. + (encrypt_response): New. + (send_confirmation_response): Encrypt the response. + + * tools/gpg-wks-server.c (send_confirmation_request): Use freeing of + BODY and BODYENC. + + wks: Also create DANE record. + * tools/gpg-wks-server.c (copy_key_as_dane): New. + (check_and_publish): Also publish as DANE record. + + gpg: Extend import-option import-export to print PKA or DANE. + * g10/export.c (do_export_stream): Move PKA and DANE printing helper + code to ... + (print_pka_or_dane_records): this fucntion. + (write_keyblock_to_output): Add arg OPTIOSN and call + print_pka_or_dane_records if requested. + + gpg: Move a function from import.c to export.c. + * g10/import.c (write_keyblock_to_output): Move to ... + * g10/export.c (write_keyblock_to_output): here. Add arg WITH_ARMOR. + Also make sure never to export ring trust packets. + +2016-07-11 Werner Koch + + gpgconf: Enhance --list-dirs. + * tools/gpgconf.c (main) : Factor code out to ... + (list_dirs): new. Rewrite to use a table. Allow selection of a + items. Add "agent-ssh-socket". + +2016-07-09 NIIBE Yutaka + + gpgv: Tweak default options for extra security. + * g10/gpgv.c (main): Set opt.no_sig _cache, so that it doesn't depend on + cached status. Similarly, set opt.flags.require_cross_cert for backsig + validation for subkey signature. + +2016-07-07 Werner Koch + + gpg: Add export options "export-pka" and "export-dane". + * g10/options.h (EXPORT_PKA_FORMAT): New. + * g10/keylist.c (list_keyblock_pka): Do not use DANE flag. + * g10/export.c: Include zb32.h. + (parse_export_options): Add options "export-pka" and "export-dane". + (do_export): Do not armor if either of these option is set. + (print_pka_or_dane_records): New. + (do_export_stream): Implement new options. + + gpg: Split a too large export function. + * g10/export.c (do_export_stream): Factor some code out to ... + (do_export_one_keyblock): new. + +2016-07-07 Justus Winter + + gpgscm: Capture output of spawned processes. + * tests/gpgscm/tests.scm (call-check): Capture stdout and stderr, and + return stdout if the child exited successfully, or include stderr in + the error. + * tests/openpgp/version.scm: Demonstrate this by checking the stdout. + +2016-07-06 Werner Koch + + doc: Escape file names in generated macros. + * doc/mkdefsinc.c (print_filename): New. + (main): Use it here. + + wks: Let the server take the encrytion key from the file. + * tools/gpg-wks-server.c (encrypt_stream): Change arg 'fingerprint' to + 'keyfile'. + (store_key_as_pending): Add arg 'r_fname' to make of the keyfile. + (send_confirmation_request): Add arg 'keyfile'. + (process_new_key): Pass on the name of the keyfile. + + gpg: New options --recipient-file and --hidden-recipient-file. + * g10/gpg.c (oRecipientFile, oHiddenRecipientFile): New. + (opts): Add options --recipient-file and --hidden-recipient-file. + (main): Implement them. Also remove duplicate code from similar + options. + * g10/keydb.h (PK_LIST_FROM_FILE): New. + (PK_LIST_SHIFT): Bump up. + * g10/pkclist.c (expand_group): Take care of PK_LIST_FROM_FILE. + (find_and_check_key): Add and implement arg FROM_FILE. + (build_pk_list): Pass new value for new arg. + * g10/getkey.c (get_pubkey_fromfile): New. + * g10/gpgv.c (read_key_from_file): New stub. + * g10/test-stubs.c (read_key_from_file): New stub. + * g10/server.c (cmd_recipient): Add flag --file. + * g10/import.c (read_key_from_file): New. + + * tests/openpgp/defs.scm (key-file1): New. + (key-file2): New. + * tests/openpgp/setup.scm: Add their private keys and import the + key-file1. + * tests/openpgp/encrypt.scm: Add new test. + + gpg: New option --no-keyring. + * g10/gpg.c (oNoKeyring): New. + (opts): Add "--no-keyring". + (main): Do not register any keyring if the option is used. + + gpg: Document use of node flags in import.c and remove unused args. + * g10/import.c (NODE_GOOD_SELFSIG): New. Use instead of 1. + (NODE_BAD_SELFSIG): New. Use instead of 2. + (NODE_DELETION_MARK): New. Use instead of 4. + (NODE_FLAG_A): New. Use to mark new nodes in merge_blocks. + (chk_self_sigs): Remove unused args FNAME and PK. + (import_one): Adjust call. Simplify error return because + chk_self_sigs does not return an error code. + (append_uid, append_key, merge_sigs, merge_keysigs): Remove unsued + args FNAME and KEYID. + (merge_blocks, import_one, import_secret_one) + (import_revoke_cert): Remove unused arg FNAME. + + gpg: Get rid of an unused arg in a function in getkey.c. + * g10/getkey.c (pk_from_block): Remove unused arg CTX. Change all + callers. + + gpg: Change calling convention for a function in getkey.c. + * g10/getkey.c (merge_selfsigs): Remove arg CTX. Add args REQ_USAGE + and WANT_EXACT. + (finish_lookup): Adjust caller. Set LOOKUP_NOT_SELECTED here... + (lookup): and not here. + +2016-07-05 Werner Koch + + gpg: Fix possible out-of-bounds read in is_armored. + * g10/armor.c (check_input): Call is_armored only if LEN >= 2. + (unarmor_pump): Use a 2 byte buffer for is_armored. + +2016-07-05 Justus Winter + + tests: Honor environment variable 'TMP'. + This fixes problems with long socket names, e.g. when doing distcheck. + + * tests/gpgscm/tests.scm (path-join): New function. + (with-temporary-working-directory): Honor 'TMP'. + (make-temporary-file): Likewise. + * tests/migrations/Makefile.am (TMP): Default to '/tmp'. + (TESTS_ENVIRONMENT): Set 'TMP'. + * tests/openpgp/Makefile.am (TMP): Default to '/tmp'. + (TESTS_ENVIRONMENT): Set 'TMP'. + + gpgscm: Improve robustness and compatibility. + * tests/gpgscm/ffi.c (do_getenv): Avoid gccism. + (do_mkdtemp): Handle errors. + + tests/migrations: Fix distcheck. + * tests/migrations/Makefile.am (TESTS): Rename test. + (TEST_FILES): Update list. + (EXTRA_DIST): Add common.scm. + * tests/migrations/common.scm (GPGTAR): New variable. + (dearmor): Rename and untar archive. + * tests/migrations/extended-private-key-format.scm: Rename. + (setup): Update. + * tests/migrations/extended-pkf.tar.asc: New file. + * tests/migrations/extended-private-key-format.gpghome: Delete. + * tests/migrations/from-classic.gpghome: Likewise. + * tests/migrations/from-classic.scm (setup): Update. + * tests/migrations/from-classic.tar.asc: New file. + + tools/gpgtar: Provide --create and --extract. + * tools/gpgtar.c (cmd_and_opt_values): New values. + (opts): New actions. + (parse_arguments): Handle new actions. + * tests/openpgp/gpgtar.scm: Test new interface. + + g10: Fix out-of-bounds read. + * g10/armor.c (use_armor_filter): We need two bytes for 'is_armored'. + +2016-07-04 Werner Koch + + wks: Add command --read to gpg-wks-client. + * tools/gpg-wks-client.c (aRead): New. + (opts): Add command "--read". + (main): Implement that. + + tests: Add a gettime test for sizeof (time_t) > 4. + * common/t-gettime.c (test_isotime2epoch): Add 4 more tests. + +2016-07-03 Werner Koch + + gpg: Avoid spurious failures on keyblocks with no or only deleted nodes. + * g10/import.c (write_keyblock_to_output): Clear ERR on success. + + wks: Let the client only export the requested UID. + * tools/gpg-wks-client.c (get_key): Export only the requested uid. + + tools: Call sendmail directly from the wks tools. + * tools/send-mail.c, tools/send-mail.h: New. + * tools/wks-util.c: New. + * tools/Makefile.am (gpg_wks_server_SOURCES): Add them. + (gpg_wks_client_SOURCES): Ditto. + * tools/gpg-wks.h (opt): Add fields use_sendmail and output. + * tools/gpg-wks-client.c: Add options --send and --output. Rename + command --send to --create. + (command_send, send_confirmation_response): Output via wks_send_mime. + * tools/gpg-wks-server.c: Add options --send and --output. + (send_confirmation_request): Output via wks_send_mime. + (check_and_publish): Add hack for name-value bug. + +2016-07-02 Werner Koch + + tools: Add options to gpg-wks-server. + * tools/gpg-wks.h (opt): Add 'default_from' and 'extra_headers'. + * tools/gpg-wks-server.c (oFrom, oHeader): New. + (parse_arguments): Set them and check args. + (get_submission_address): New. + (send_confirmation_request): Set correct From address. Add extra + headers. + (process_new_key): Return an error code. + + tools: Extend mime-maker.c:mime_maker_add_header. + * tools/mime-maker.c (add_header): Check header name and allow + name-value syntax. + (mime_maker_add_header): Add mode for a syntax check. + + doc: Describe filter expressions. + * doc/gpg.texi: Remove some superfluous .E. + (FILTER EXPRESSIONS): New. + + yat2m: Fix table formatting. + * doc/yat2m.c (proc_texi_cmd): Use .TQ for @itemx. Print a .P at the + end of a level 0 table. + +2016-07-01 Werner Koch + + gpg: New option --export-filter. + * g10/gpg.c (oExportFilter): New. + (opts): Add --export-filter. + (main): Handle option. + * g10/export.c: Include recsel.h, init.h, and mbox-util.h. + (export_keep_uid): New global var. + (cleanup_export_globals): New. + (parse_and_set_export_filter): New. + (filter_getval): New. + (apply_keep_uid_filter): New. + (do_export_stream): Apply filter if set. + + gpg: New option --import-filter. + * g10/gpg.c (oImportFilter): New. + (opts): Add --import-filter. + (main): Handle option. + * g10/import.c: Include recsel.h, init.h, and mbox-util.h. + (import_keep_uid): New global var. + (cleanup_import_globals): New. + (parse_and_set_import_filter): New. + (filter_getval): New. + (apply_keep_uid_filter): New. + (import_one): Apply filter if set. + + gpg: Allow to cache the mbox in a user id struct. + * g10/packet.h (PKT_user_id): Add field 'mbox'. + * g10/free-packet.c (free_user_id): Free that. + + gpg: Make sure a user ID packet has always a terminating Nul in memory. + * g10/keygen.c (write_uid): Avoid overflow. + + common: Add function to select records etc. + * common/recsel.c, common/recsel.h: New. + * common/t-recsel.c: New. + + common: Smart up register_mem_cleanup_func. + * common/init.c (register_mem_cleanup_func): Avoid double registration. + +2016-07-01 Justus Winter + + common: Annotate semi-static allocation. + * common/argparse.c (optfile_parse): Allow string arguments to leak. + + g10: Fix memory leak. + * g10/keyserver.c (parse_keyserver_uri): Free URI. + + tools/gpgtar: Annotate semi-static allocation. + * tools/gpgtar.c (shell_parse_argv): Annotate argument vector as + leaked. + + g10: Fix memory leak. + * g10/import.c (transfer_secret_keys): Release curve from the previous + iteration. + + g10: Fix build with disabled kbnode cache. + * g10/kbnode.c (release_unused_nodes): Fix build with disabled kbnode + cache. + + g10: Fix memory leak. + * g10/trustdb.c (tdb_get_validity_core): Fix kbnode leak. + + g10: Fix memory leak. + * g10/keygen.c (keygen_set_std_prefs): Fix memory leak. + + Fix trivial memory leaks in tests. + * dirmngr/t-ldap-parse-uri.c (check_ldap_escape_filter): Free result. + * g10/t-stutter.c (main): Free file name. + +2016-06-30 Justus Winter + + tools: Fix trivial memory leak. + * tools/gpg-connect-agent.c (main): Fix trivial memory leak. + + g10: Fix memory leak. + * g10/export.c (do_export_stream): Free secret parameters. + + g10: Fix memory leak. + * g10/keygen.c (read_parameter_file): Free 'line'. + + g10: Fix memory leak. + * g10/sign.c (mk_notation_policy_etc): Free 'mbox'. + + common: Fix memory leak. + * g10/textfilter.c (copy_clearsig_text): Free buffer. + + common: Fix memory leak. + * common/iobuf.c (iobuf_set_partial_body_length_mode): Only create + context if necessary. + + common: Fix memory leak. + * common/simple-pwquery.c (agent_open): Free socket path. + + g10: Fix keybox-related memory leaks. + * g10/keydb.c (keydb_release): Clear keyblock cache. + (keydb_get_keyblock): Revert previous change. + * kbx/keybox-blob.c (create_blob_finish): Free previous buffer, free + fixups after applying them. + (_keybox_release_blob): Free buffer. Currently, the buffer has been + extracted before the keybox is released, but this is the right thing + to do here. + + g10: Fix memory leak. + * g10/compress.c (release_context): Free buffers. + + g10: Fix memory leak. + * g10/sign.c (write_plaintext_packet): Free packet. + + g10: Fix memory leak. + * g10/mainproc.c (release_list): Do not exit early if list is NULL, + there are other resources that must be released. + + gpgscm: Fix reallocating string ports. + * tests/gpgscm/scheme.c (realloc_port_string): Use memcpy because + Scheme strings may contain 0s. + + gpgscm: Free memory backing string ports. + * tests/gpgscm/scheme.c (finalize_cell): Free memory backing string + ports. + + gpgscm: Use the allocator from libgcrypt. + * tests/gpgscm/main.c (main): Use the allocator from libgcrypt. + + w32: Fix build. + * g10/keyedit.c (keyedit_quick_revuid): Fix call to + 'check_trustdb_stale'. + +2016-06-30 Daniel Kahn Gillmor + + g10: Implement gpg --quick-revuid. + * g10/revoke.c (get_default_uid_revocation_reason): New. + * g10/keyedit.c (menu_revuid): Break out creation of uid revocation + into new function core_revuid. + * g10/keyedit.c (keyedit_quick_revuid): New. Selects key and + uid, invokes core_revuid. + * g10/gpg.c (main): Handle --quick-revuid argument. + * doc/gpg.texi: Document --quick-revuid. + +2016-06-29 Werner Koch + + tools: Add gpg-wks-client and gpg-wks-server. + * configure.ac: Add option --enable-wks-tools + * tools/gpg-wks-client.c: New. + * tools/gpg-wks-server.c: New. + * tools/gpg-wks.h: new. + * tools/wks-receive.c: New. + * tools/call-dirmngr.c, tools/call-dirmngr.h: New. + + build: Improve GNUPG_BUILD_PROGRAM macro. + * acinclude.m4 (GNUPG_BUILD_PROGRAM): Allow for dash in options. + + tools: Add modules for MIME parsing and creating. + * tools/mime-maker.c: New. + * tools/mime-maker.h: New. + * tools/mime-parser.c: New. + * tools/mime-parser.h: New. + +2016-06-28 Justus Winter + + gpgscm: Fix memory leaks. + * tests/gpgscm/ffi-private.h (ffi_schemify_name): Fix prototype. + (ffi_define_function_name): Free schemified name. + (ffi_define_function): Likewise. + (ffi_define_constant): Likewise. + (ffi_define_variable_pointer): Likewise. + * tests/gpgscm/ffi.c (do_wait_processes): Free arrays. + (ffi_schemify_name): Fix type. + * tests/gpgscm/main.c (main): Free 'sc'. + + gpgscm: Free file names. + * tests/gpgscm/scheme.c (scheme_load_named_file): Free file name. + + gpgscm: Fix buffer overflow. + * tests/gpgscm/scheme.c (store_string): Avoid writing past allocated + buffer. + + g10: Fix memory leaks. + * g10/keydb.c (keydb_get_keyblock): Free 'sigstatus' and 'iobuf'. + * g10/t-keydb-get-keyblock.c: Fix trivial memory leaks. + * g10/t-keydb.c: Likewise. + + common: Fix memory leaks. + * common/ccparray.c (ccparray_put): Free old array. + * common/stringhelp.c (do_make_filename): Free 'home'. + * common/t-convert.c: Fix trivial memory leaks. + * common/t-iobuf.c: Likewise. + * common/t-mbox-util.c: Likewise. + * common/t-name-value.c: Likewise. + * common/t-stringhelp.c: Likewise. + * common/t-strlist.c: Likewise. + +2016-06-28 Werner Koch + + dirmngr: add option to retrieve extra WKS info. + * dirmngr/server.c (cmd_wkd_get): Add option --submission-address. + + gpg: Add hack to --quick-gen-key to create Curve25519 keys. + * g10/keygen.c (quick_generate_keypair): Add special algo string + "test-default". + + common: New function rfctimestamp. + * common/gettime.c (rfctimestamp): New. + + common: Add missing header file for clarity. + * common/zb32.c: Include zb32.h. + +2016-06-28 Justus Winter + + tools/gpgtar: Fix handling of '-'. + * tools/gpgtar-extract.c (gpgtar_extract): Use stdin if file is '-'. + * tools/gpgtar-list.c (gpgtar_list): Likewise. + + common: Close input stream. + * common/exechelp-posix.c (gnupg_spawn_process): Also close the input + stream in the child. + + common: Fix copying data from the spawned child. + Fixes intermittent gpgtar failures. + + * common/exectool.c (copy_buffer_do_copy): Initialize 'nwritten'. + (gnupg_exec_tool_stream): Loop until all data is copied. + +2016-06-28 NIIBE Yutaka + + g10: Fix --list-packets. + * g10/gpg.c (main): Call set_packet_list_mode after assignment of + opt.list_packets. + * g10/mainproc.c (do_proc_packets): Don't stop processing with + --list-packets as the comment says. + * g10/options.h (list_packets): Fix the comment. + * g10/parse-packet.c: Fix the condition for opt.list_packets. + +2016-06-25 Werner Koch + + build: Add aclocal macro from pkg-config. + * m4/pkg.m4: New. + + yat2m: Silence lint warnings and fix a printf format bug. + * doc/yat2m.c (ATTR_PRINTF, ATTR_NR_PRINTF, ATTR_MALLOC): New. + (die, err, inf, xmalloc, xcalloc): New prototypes with attributes. + (get_section_buffer): Take care of !N_SECTIONS. + (proc_texi_cmd): Cast precision format arg. + (proc_texi_buffer): Do not set IN_CMD when not used afterwards. + +2016-06-24 Werner Koch + + gpg: New import option "import-export". + * g10/import.c (parse_import_options): Add option "import-export". + (write_keyblock_to_output): New. + (import_one): Implement option. + +2016-06-23 Werner Koch + + gpg: New import option "import-show". + * g10/options.h (IMPORT_SHOW): New. + * g10/import.c (parse_import_options): Add "import-show". + (import_one): Implement that. + + gpg: Do not print the validity after key generation. + * g10/keylist.c (struct keylist_context): Add field NO_VALIDITY. + (list_keyblock_print): Take care of it. + (list_keyblock_direct): Add arg NO_VALIDITY. + * g10/keygen.c (do_generate_keypair): Merge keyblock and print w/o + validity. + + common: Fix possible small memory leak in b64dec.c. + * common/b64dec.c (b64dec_finish): Always release TITLE. + +2016-06-23 Justus Winter + + tests/openpgp: Fake the system time for the tofu test. + The keys in the tofu test are set to expire on 2016-09-17. Fake the + system time for this test. + + This commit includes changes to the old test as well, for those who + need to backport it. + + * tests/openpgp/gpg-agent.conf.tmpl: Drop trailing newlines. + * tests/openpgp/tofu.scm: Fake system time. + * tests/openpgp/tofu.test: Likewise. + + gpgscm: Handle exceptions in the transformation monad. + * tests/gpgscm/tests.scm (pipe:do): Raise errors. + (tr:spawn): Catch and return errors. + (tr:call-with-content): Likewise. + (tr:{open,write-to,pipe-do,assert-identity,assert-weak-identity}): + Adapt. + + tests/openpgp: Improve tests. + * tests/openpgp/multisig.scm: Simplify test. + * tests/openpgp/setup.scm (dearmor): Use pipe. + + gpgscm: Add types for special objects. + * tests/gpgscm/scheme.c (enum scheme_types): Add types for boolean, + nil, eof, and the sink object. + (type_to_string): Handle new types. + (scheme_init_custom_alloc): Give special objects a type. + + gpgscm: Fix Scheme initialization. + This potentially causes a crash if the garbage collector marks an eof + object. + + * tests/gpgscm/scheme.c (scheme_init_custom_alloc): Initialize + 'EOF_OBJ'. + +2016-06-23 Werner Koch + + common: Add dedicated private key functions to name-value.c. + * common/name-value.c (struct name_value_container): Add field + 'private_key_mode'. + (my_error): New. Use instead of gpg_error. + (nvc_new_private_key): New. + (nve_release): Add arg 'private_key_mode'. + (nvc_release): Call nve_release with private_key_mode flag. + (nvc_delete): Ditto. + (_nvc_add): Do no special case "Key:" in non-private_key_mode. + (nvc_get_private_key): Return error in non-private_key_mode. + (nvc_set_private_key): Ditto. + (nvc_parse): Factor all code out to ... + (do_nvc_parse): new. Add arg 'for_private_key'. + (nvc_parse_private_key): New. + * agent/findkey.c (write_extended_private_key): Replace nvc_parse by + nvc_parse_private_key. + (read_key_file): Ditto. + + * common/t-name-value.c (private_key_mode): New variable. + (my_nvc_new): New. Replace all callers. + (test_key_extraction): Take mode in account. + (run_tests): Ditto. + (run_modification_tests): Ditto. + (parse): Ditto. + (main): Add option --parse and rename --parse to --parse-key. + + common: Rename external symbols in name-value.c. + * common/name-value.c, common/name-value.h: Rename symbol prefixes + from "pkc_" to "nvc_" and from "pke_" to "nve_". Change all callers. + + common: Rename private-keys.c to name-value.c. + * common/private-keys.c: Rename to name-value.c. + * common/private-keys.h: Rename to name-value.h. Chage all users. + * common/t-private-keys.c: Rename to t-name-value.c. + * common/Makefile.am: Adjust accordingly. + + common: Add PGP armor decoding to b64dec. + * common/b64dec.c (decoder_states): Add new states. + (b64dec_proc): Handle PGP armored format. + +2016-06-23 NIIBE Yutaka + + g10: Fix regression of card-edit/fetch. + * g10/card-util.c (fetch_url): Call keyserver_fetch instead of + keyserver_import_fprint. + +2016-06-21 Justus Winter + + tests/migrations: Convert to Scheme and re-enable. + * configure.ac: Re-enable. + * tests/Makefile.am: Likewise. + * tests/migrations/Makefile.am (TESTS): Use Scheme tests. + * tests/migrations/common.scm: New file. + * tests/migrations/extended-private-key-format.scm: Likewise. + * tests/migrations/from-classic.scm: Likewise. + * tests/migrations/extended-private-key-format.test: Drop file. + * tests/migrations/from-classic.test: Drop file. + + gpgscm: Add more file handling functions. + * tests/gpgscm/ffi.c (do_glob): New function. + (ffi_init): Define new function. + * tests/gpgscm/tests.scm (basename-suffix): New function.x + + tests/openpgp: Port the remaining tests to Scheme. + * tests/openpgp/Makefile.am (TESTS): Add new tests. + * tests/openpgp/defs.scm (gpg-with-colons): New function. + (get-config): Use new function. + * tests/openpgp/export.scm: New file. + * tests/openpgp/tofu.scm: Likewise. + + gpgscm: Improve test framework. + * tests/gpgscm/lib.scm (echo): Move... + * tests/gpgscm/tests.scm (echo): ... here. + (info, error, skip): And use echo here. + (file-exists?): New function. + (tr:spawn): Check that source exists and if the sink has been created. + (tr:call-with-content): Hand in optional arguments. + + gpgscm: Use native string searching functions. + * tests/gpgscm/ffi-private.h: Handle character arguments. + * tests/gpgscm/ffi.c (do_string_index): New function. + (do_string_rindex): Likewise. + (do_string_contains): Likewise. + (ffi_init): Define new functions. + * tests/gpgscm/ffi.scm (ffi-define): New macro. + * tests/gpgscm/lib.scm (string-index): Use native function, + demonstrate behavior. + (string-rindex): Likewise. + (string-contains?): Likewise. + Demonstrate behavior of various other functions. + (read-all): Rework so that it can handle large files. + + gpgscm: Improve error reporting. + * tests/gpgscm/scheme.c (type_to_string): New function. + (Eval_Cycle): Include actual type in error message. + + gpgscm: Make memory allocation failures fatal. + * tests/gpgscm/scheme.c (Eval_Cycle): Exit if we run out of memory. + +2016-06-21 Werner Koch + + sm: Do not install cacert and other root certificates. + * doc/Makefile.am (dist_pkgdata_DATA): Move qualified.txt and + com-certs.pem to ... + (EXTRA_DIST): here. + +2016-06-20 Werner Koch + + gpg: Add experimental support for an issuer fpr. + * common/openpgpdefs.h (SIGSUBPKT_ISSUER_FPR): New. + * g10/build-packet.c (build_sig_subpkt_from_sig): Add arg PKSK and + insert the issuer fpr if needed. + * g10/sign.c (write_signature_packets): Pass signing key. + (make_keysig_packet): Ditto. + (update_keysig_packet): Ditto. + * g10/parse-packet.c (dump_sig_subpkt): Print issuer fpr. + (parse_one_sig_subpkt): Detect issuer fpr. + (can_handle_critical): Add issuer fpr. + * g10/mainproc.c (check_sig_and_print): Try to get key via fingerprint. + * g10/gpgv.c (keyserver_import_fprint): New stub. + * g10/test-stubs.c (keyserver_import_fprint): New stub. + + gpg: New option --rfc4880bis. + * g10/options.h (struct opt): Add field flags.rfc4880bis. + * g10/gpg.c (oRFC4880bis): new. + (opts): add --rfc4880bis. + (main): Implement that and print a warning. + +2016-06-19 Niibe Yutaka + + scd: Reset nonnull_nad to zero for VENDOR_GEMPC. + * (parse_ccid_descriptor): nonnull_nad = 0 for all GEMPC device. + +2016-06-17 Werner Koch + + tests: Make make distcheck work again. + * Makefile.am (tests): Remove test code which would led to doubling + calls to for e.g. "make distclean". + * tests/Makefile.am: Typo fixes. + * tests/gpgscm/Makefile.am (EXTRA_DIST): Fix name of License file. + Add repl.scm. + (check): Replace by check-local because check is a standard automake + target. + * tests/openpgp/Makefile.am (TESTS_ENVIRONMENT): Replace gmake0sim by + automake generated macro. + (EXTRA_DIST): Add defs.scm + + gpgscm: Silence compiler warnings. + * tests/gpgscm/scheme.c (mk_integer): Rename arg NUM to N. + (fill_vector): Ditto. + (mark): Rename var NUM to N. + (set_slot_in_env): Mark SC as unused. + (is_any): Mark P as unused. + + Add license notices for TinySCHEME. + * tests/gpgscm/COPYING: Rename to ... + * tests/gpgscm/LICENSE.TinySCHEME: this. + * AUTHORS: Add a note about TinySCHEME. + * build-aux/speedo/w32/pkg-copyright.txt: Add TinySCHEME notice. + +2016-06-17 Justus Winter + + tests/openpgp: Reimplement tests in Scheme. + * Makefile.am: Build the test infrastructure on Windows. + * tests/openpgp/Makefile.am (required_pgms): Add gpgscm. + (TESTS_ENVIRONMENT): Make sure gpgscm and the libraries are found. + (TESTS): Replace tests with the new Scheme implementations. + * tests/openpgp/4gb-packet.scm: New file. + * tests/openpgp/README: Likewise. + * tests/openpgp/armdetach.scm: Likewise. + * tests/openpgp/armdetachm.scm: Likewise. + * tests/openpgp/armencrypt.scm: Likewise. + * tests/openpgp/armencryptp.scm: Likewise. + * tests/openpgp/armor.scm: Likewise. + * tests/openpgp/armsignencrypt.scm: Likewise. + * tests/openpgp/armsigs.scm: Likewise. + * tests/openpgp/clearsig.scm: Likewise. + * tests/openpgp/conventional-mdc.scm: Likewise. + * tests/openpgp/conventional.scm: Likewise. + * tests/openpgp/decrypt-dsa.scm: Likewise. + * tests/openpgp/decrypt.scm: Likewise. + * tests/openpgp/default-key.scm: Likewise. + * tests/openpgp/defs.scm: Likewise. + * tests/openpgp/detach.scm: Likewise. + * tests/openpgp/detachm.scm: Likewise. + * tests/openpgp/ecc.scm: Likewise. + * tests/openpgp/encrypt-dsa.scm: Likewise. + * tests/openpgp/encrypt.scm: Likewise. + * tests/openpgp/encryptp.scm: Likewise. + * tests/openpgp/finish.scm: Likewise. + * tests/openpgp/genkey1024.scm: Likewise. + * tests/openpgp/gpgtar.scm: Likewise. + * tests/openpgp/import.scm: Likewise. + * tests/openpgp/mds.scm: Likewise. + * tests/openpgp/multisig.scm: Likewise. + * tests/openpgp/run-tests.scm: Likewise. + * tests/openpgp/seat.scm: Likewise. + * tests/openpgp/setup.scm: Likewise. + * tests/openpgp/signencrypt-dsa.scm: Likewise. + * tests/openpgp/signencrypt.scm: Likewise. + * tests/openpgp/sigs-dsa.scm: Likewise. + * tests/openpgp/sigs.scm: Likewise. + * tests/openpgp/use-exact-key.scm: Likewise. + * tests/openpgp/verify.scm: Likewise. + * tests/openpgp/version.scm: Likewise. + + tests/gpgscm: Add a TinySCHEME-based test driver. + * configure.ac: Add new component. + * tests/Makefile.am: Likewise. + * tests/gpgscm/Makefile.am: New file. + * tests/gpgscm/ffi-private.h: Likewise. + * tests/gpgscm/ffi.c: Likewise. + * tests/gpgscm/ffi.h: Likewise. + * tests/gpgscm/ffi.scm: Likewise. + * tests/gpgscm/lib.scm: Likewise. + * tests/gpgscm/main.c: Likewise. + * tests/gpgscm/private.h: Likewise. + * tests/gpgscm/repl.scm: Likewise. + * tests/gpgscm/scheme-config.h: Likewise. + * tests/gpgscm/t-child.c: Likewise. + * tests/gpgscm/t-child.scm: Likewise. + * tests/gpgscm/tests.scm: Likewise. + + tests/gpgscm: Foreign objects support for TinySCHEME. + * tests/gpgscm/scheme-private.h (struct cell): Add 'foreign_object'. + (is_foreign_object): New prototype. + (get_foreign_object_{vtable,data}): Likewise. + * tests/gpgscm/scheme.c (enum scheme_types): New type. + (is_foreign_object): New function. + (get_foreign_object_{vtable,data}): Likewise. + (mk_foreign_object): Likewise. + (finalize_cell): Free foreign objects. + (atom2str): Pretty-print foreign objects. + (vtbl): Add new functions. + * tests/gpgscm/scheme.h (struct foreign_object_vtable): New type. + (mk_foreign_object): New prototype. + (struct scheme_interface): Add new functions. + + Patch from Thomas Munro, + https://sourceforge.net/p/tinyscheme/patches/13/ + + tests/gpgscm: Dynamically allocate string buffer. + * tests/gpgscm/scheme-config.h (strbuff{,_size}): Make buffer dynamic. + * tests/gpgscm/scheme.c (expand_strbuff): New function. + (putcharacter): Adapt length test. + (readstrexp): Expand buffer if necessary. + (scheme_init_custom_alloc): Initialize buffer. + (scheme_deinit): Free buffer. + + Patch from Thomas Munro, + https://sourceforge.net/p/tinyscheme/patches/11/ + + tests/gpgscm: Make exception value available. + * tests/gpgscm/init.scm (throw): Hand exception value to the handler. + (catch): And bind it to *error*. + + tests/gpgscm: Add package macro. + * tests/gpgscm/init.scm: Add package macro from manual. + + tests/gpgscm: Expose function to open streams as Scheme ports. + * tests/gpgscm/scheme.c (vtbl): Add 'port_from_file' to the vtable. + * tests/gpgscm/scheme.h (struct scheme_interface): New field + 'mk_port_from_file'. + + tests/gpgscm: Nicer error message. + * tests/gpgscm/scheme.c (opexe_0): Include the value that we tried to + evaluate as function-like in the error message. + + tests/gpgscm: Fix error hook. + * tests/gpgscm/init.scm (*error-hook*): Fix error hook so that the + whole error message is displayed. + + tests/gpgscm: Handle unhandled enumeration values. + * tests/gpgscm/scheme.c (opexe_{3,4}): Handle unhandled enumeration + values in the opcode dispatching code. + + tests/gpgscm: Verbatim import of latest TinySCHEME. + Revision 110 from svn://svn.code.sf.net/p/tinyscheme/code/trunk + + * tests/gpgscm/COPYING: New file. + * tests/gpgscm/Manual.txt: Likewise. + * tests/gpgscm/init.scm: Likewise. + * tests/gpgscm/opdefines.h: Likewise. + * tests/gpgscm/scheme-private.h: Likewise. + * tests/gpgscm/scheme.c: Likewise. + * tests/gpgscm/scheme.h: Likewise. + +2016-06-17 Werner Koch + + scd: Make option --homedir work. + * scd/scdaemon.c (opts): Add --homedir. + +2016-06-16 Werner Koch + + Release 2.1.13. + +2016-06-16 Yuri Chornoivan + + po: Update Ukrainian translation. + +2016-06-16 Ineiev + + po: Update Russian translation. + +2016-06-16 Werner Koch + + po: Update German translation. + + Add missing files so that make distcheck works again, + * tests/openpgp/Makefile.am (CLEANFILES): Add created file + "passphrases". + * tools/Makefile.am (EXTRA_DIST): Add no-libgcrypt.c. + + tools: Fix typo in function name of symcryptrun. + * tools/symcryptrun.c (main): Fix typo. + +2016-06-15 Niibe Yutaka + + g10: Fix another race condition for trustdb access. + * g10/tdbio.c (create_version_record): Call create_hashtable to always + make hashtable, together with the version record. + (get_trusthashrec): Remove call to create_hashtable. + +2016-06-14 Werner Koch + + gpg: Print the subkey's curve and not the primary key curve. + * g10/keylist.c (list_keyblock_colon): Use PK2 for the subkey's curve. + + ldap: Improve info output for v3 fallback. + * dirmngr/dirmngr_ldap.c (fetch_ldap): Do not use log_debug in an + unprotected section. Replace log_debug by log_info in verbose mode. + +2016-06-14 Andre Heinecke + + dirmngr: Try ldap protocol V3 as fallback. + * dirmngr/dirmngr_ldap.c (fetch_ldap): Try V3 Protocol in case + default Protocol gives error. + + dirmngr: Print ldap error if bind fails. + * dirmngr/dirmngr_ldap.c (fetch_ldap): Use ldap_err2string on bind + return. + +2016-06-14 Werner Koch + + gpgsm: Allow ciphers AES192 and SERPENT256. + * sm/gpgsm.c (main): Add AES192 cipher. Allow SERPENT256. + + doc: Add files and envvars to a new index. + * doc/gnupg.texi: Define new index "ef". + (Environment Index): New. + + gpg: Avoid endless loop in a tofu error case. + * g10/tofu.c (get_trust): Do not jump to out. + + gpg: Split tofu's get_trust function into several smaller ones. + * g10/tofu.c (get_trust): Factor code out to ... + (format_conflict_msg_part1): new and to ... + (ask_about_binding): new. + +2016-06-13 Werner Koch + + gpg: Un-deprecate option --auto-key-retrieve. + * g10/gpg.c (main): Remove deprecation warning. + + gpg: New option --disable-signer-uid, create Signer's UID sub-packet. + * g10/gpg.c (oDisableSignerUID): New. + (opts): New option '--disable-signer-uid'. + (main): Set option. + * g10/options.h (opt): Add field flags.disable_signer_uid. + * g10/sign.c: Include mbox-util.h. + (mk_notation_policy_etc): Embed the signer's uid. + * g10/mainproc.c (check_sig_and_print): Do not use WKD for auto key + retrieval if --disable-signer-uid is used. + + gpg: Try Signer's User ID sub-packet with --auto-key-retrieve. + * g10/packet.h (PKT_signature): Add field 'signers_uid'. + * g10/parse-packet.c (parse_signature): Set this field. + * g10/free-packet.c (free_seckey_enc): Free field. + (copy_signature): Copy field. + * g10/mainproc.c (akl_has_wkd_method): New. + (check_sig_and_print): Extend NEWSIG status. If WKD is enabled try to + locate a missing key via the signature's Signer's User ID sub-packet. + Do this right before trying a keyserver lookup. + +2016-06-11 Werner Koch + + gpg: Remove C-99ism, re-indent, and simplify one function. + * g10/call-agent.c (struct keyinfo_data): Rename to + keyinfo_data_parm_s. + (agent_get_keyinfo): Replace C-99 style init. + (keyinfo_status_cb): Use new fucntion split_fields. + * g10/export.c (match_curve_skey_pk): Add missings returns error + cases. + (cleartext_secret_key_to_openpgp): Better clear PK->PKEY first. + + common: New function split_fields. + * common/stringhelp.c (split_fields): New. + * common/t-stringhelp.c: Include assert.h. + (test_split_fields): New. + (main): Call test. + +2016-06-11 Daniel Kahn Gillmor + + g10: Export cleartext keys as cleartext. + * g10/export.c (do_export_stream): If a key is stored by the agent in + cleartext, then try to export it as cleartext. + * tests/openpgp/export.test: For secret keys that are stored in + cleartext, test should try to export without pinentry interaction. + + g10: Allow receiving cleartext secret keys from agent. + * g10/export.c (match_curve_skey_pk): New function, testing whether an + OpenPGP public key and an S-expression use the same curve. + * g10/export.c (cleartext_secret_key_to_openpgp): New function, + filling in the secret key parameters of a PKT_public_key object from + a corresponding cleartext S-expression. + * g10/export.c, g10/main.h (receive_seckey_from_agent): Add cleartext + parameter, enabling retrieval of the secret key, unlocked. + * g10/export.c (do_export_stream): Send cleartext as 0, keeping current + behavior. + * g10/keygen.c (card_store_key_with_backup): Use cleartext=0 to ensure + that smartcard backups are all passphrase-locked. + + g10: Add openpgp_protected flag to agent secret key export functions. + * g10/call-agent.c, g10/call-agent.h (agent_export_key): Add + openpgp_protected flag. + * g10/export.c (receive_seckey_from_agent): Request openpgp_protected + secret keys from agent. + * agent/command.c (hlp_export_key): EXPORT_KEY help text: add a + brief description of the effect of --openpgp. + + g10: report whether key in agent is passphrase-protected or not. + * g10/call-agent.c, g10/call-agent.h (agent_get_keyinfo): add + r_cleartext parameter to report whether a key is stored without + passphrase protection. + * g10/gpgv.c, g10/test-stubs.c: augment dummy agent_get_keyinfo to + match new API. + * g10/export.c, g10/keyedit.c, g10/keygen.c, g10/keylist.c, + g10/sign.c: pass NULL to agent_get_keyinfo since we do not yet + need to know whether agent is passphrase-protected. + +2016-06-08 Werner Koch + + Explicitly restrict socket permissions. + * agent/gpg-agent.c (create_server_socket): Call chmod before listen. + * scd/scdaemon.c (create_server_socket): Ditto. + * dirmngr/dirmngr.c (main): Ditto. + + w32: Fix recent build regression. + * common/homedir.c (_gnupg_socketdir_internal) [W32]: Add definition + for NAME. + * g10/gpg.c (main) [W32]: Fix use og gnupg_homedir. + + * agent/gpg-agent.c (remove_socket): Remove unused var P. + * scd/scdaemon.c (cleanup): Ditto. + + gpgconf: New commands --create-socketdir and --remove-socketdir. + * tools/gpgconf.c: Include unistd.h. + (aCreateSocketDir, aRemoveSocketDir): New. + (opts): Add --create-socketdir and --remove-socketdir. + (main): Implement them. + + Implement /run/user/UID/gnupg based sockets. + * common/homedir.c: Include sys/stat.h and zb32.h. + (w32_portable_app, w32_bin_is_bin): Change type from int to byte. + (non_default_homedir): New. + (is_gnupg_default_homedir): New. + (default_homedir): Set non_default_homedir. + (gnupg_set_homedir): Set non_default_homedir and make + the_gnupg_homedir and absolute directory name. + (gnupg_homedir): Return an absolute directory name. + (_gnupg_socketdir_internal): New. + (gnupg_socketdir): Implement /run/user/ based sockets. + * tools/gpg-connect-agent.c (get_var_ext): Replace now obsolete + make_filename by xstrdup. + * tools/gpgconf.c (main): Sue gnupg_homedir for the "homedir:" output. + + gpgconf: Add option --homedir. + * tools/gpgconf.c (opts): Add --homedir. + (main): Set homedir. + + Do not use no-libgcrypt dummy for tools. + * tools/Makefile.am (gpgconf_SOURCES): Remove no-libgcrypt.c. + (gpgconf_LDADD): Add LIBGCRYPT_LIBS. + (gpg_connect_agent_LDADD): Ditto. + (gpgtar_LDADD): Ditto. + * dirmngr/Makefile.am (dirmngr_client_LDADD): Ditto. + (t_common_ldadd): Ditto. Remove no-libgcrypt.o. + + Do not try to remove the enclosing directory of sockets. + * agent/gpg-agent.c (remove_socket): Do not remove the enclosing + directory. + * scd/scdaemon.c (cleanup): Ditto. + +2016-06-07 Werner Koch + + common: New function gnupg_socketdir. + * common/homedir.c (gnupg_socketdir): New. + * agent/gpg-agent.c (create_socket_name): Use new function instead of + gnupg_homedir. + (check_own_socket): Ditto. + (check_for_running_agent): Ditto. + * agent/preset-passphrase.c (main): Ditto. + * common/asshelp.c (start_new_gpg_agent): Ditto. + * scd/scdaemon.c (create_socket_name): Ditto. + * tools/gpgconf.c (main): Ditto. + * tools/symcryptrun.c (main): Ditto. + + common: Remove homedir arg from start_new_{dirmngr,gpg_agent}. + * common/asshelp.c (start_new_gpg_agent): Remove arg 'homedir' in + favor of gnupg_homedir (). Change all callers. + (start_new_dirmngr): Ditto. + * common/get-passphrase.c (gnupg_prepare_get_passphrase): Remove arg + 'homedir'. + + Replace use of opt.homedir by accessor functions. + * common/homedir.c (the_gnupg_homedir): New var. + (gnupg_set_homedir): New. + (gnupg_homedir): New. + * g10/options.h (struct opt): Remove 'homedir' and replace all users + by the new accessor functions. + * g13/g13-common.h (struct opt): Ditto. + * scd/scdaemon.h (struct opt): Ditto. + * sm/gpgsm.h (struct opt): Ditto. + * dirmngr/dirmngr.h (struct opt): Ditto. + * agent/preset-passphrase.c (opt_homedir): Ditto. + * agent/protect-tool.c (opt_homedir): Ditto. + +2016-06-07 NIIBE Yutaka + + po: Update Japanese translation. + + gpg: Fix command line parsing of --quick-addkey and --quick-gen-key. + * g10/gpg.c (main): Compose a block by curly braces. + +2016-06-06 Werner Koch + + gpg: Use --keyid-format=none by default. + * g10/gpg.c (main): Init keyid_format to KF_NONE. + * g10/keyid.c (format_keyid): Ditto. + (keystrlen): Ditto. + + gpg: Add option --with-subkey-fingerprint. + * g10/gpg.c (oWithSubkeyFingerprint): New. + (opts): Add --with-subkey-fingerprint[s]. + (main): Set that option. + * g10/options.h (struct opt): Add 'with_subkey_fingerprint'. + * g10/keylist.c (list_keyblock_print): Print subkey fingerprint. + (print_fingerprint): Tweak printing to use compact format if + desirable. + + gpg: Implement --keyid-format=none. + * g10/gpg.c (main): Add option "none" to --keyid-format. + * g10/options.h (KF_NONE): New. + * g10/keyid.c (format_keyid): Implement that. + (keystr): Use format "long" is KF_NONE is in use. + (keystr_with_sub): Ditto. + * g10/keylist.c (list_keyblock_print): Adjust indentaion for KF_NONE. + Factor some code out to ... + (print_key_line): new. + (print_fingerprint): Add mode 20. + * g10/mainproc.c (list_node): Use print_key_line. Replace MAINKEY by + flags.primary in the PK. Fix putting a " revoked..." string into the + colons format. + * g10/pkclist.c (do_edit_ownertrust): Use print_key_line. This + slightly changes the putput format. + * g10/revoke.c (gen_standard_revoke): Use print_key_line. This may + also put "expires: " into the output. + +2016-06-04 Werner Koch + + w32: Require --enable-build-timestamp for the BUILD_HOSTNAME. + * configure.ac (BUILD_HOSTNAME): Set to "" bey default. + * build-aux/speedo.mk (speedo_pkg_gnupg_configure): Add + --enable-build-timestamp. + +2016-06-02 Werner Koch + + gpg: Add the fingerprint to KEY_CREATED for subkeys. + * g10/keygen.c (print_status_key_created): Make more robust by + allowing a NULL for PK. + (generate_subkeypair): Use print_status_key_created. + (generate_card_subkeypair): Ditto. + + gpg: Try to use the passphrase from the primary for --quick-addkey. + * agent/command.c (cmd_genkey): Add option --passwd-nonce. + (cmd_passwd): Return a PASSWD_NONCE in verify mode. + * g10/call-agent.c (agent_genkey): Add arg 'passwd_nonce_addr' and do + not send a RESET if given. + (agent_passwd): Add arg 'verify'. + * g10/keygen.c (common_gen): Add optional arg 'passwd_nonce_addr'. + (gen_elg, gen_dsa, gen_ecc, gen_rsa, do_create): Ditto. + (generate_subkeypair): Use sepeare hexgrip var for the to be created + for hexgrip feature. Verify primary key first. Make use of the + passwd nonce. Allow for a static passphrase. + + gpg: Extend the --quick-gen-key command. + * g10/keygen.c (quickgen_set_para): Add arg 'use'. + (quick_generate_keypair): Add args 'algostr', 'usagestr', and + 'expirestr'. Implement primary only key mode. + (parse_algo_usage_expire): Set NBITS for the default algo. + * g10/gpg.c (main): Extend --quick-gen-key command. + + gpg: Improve the new parse_subkey_algostr_usagestr fucntion. + * g10/keygen.c (parse_usagestr): Allow "cert". + (generate_subkeypair): Factor expire parsing out to ... + (parse_subkey_algostr_usagestr): here. Rename to ... + (parse_algo_usage_expire): this. Add arg 'for_subkey'. Set CERT for + primary key and check that it is not set for subkeys. + + gpg: New command --quick-addkey. + * g10/keygen.c (DEFAULT_STD_SUBKEYUSE): New. + (ask_keysize): Factor code out to ... + (get_keysize_range, fixup_keysize): new. + (parse_parameter_usage): Factor parsing out to ... + (parse_usagestr): new. Allow use of "encr" as alias for "encrypt". + (parse_subkey_algostr_usagestr): New. + (generate_subkeypair): Add new args. Implement unattended mode. + + * g10/keyedit.c (keyedit_quick_sign): Factor some code out to ... + (find_by_primary_fpr): new. + (keyedit_quick_addkey): New. + * g10/gpg.c (aQuickAddKey): New. + (opts): Add --quick-addkey. + (main): Implement. + + gpg: Do not abort on certain invalid packets. + * g10/build-packet.c (write_fake_data): Check for non-opaque data. + * g10/seskey.c (do_encode_md): Return NULL instead of abort. + + common: New function openpgp_is_curve_supported. + * common/openpgp-oid.c: Include openpgpdefs.h. + (oidtable): Add field pubkey_algo. + (openpgp_is_curve_supported): New. + +2016-06-01 NIIBE Yutaka + + g10: Allow User ID length >= 256. + * build-packet.c (do_user_id): Call write_header2 with HDRLEN not set. + +2016-05-31 Werner Koch + + gpg: New status code NOTATION_FLAGS. + * common/status.h (STATUS_NOTATION_FLAGS: New. + * g10/packet.h (struct notation): Add flags.human. + (notation_t): New typedef. + * g10/build-packet.c (sig_to_notation): Set flags.human. + * g10/keylist.c (show_notation): Write STATUS_NOTATION_FLAGS. + +2016-05-28 Werner Koch + + common: Add a status callback to gnupg_exec_tool_stream. + * common/exectool.h (exec_tool_status_cb_t): New. + * common/exectool.c: Include missing exectool.h. + (read_and_log_buffer_t): Replace array by pointer. + (gnupg_exec_tool_stream): Add args 'status_cb' and 'status_cb_value'. + Change all callers to pass NULL for them. Malloc buffer for + FDERRSTATE. + (read_and_log_stderr): Implement status_fd feature. + +2016-05-27 Werner Koch + + common: Allow a second input stream for gnupg_exec_tool_stream. + * common/exechelp-posix.c (do_exec): Add arg 'except' and pass to + close_all_fds. + (gnupg_spawn_process): Add arg 'except'. Change callers to pass NULL + for it. + * common/exechelp-w32.c (gnupg_spawn_process): Add dummy arg 'except'. + * common/exechelp-w32ce.c (gnupg_spawn_process): Ditto. + * common/exectool.c (copy_buffer_do_copy): Allow NULL for SINK. + (gnupg_exec_tool_stream): Add arg 'inextra'. Change callers to pass + NULL for it. Allow NULL for OUTPUT. + + common: Simplify the fd closing patch 512c56a. + * common/exechelp-posix.c (get_max_fds): Use /proc/self. + + common: Speedup closing fds before an exec. + * common/exechelp-posix.c [__linux__]: Include dirent.h. + (get_max_fds) [__linux__]: Return the actual used highest fd. + + tools: Improve debug output of rfc822parse. + * tools/rfc822parse.c (show_event): Add missing events. + + build: Remove obsolete tests for funopen and fopencookie. + * configure.ac (AC_CHECK_FUNCS): Remove tests for funopen. + + common: Extend gnupg_create_inbound_pipe et al. + * common/exechelp-posix.c (gnupg_create_inbound_pipe): Add args 'r_fp' + and 'nonblock'. + (gnupg_create_outbound_pipe): Ditto. + * common/exechelp-w32.c (gnupg_create_inbound_pipe): Add non yet + functional args 'r_fp' and 'nonblock'. + (gnupg_create_outbound_pipe): Ditto. + * common/exechelp-w32ce.c (gnupg_create_inbound_pipe): Ditto. + (gnupg_create_outbound_pipe): Ditto. + + common: Make use of default_errsource in exechelp. + * common/exechelp-posix.c (my_error_from_syserror, my_error): New. + Use them instead of gpg_error and gpg_error_from_syserror. + (create_pipe_and_estream): Remove arg ERRSOURCE and fix use of + OUTBOUND which has a wrong name. Adjust callers. + (gnupg_spawn_process): Remove arg ERRSOURCE and replace by use of + DEFAULT_ERRSOURCE. + * common/exechelp-w32.c (gnupg_spawn_process): Ditto. + * common/exechelp-w32ce.c (gnupg_spawn_process): Ditto. + * common/exectool.c (gnupg_exec_tool_stream): Do not pass + GPG_ERROR_FROM_SYSERROR. + * tools/gpgconf-comp.c (gc_component_check_options): Ditto. + (retrieve_options_from_program): Ditto. + + gpg: Keep current and total of PROGESS status lines small enough. + * g10/progress.c (progress_filter): Factor status wrote out to... + (write_status_progress): New. Scale values down. + +2016-05-27 NIIBE Yutaka + + configure: Detection of libusb on FreeBSD. + * configure.ac (LIBUSB_LIBS): Use LIBUSB_NAME for AC_CHECK_LIB. + +2016-05-25 Werner Koch + + build: Switch to new URL for swdb.lst. + +2016-05-24 Werner Koch + + gpgtar: Simplify code by using ccparray. + * tools/gpgtar-create.c (gpgtar_create): Use ccparray functions. + * tools/gpgtar-extract.c (gpgtar_extract): Ditto. + * tools/gpgtar-list.c (gpgtar_list): Ditto. + + common: Add simple dynamic array function. + * common/ccparray.c: New. + * common/ccparray.h: New. + * common/t-ccparray.c: New. + * common/Makefile.am (common_sources): Add files. + (module_tests): Add test file. + (t_ccparray_LDADD): New. + +2016-05-23 Justus Winter + + tests: Test the pinentry interactions when exporting keys. + * tests/openpgp/export.test: Test pinentry interactions. + + tests: Add support for a passphrase queue to fake pinentry. + * tests/openpgp/fake-pinentry.c (get_passphrase): New function. + (main): Add option --passphrasefile and read passphrases from it. + + tests: Add logging to fake pinentry. + * tests/openpgp/fake-pinentry.c (log_stream): New variable. + (reply): New function. + (spacep,skip_options,option_value): Copy from common. + (main): Parse arguments, add --logfile option, write logfile. + + tests: Add export test. + * tests/openpgp/Makefile.am (TESTS): Add new file. + * tests/openpgp/export.test: New file. + +2016-05-21 Werner Koch + + gpg: Speed up key listing in Tofu mode. + * g10/tofu.c (get_trust): Add arg PK. Uses this instead of a an extra + lookup of the public key by fingerrpint. + (tofu_register): Pass PK to get_trust. + (tofu_get_validity): Ditto. + + *g10/tofu.c (tofu_register): Remove unused FINGERPRINT_PP. + + gpg: Avoid name spaces clash with future sqlite versions. + * g10/sqlite.c: Rename to gpgsql.c. Change function prefixes to + gpgsql_. + * g10/sqlite.h: Rename to gpgsql.h. + * g10/tofu.c: Adjust for changes. + + gpg: Explicitly close a combined Tofu DB. + * g10/tofu.c (tofu_closedbs): Close combined DB. + + gpg: Store the Tofu meta handle for databases in CTRL. + * g10/gpg.h (struct tofu_dbs_s, tofu_dbs_t): New declarations. + (struct server_control_s): Add field tofu.dbs. + * g10/tofu.c (struct dbs): Rename to tofu_dbs_s. Replace all users by + by tofu_dbs_t. + (opendbs): Add arg CTRL. Cache the DBS in CTRL. + (closedbs): Rename to tofu_closedbs and make global. Add arg CTRL. + (tofu_register): Add arg CTRL. Change all callers. Do not call + closedbs. + (tofu_get_validity): Ditto. + (tofu_set_policy): Ditto. + (tofu_get_policy): Ditto. + (tofu_set_policy_by_keyid): Add arg CTRL. + * g10/gpg.c (gpg_deinit_default_ctrl): Call tofu_closedbs. + + gpg: Pass CTRL object down to the trust functions. + + gpg: Fix the TOFU_STATS_LONG status. + * g10/tofu.c (show_statistics): Print TOFU STATS with formatting + characters. + +2016-05-19 Werner Koch + + gpg: Print "[ never ]" instead of err for validity. + * g10/trust.c (uid_trust_string_fixed): Handle NEVER. + +2016-05-18 Werner Koch + + dirmngr: Adjust the WKD lookup to specs version -01. + * dirmngr/server.c (cmd_wkd_get): Remove second occurrence of the + domain part. + +2016-05-17 Werner Koch + + gpg: Emit new status line KEY_CONSIDERED. + * common/status.h (STATUS_KEY_CONSIDERED): New. + * g10/getkey.c: Include status.h. + (LOOKUP_NOT_SELECTED, LOOKUP_ALL_SUBKEYS_EXPIRED): New. + (finish_lookup): Add arg R_FLAGS. Count expired and revoked keys and + set flag. Check a requested usage before checking for expiraion or + revocation. + (print_status_key_considered): New. + (lookup): Print new status. + +2016-05-11 NIIBE Yutaka + + g10: Fix signature checking. + * g10/sig-check.c (check_signature_over_key_or_uid): Fix call to + walk_kbnode. + +2016-05-10 Werner Koch + + gpg: Allow unattended deletion of secret keys. + * agent/command.c (cmd_delete_key): Make the --force option depend on + --disallow-loopback-passphrase. + * g10/call-agent.c (agent_delete_key): Add arg FORCE. + * g10/delkey.c (do_delete_key): Pass opt.answer_yes to + agent_delete_key. + +2016-05-09 Werner Koch + + gpg: Fix buglet in the check_all_keysigs function. + * g10/keyedit.c (sig_comparison): Actually compare the pubkey + algorithms. + + gpg: Request a "save" after cmd "check" fixed something. + * g10/keyedit.c (keyedit_menu) : Set modified. + +2016-05-09 NIIBE Yutaka + + po: Update Japanese translation. + +2016-05-04 Werner Koch + + Release 2.1.12. + + speedo,w32: Remove the installation directory page. + * build-aux/speedo/w32/inst.nsi (MUI_PAGE_DIRECTORY): Remove. + + gpg: Fix const char pointer mismatch with gettext. + * g10/tofu.c (get_trust): Use const char *. + + speedo: Build sqlite with static-libgcc. + * build-aux/speedo/patches/sqlite.patch: New. + * Makefile.am (EXTRA_DIST): Add file. + + speedo: Also try patch files w/o version number. + * build-aux/speedo.mk (SPKG_template): Try such a patch file. + +2016-05-04 Andre Heinecke + + speedo,w32: Install sqlite. + * build-aux/speedo/w32/inst.nsi (-sqlite, -un.sqlite): New. + + speedo,w32: Fix uninstallation. + * build-aux/speedo/w32/inst.nsi (-un.gnupg): Delete distsigkey and + dirmngr-conf.skel + + speedo,w32: Install localisation. + * build-aux/speedo/w32/inst.nsi (-libgpg-error, GnuPG): Install l10n. + (-un.libgpg-error, -un.gnupg): Uninstall l10n files. + +2016-05-04 Werner Koch + + tests: Disable the migrations tests. + * tests/Makefile.am (SUBDIRS): Remove migrations. + * configure.ac (AC_CONFIG_FILES): Remove migrations Makefile. + +2016-05-04 Ineiev + + po: Update Russian translation. + +2016-05-04 Werner Koch + + po: Update German translation. + + Some minor string changes and fixed a printf format. + * g10/build-packet.c (notation_value_to_human_readable_string): Use + %zu for size_t. + + build: Update config.{guess,sub} to 2016-04-02 and 2016-03-30. + * build-aux/config.guess: Update. + * build-aux/config.sub: Update. + + agent: Make --allow-loopback-pinentry the default. + * agent/gpg-agent.c (oNoAllowLoopbackPinentry): New. + (opts): Add --no-allow-loopback-pinentry. Hide + description of --allow-loopback-pinentry. + (parse_rereadable_options): Set opt.allow_loopback_pinentry by + default. + (main): Replace allow-loopback-pinentry by no-allow-loopback-pinentry + in the gpgconf list. + * tools/gpgconf-comp.c (gc_options_gpg_agent): Ditto. + +2016-05-03 Werner Koch + + common: Print https URLs in help messages. + * common/argparse.c (strusage): Print https URLS. + + tests: Silence output of some tests. + * common/t-exechelp.c (print_open_fds): Silence non-verbose output. + (test_close_all_fds): Ditto. + * common/t-session-env.c (show_stdnames): Indent output. + * g10/test.c (TEST): Silence non-verbose okay output. + (exit_tests): Ditto. + * tools/gpg-zip.in (tar_verbose_opt): Add option --quiet. + * tests/openpgp/gpgtar.test (GPGZIP): Pass option --quiet. + * tests/openpgp/mds.test: Indent MD5 notice. + * tests/openpgp/version.test: Indent --version output. + + gpg: Emit status lines TOFU_STATS and TOFU_STATS_LONG. + * g10/tofu.c (NO_WARNING_THRESHOLD): Rename to BASIC_TRUST_THRESHOLD. + (FULL_TRUST_THRESHOLD): New. + (write_stats_status): New. + (show_statistics): Call new function. Print TOFU_STATS_LONG. + +2016-05-02 Werner Koch + + gpg: Extend TRUST_foo status lines with the trust model. + * g10/trustdb.h (TRUST_FLAG_TOFU_BASED): New. + * g10/trustdb.c (trust_model_string): Lowercase the strings. Add arg + "model" and change callers to call with OPT.TRUST_MODEL. + * g10/tofu.c (tofu_wot_trust_combine): Set TRUST_FLAG_TOFU_BASED. + * g10/pkclist.c (write_trust_status): New. + (check_signatures_trust): Call new function. + + gpg: Improve line wrapping for a tofu message. + * g10/tofu.c (time_ago_str): Mark non-breakable spaces. + (show_statistics): Remove marks. + + gpg: Re-format some tofu messages. + * common/status.h (STATUS_TOFU_USER, STATUS_TOFU_STATS) + (STATUS_TOFU_STATS_SHORT, STATUS_TOFU_STATS_LONG): New. + * g10/tofu.c (NO_WARNING_THRESHOLD): New. + (record_binding, tofu_register): Take care of --dry-run. + (show_statistics): Print STATUS_TOFU_USER. Reformat some messages. + Fix the ngettext/strcmp thing. Use log_string instead of log_info. + Use NO_WARNING_THRESHOLD constant. + (get_trust): Use format_text and print a compact fingerprint. + +2016-05-02 NIIBE Yutaka + + scd: More fix of error return path. + * scd/command.c (open_card): Return GPG_ERR_ENODEV on the failure of + apdu_connect. + +2016-04-29 Werner Koch + + common: Extend log_string to indent lines. + * common/logging.c (do_logv): Add indentation when called via + log_string. + + gpg: Factor some code code out of tofu.c. + * g10/tofu.c (string_to_long): New. + (string_to_ulong): New. + (get_single_unsigned_long_cb): Replace strtol/strtoul by new function. + (get_single_long_cb): Ditto. + (signature_stats_collect_cb): Ditto. + (get_policy): Ditto. + (show_statistics): Ditto. Uese es_free instead of free. + + gpg: Remove all assert.h and s/assert/log_assert/. + + common: Improve log_assert. + * common/logging.c (bug_at): Do not i18n the string. + (_log_assert): New. + * common/logging.h (log_assert): Use new function and pass line + information. + +2016-04-28 NIIBE Yutaka + + scd: Fix error return path. + * scd/ccid-driver.c (bulk_in): Remove EAGAIN handling. + Handle LIBUSB_ERROR_NO_DEVICE to return CCID_DRIVER_ERR_NO_READER. + +2016-04-27 NIIBE Yutaka + + scd: Fix memory leaks. + * scd/ccid-driver.c (scan_or_find_usb_device): Return on + LIBUSB_ERROR_NO_MEM. Free CONFIG before return except on error. + (scan_or_find_devices): Free device list. + +2016-04-27 Werner Koch + + gpg: Add experimental AKL method "wkd" and option --with-wkd-hash. + * g10/getkey.c (parse_auto_key_locate): Add method "wkd". + (get_pubkey_byname): Implement that method. Also rename a variable. + * g10/call-dirmngr.c (gpg_dirmngr_wkd_get): New. + * g10/keyserver.c (keyserver_import_wkd): New. + * g10/test-stubs.c (keyserver_import_wkd): Add stub. + * g10/gpgv.c (keyserver_import_wkd): Ditto. + * g10/options.h (opt): Add field 'with_wkd_hash'. + (AKL_WKD): New. + + * g10/gpg.c (oWithWKDHash): New. + (opts): Add option --with-wkd-hash. + (main): Set that option. + * g10/keylist.c (list_keyblock_print): Implement that option. + + dirmngr: Add experimental command WKD_GET. + * dirmngr/server.c (cmd_wkd_get): New. + (register_commands): Add command WKD_GET. + + dirmngr: Use system provided root CAs with KS_FETCH. + * dirmngr/ks-engine-http.c (ks_http_fetch): Use HTTP_FLAG_TRUST_SYS. + +2016-04-26 Werner Koch + + http: Allow to request system defined CAs for TLS. + * dirmngr/http.h (HTTP_FLAG_TRUST_DEF, HTTP_FLAG_TRUST_SYS): New. + * dirmngr/http.c (http_session_new): Add arg "flags". + * dirmngr/ks-engine-hkp.c (send_request): Use new flag + HTTP_FLAG_TRUST_DEF for the new arg of http_session_new. + * dirmngr/ks-engine-http.c (ks_http_fetch): Ditto. + * dirmngr/t-http.c (main): Ditto. + +2016-04-25 Werner Koch + + common: Minor fixes for the new private-keys.c. + * common/private-keys.c (my_error_from_syserror): New. Use it in + place of gpg_error_from_syserror. + (_pkc_add, pkc_lookup, pke_next_value): Use ascii_strcasecmp. + (pkc_parse): Use xtrystrdup and append_to_strlist_try as intended. + + (_pkc_add): Add braces around if-statement. + + common: Use new function to print a failure of xtrymalloc. + * common/miscellaneous.c (xoutofcore): New. + * common/strlist.c (append_to_strlist): Use instead of abort. + (append_to_strlist_try): Use xtrymalloc instead of xmalloc. + +2016-04-21 Justus Winter + + common: Add support for the new extended private key format. + * agent/findkey.c (write_extended_private_key): New function. + (agent_write_private_key): Detect if an existing file is in extended + format and update the key within if it is. + (read_key_file): Handle the new format. + * agent/keyformat.txt: Document the new format. + * common/Makefile.am: Add the new files. + * common/private-keys.c: New file. + * common/private-keys.h: Likewise. + * common/t-private-keys.c: Likewise. + * common/util.h (alphap, alnump): New macros. + * tests/migrations: Add test demonstrating that we can cope with the + new format. + + common: Add 'free_strlist_wipe' which wipes memory. + * common/strlist.c (free_strlist_wipe): New function. + * common/strlist.h (free_strlist_wipe): New prototype. + + common: Add 'append_to_strlist_try' which can fail. + * common/strlist.c (append_to_strlist): Use the new function. + (append_to_strlist_try): New function. + * common/strlist.h (append_to_strlist_try): New prototype. + + agent: Convert key format document to org. + * agent/keyformat.txt: Convert to org mode. + + tests: Make migration test more robust and silent. + * tests/migrations/from-classic.test: Fix in-tree build, silence test. + +2016-04-21 Werner Koch + + w32: Use --enable-gpg2-is-gpg by default. + * autogen.rc: Add option also for plain Windows. + + w32: Replace libiconv DLL by iconv feature of libgpg-error. + * configure.ac: Do nor require libiconv for W32. + * common/utf8conv.c [W32]: Do not incluce iconv.h. Request + libgpg-error iconv macros. + (jnlib_iconv): Use ICONV_CONST macro. + * build-aux/speedo/w32/inst.nsi [!WITH_GUI]: Do not install libiconv. + * build-aux/speedo.mk (speedo_spkgs) [!WITH_GUI]: Likewise. + +2016-04-20 Justus Winter + + agent: Sanitize permissions of the private key directory. + * agent/gpg-agent.c (create_private_keys_directory): Set permissions. + * common/sysutils.c (modestr_to_mode): New function. + (gnupg_mkdir): Use new function. + (gnupg_chmod): New function. + * common/sysutils.h (gnupg_chmod): New prototype. + * tests/migrations/from-classic.test: Test migration with existing + directory. + + tests: Test the migration from a classic GnuPG home directory. + * configure.ac: Add new directory. + * tests/Makefile.am (SUBDIRS): Likewise. + * tests/migrations/Makefile.am: New file. + * tests/migrations/from-classic.gpghome/pubring.gpg.asc: Likewise. + * tests/migrations/from-classic.gpghome/secring.gpg.asc: Likewise. + * tests/migrations/from-classic.gpghome/trustdb.gpg.asc: Likewise. + * tests/migrations/from-classic.test: Likewise. + +2016-04-20 Werner Koch + + speedo: Use swdb.lst to define the SQLite version. + * build-aux/speedo.mk: Change sqlite to use our mirror and the + swdb.lst file. + * build-aux/speedo/w32/inst.nsi: gpg is now build and installed as + gpg. + +2016-04-19 Werner Koch + + gpg: Improve UID selction of --quick-sign-key. + * g10/keyedit.c (keyedit_quick_sign): Improve UID selection and print + error for non-found userids. + + gpg: Avoid debug like output at start of --edit-key. + * g10/keyedit.c (check_all_keysigs): Print info only after something + has been modified. + +2016-04-15 Andre Heinecke + + dirmngr: Fix https never reported in general help. + * dirmngr/ks-engine-http.c (ks_hkp_help): Also print https + when supported and no uri provided. + + dirmngr: Fix https incorrectly reported in help. + * dirmngr/ks-engine-http.c (ks_hkp_help): Only print https if tls + is supported. + +2016-04-14 Werner Koch + + agent: Fix regression due to recent commit 4159567. + * agent/protect.c (do_encryption): Fix CBC hashing. + + agent: Allow gpg-protect-tool to handle openpgp-native protection. + * agent/protect-tool.c (read_and_unprotect): Add arg ctrl and pass to + agent_unprotect. + (main): Allocate a simple CTRL object and pass it to + read_and_unprotect. + (convert_from_openpgp_native): Remove stub. + (agent_key_available, agent_get_cache): New stubs. + (agent_askpin): New emulation for the one in call-pinentry.c. + (agent_write_private_key): New to dump key. + * agent/Makefile.am (gpg_protect_tool_SOURCES): Add cvt-openpgp.c + + tests: Set fake-pinentry's stdout and stdin to _IOLBF. + * tests/openpgp/fake-pinentry.c (main): Call setvbuf. Show passphrase + at startup. Increase buffer. + +2016-04-12 Werner Koch + + agent: Implement new protection mode openpgp-s2k3-ocb-aes. + * agent/protect.c (agent_protect): Add arg use_ocb. Change all caller + to pass -1 for default. + * agent/protect-tool.c: New option --debug-use-ocb. + (oDebugUseOCB): New. + (opt_debug_use_ocb): New. + (main): Set option. + (read_and_protect): Implement option. + + * agent/protect.c (OCB_MODE_SUPPORTED): New macro. + (PROT_DEFAULT_TO_OCB): New macro. + (do_encryption): Add args use_ocb, hashbegin, hashlen, timestamp_exp, + and timestamp_exp_len. Implement OCB. + (agent_protect): Change to support OCB. + (do_decryption): Add new args is_ocb, aadhole_begin, and aadhole_len. + Implement OCB. + (merge_lists): Allow NULL for sha1hash. + (agent_unprotect): Change to support OCB. + (agent_private_key_type): Remove debug output. + + indent: Help Emacs not to get confused by conditional compilation. + * agent/protect.c (calibrate_get_time) [W32]: Use separate function + calls for W32 and W32CE. + +2016-04-07 Justus Winter + + g10: Fix exporting secret keys of certain sizes. + * g10/build-packet.c (do_key): Do not use the header length specified + by the public key packet from the keyring, but let 'write_header2' + compute the required length. + +2016-04-06 Justus Winter + + Revert "g10: Support armored keyrings in gpgv." + This reverts commit abb352de51bc964c06007fce43ed6f6caea87c15. + +2016-04-05 Justus Winter + + dirmngr: Autodetect PEM format in dirmngr-client. + * dirmngr/dirmngr-client.c (init_asctobin): New function. + (main): Move the initialization code to the new function. + (read_pem_certificate): Initialize base64 table. + (read_certificate): Try to decode certificates given in files as PEM + first. + +2016-04-05 Werner Koch + + build: Fix for: Build gpgcompose only in maintainer mode. + * g10/Makefile.am (noinst_PROGRAMS): Always add module_tests. + + doc: Install gpg and gpgv man pages under the correct name. + * doc/mkdefsinc.c (main): Add double include guard. Set variable + gpgtwohack. Define macros gpgname and gpgvname. + * doc/gpg.texi: Remove macro definition for gpgname. Use Texinfo var + gpgtwohack to prepare the man pages. Use @gpgname everywhere. + * doc/gpgv.texi: Likewise. + * doc/Makefile.am (myman_pages): Remove gpg2.1 and gpgv2.1 but add + them depending on USE_GPG2_HACK. + + build: Build gpgcompose only in maintainer mode. + * g10/Makefile.am (noinst_PROGRAMS): Add gpgcompose only in maintainer + mode. + + gpg: Replace use of "gpg2" by GPG_NAME. + +2016-04-04 Werner Koch + + Now build "gpg" binary but install as "gpg2" + * configure.ac (USE_GPG2_HACK): New ac_define am_conditional. + * common/homedir.c (gnupg_module_name): Replace use of macro + NAME_OF_INSTALLED_GPG. + * g10/keygen.c (generate_keypair): Ditto. + * g10/Makefile.am (bin_PROGRAMS): Remove. + (noinst_PROGRAMS): Add gpg or gpg2 and gpgv or gpg2. + (gpg2_hack_list): New. + (use_gpg2_hack): New. + (gpg2_SOURCES): Rename to gpg_SOURCES. + (gpgv2_SOURCES): Rename to gpgv_SOURCES. + (gpg2_LDADD): Rename to gpg_LDADD. + (gpgv2_LDADD): Rename to gpgv_LDADD. + (gpg2_LDFLAGS): Rename to gpg_LDFLAGS. + (gpgv2_LDFLAGS): Rename to gpgv2_LDFLAGS. + (install-exec-hook): Remove WinCE specific rules and add new rules. + (uninstall-local): Uninstall gpg/gpg2 and gpgv/gpgv2. + * tests/openpgp/Makefile.am (required_pgms): s/gpg2/gpg/. + * tests/openpgp/defs.inc: Ditto. + * tests/openpgp/gpgtar.test: Ditto. + * tests/openpgp/mkdemodirs: Ditto. + * tests/openpgp/signdemokey: Ditto. + + * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Remove obsolete + --enable-mailto, add --enable-gpg2-is-gpg. + + tests: Add missing file. + * tests/openpgp/Makefile.am (TEST_FILES): Add plain-largeo.asc. + +2016-04-04 Justus Winter + + g10: Support armored keyrings in gpgv. + * doc/gpgv.texi: Document the feature. + * g10/Makefile.am (gpgv2_SOURCES): Add dearmor.c. + * g10/dearmor.c (dearmor_file): Add sink argument. + * g10/gpg.c (main): Adapt accordingly. + * g10/gpgv.c (make_temp_dir): New function. + (main): De-armor keyrings. + * g10/main.h (dearmor_file): Adapt prototype. + + tests: Fix default key test. + * tests/openpgp/default-key.test: Avoid using the option + '--trust-model' unconditionally. + +2016-04-01 Justus Winter + + build: Check for conflicting trust model options. + * configure.ac: Disable TOFU if configured without trust models, and + check for conflicting options. + + g10: Remove option --always-trust if compiled without trust models. + * g10/gpg.c (opts): Remove option --always-trust if compiled without + trust models. + +2016-03-31 Justus Winter + + speedo,w32: Build libsqlite3. + * build-aux/speedo.mk (speedo_spkgs): Add libsqlite3 on w32. + (libsqlite3_ver): New variable. + (speedo_pkg_libsqlite3_tar): Likewise. + + g10: Use gpg-error abstraction of sched_yield. + * g10/tofu.c (begin_transaction): Use 'gpgrt_yield'. + +2016-03-29 Werner Koch + + gpg: Fix NULL-segv for missing tofu DB. + * g10/tofu.c (opendb): Guard call to timeout function. + +2016-03-22 Werner Koch + + gpg: Improve message when asking for key capabilities. + * g10/keygen.c (ask_key_flags): Improve message. + + gpg: Remove the extra prompt for Curve25519. + * g10/keygen.c (MY_USE_ECDSADH): New macro local to ask_curve. + (ask_curve): Use a fixed table of curve names and reserve a slot for + Curve448. Simplify CurveNNNN/EdNNNN switching. + (ask_curve): Remove the Curve25519 is non-standard prompt. + +2016-03-19 Werner Koch + + gpg: Silence trustdb computation with --quiet. + * g10/trustdb.c (validate_keys): Do not print log_info stuff in quiet + mode. + +2016-03-17 Werner Koch + + sm: Always create a keybox header when creating a new keybox. + * sm/keydb.c (maybe_create_keybox): Create the header blob. + +2016-03-17 Neal H. Walfield + + doc: Improve documentation of --enable-large-rsa. + * doc/gpg.texi (--enable-large-rsa): Improve text. + +2016-03-17 NIIBE Yutaka + + agent: allow removal of the shadowed key. + * agent/findkey.c (agent_delete_key): Remove the key when asked. + +2016-03-16 NIIBE Yutaka + + g10: Add const qualifier. + * g10/gpgcompose.c (show_help): Those are strings not to be modified. + +2016-03-15 Werner Koch + + gpg: Do not rely on a certain evaluation order. + * g10/keyedit.c (print_and_check_one_sig): Call check_key_signature + before derefing IS_SELFSIG. + +2016-03-14 Werner Koch + + scd: Add manufacturer id 0x000a. + * g10/card-util.c (get_manufacturer): Add it. + +2016-03-10 Kevin J. McCarthy + + g10: Silence message if --quiet is given. + * g10/getkey.c (parse_def_secret_key): Silence message if --quiet is + given. + +2016-03-08 Neal H. Walfield + + gpg: Add a new test. + * g10/Makefile.am (EXTRA_DIST): Add t-stutter-data.asc. + (module_tests): Add t-stutter. + (t_stutter_SOURCES): New variable. + (t_stutter_LDADD): New variable. + +2016-03-07 Justus Winter + + sm: Implement pinentry loopback and reading passphrases from fd. + * doc/gpgsm.texi: Document '--pinentry-mode' and '--passphrase-fd'. + * sm/Makefile.am (gpgsm_SOURCES): Add new files + * sm/call-agent.c (struct default_inq_parm_s): New definition. + (start_agent): Pass in the pinentry mode. + (default_inq_cb): Handle 'PASSPHRASE' and 'NEW_PASSPHRASE' inquiries. + Adapt all call sites to the new callback cookie. + * sm/gpgsm.c (cmd_and_opt_values): Add new values. + (opts): Add new options. + (main): Handle new options. + * sm/gpgsm.h (struct opt): Add field 'pinentry_mode'. + * sm/passphrase.c: New file. + * sm/passphrase.h: Likewise. + + sm: Remove unused argument '--fixed-passphrase'. + * doc/gpgsm.texi: Drop description. + * sm/gpgsm.c (cmd_and_opt_values): Drop enum value. + (opts): Drop argument. + (main): Drop argument handling. + * sm/gpgsm.h (struct opt): Drop field 'fixed_passphrase'. + + kbx: Avoid undefined behavior. + * kbx/keybox-file.c (_keybox_read_blob2): Cast to unsigned int before + shifting. + +2016-03-07 NIIBE Yutaka + + scd: Bug fix for a device with multiple interfaces. + * scd/ccid-driver.c (scan_or_find_usb_device): Use IFC_NO when + accessing interface information. + +2016-03-04 Justus Winter + + build: Make libusb a hard requirement if the ccid driver is requested. + * configure.ac: Print an error message and die if the internal ccid + driver is requested but no suitable libusb is found. + + g10: Drop superfluous declaration. + * g10/main.h (disable_core_dumps): Drop declaration. + + g10: Guard code against errors. + * g10/keygen.c (do_generate_keypair): Check for errors, in which case + 'pri_psk' is NULL. + +2016-03-03 Justus Winter + + dirmngr: Add more missing CFLAGS. + * dirmngr/Makefile.am (t_ldap_parse_uri_CFLAGS): Add + 'GCRYPT_CFLAGS'. + (t_dns_stuff_CFLAGS): Likewise. + + tests/openpgp: Skip gpgtar test if it has not been built. + * tests/openpgp/gpgtar.test: Check if executable exists. + +2016-03-02 Neal H. Walfield + + gpg: Add new program gpgcompose. + * g10/packet.h: Include "util.h". + * g10/encrypt.c (encrypt_seskey): Don't mark as static. + * g10/gpgcompose.c: New file. + * g10/Makefile.am (noinst_PROGRAMS): Add gpgcompose. + (gpg2_SOURCES): Split everything but gpg.c into... + (gpg_sources): ... this new variable. + (gpgcompose_SOURCES): New variable. + (gpgcompose_LDADD): Likewise. + (gpgcompose_LDFLAGS): Likewise. + + gpg: More robustly detect valid non-armored OpenPGP messages. + * g10/armor.c (is_armored): More robustly detect valid non-armored + OpenPGP messages. + + common: Provide a function for mapping packet types to strings. + * common/openpgpdefs.h (pkttype_str): New function. + + gpg: Rename pop_filter to iobuf_pop_filter and export it. + * common/iobuf.c (pop_filter): Rename from this... + (iobuf_pop_filter): ... to this. Don't mark it as static. + + gpg: Split write_pubkey_enc_from_list. + * g10/encrypt.c (write_pubkey_enc_from_list): Split the body of this + function out into... + (write_pubkey_enc): ... this new function. + + gpg: Allow the caller to write the contents of a plaintext packet. + * g10/build-packet.c (do_plaintext): Change the semantics such that if + PT->BUF is NULL, it is the caller's responsibility to write the + content (and disable partial body length mode, if appropriate). + + gpg: Add a new function for creating binary notations. + * g10/build-packet.c (blob_to_notation): New function. + + gpg: Refactor the printing of binary notations. + * g10/build-packet.c (sig_to_notation): Break printing of binary + notations into... + (notation_value_to_human_readable_string): ... this new function. + Provide a small preview of the binary data substituting non-printable + characters with '?'. + +2016-03-02 Uldis Anšmits + + tests/openpgp: Make tests more portable. + * tests/openpgp/default-key.test: Avoid 'grep -q'. + * tests/openpgp/gpgtar.test: Avoid 'grep -qe' and 'diff -q'. + * tests/openpgp/use-exact-key.test: Avoid 'grep -q'. + +2016-03-02 Justus Winter + + common: Consolidate Assuan server argument handling. + * common/Makefile.am (common_sources): Add new files. + * common/server-help.c: New file. + * common/server-help.h: Likewise. + * agent/command.c: Drop argument handling primitives in favor of using + the consolidated ones. + * dirmngr/server.c: Likewise. + * g10/server.c: Likewise. + * g13/server.c: Likewise. + * scd/command.c: Likewise. + * sm/server.c: Likewise. + +2016-03-01 Justus Winter + + dirmngr: Add missing CFLAGS. + * dirmngr/Makefile.am (t_ldap_parse_uri_CFLAGS): Add + 'GPG_ERROR_CFLAGS'. + (t_dns_stuff_CFLAGS): Likewise. + + tools: Drop superfluous include. + * tools/gpgtar.c: Do not include unused 'npth.h'. + +2016-02-26 Werner Koch + + gpg: Prettify a 2 octet hex output. + * g10/sig-check.c (check_key_signature2): Wrap line and use %02x. + +2016-02-25 Neal H. Walfield + + gpg: Show debugging info if a sig with an unsupported sig class is used. + * g10/sig-check.c (check_key_signature2): If SIG->CLASS is + unsupported, show some debugging information. Don't use BUG to fail. + Just return GPG_ERR_BAD_SIGNATURE. + + gpg: More carefully encode a packet's length. + * g10/build-packet.c (write_header2): Make sure the length bits are + cleared. Fail if HDRLEN is set and the specified length can't be + encoded in the available space. + + gpg: Avoid directly twiddling bits. + * g10/build-packet.c (do_plaintext): Use ctb_new_format_p to check the + packet's format. + (write_header2): Likewise. + + gpg: Add some asserts. + * g10/build-packet.c (ctb_new_format_p): New function. + (ctb_pkttype): New function. + (do_user_id): Add some asserts. + (do_key): Likewise. + (do_symkey_enc): Likewise. + (do_pubkey_enc): Likewise. + (do_plaintext): Likewise. + (do_encrypted): Likewise. + (do_encrypted_mdc): Likewise. + (do_compressed): Likewise. + (do_signature): Likewise. + (do_signature): Likewise. + (write_header2): Likewise. + + gpg: Avoid an unnecessary copy. + * g10/build-packet.c (sig_to_notation): Avoid an unnecessary copy of + the data: the size of the packet is fixed. + +2016-02-23 Neal H. Walfield + + common: Reduce buffer size. + * common/iobuf.c (iobuf_copy): Change buffer size from 1 MB to 32 KB. + + common: Improve a function's documentation and comments. + * common/iobuf.c (iobuf_set_partial_body_length_mode): Fix + documentation and comment. Add an assert. + + common: Add log_assert. + * common/logging.h (log_assert): New macro. + + gpg: Use higher-level functions. + * g10/build-packet.c (do_symkey_enc): Use iobuf_write instead of + iobuf_put in a loop. Use iobuf_copy instead of iobuf_read and + iobuf_write in a loop. Move the memory wiping from here... + * common/iobuf.c (iobuf_copy): ... to here. + + common: Check for an error before reading. + * common/iobuf.c (iobuf_copy): If DEST has a pending error, don't + start copying. + + common: More accurately name function. + * common/iobuf.c (iobuf_set_partial_block_mode): Rename from this... + (iobuf_set_partial_body_length_mode): ... to this. Update callers. + +2016-02-23 Werner Koch + + g13: Add commands --suspend and --remove. + * g13/g13.c (aSuspend, aResume): New. + (opts): Add commands --suspend and --resume. + (main): Implement dummy command aUmount. Implement commands aResume + and aSuspend. + * g13/sh-cmd.c (cmd_suspend): New. + (cmd_resume): New. + (register_commands): Add commands RESUME and SUSPEND. + * g13/server.c (cmd_suspend): New. + (cmd_resume): New. + (register_commands): Add commands RESUME and SUSPEND. + * g13/be-dmcrypt.c (be_dmcrypt_suspend_container): New. + (be_dmcrypt_resume_container): New. + * g13/backend.c (be_suspend_container): New. + (be_resume_container): New. + * g13/suspend.c, g13/suspend.h: New. + * g13/mount.c (parse_header, read_keyblob_prefix, read_keyblob) + (decrypt_keyblob, g13_is_container): Move to ... + * g13/keyblob.c: new file. + (keyblob_read): Rename to g13_keyblob_read and make global. + (keyblob_decrypt): Rename to g13_keyblob_decrypt and make global. + * g13/sh-dmcrypt.c (check_blockdev): Add arg expect_busy. + (sh_dmcrypt_suspend_container): New. + (sh_dmcrypt_resume_container): New. + * g13/call-syshelp.c (call_syshelp_run_suspend): New. + (call_syshelp_run_resume): New. + + g13: Run mount after dmsetup. + * g13/g13-syshelp.c (main): Reject userids with a slash. + * g13/sh-dmcrypt.c (sh_dmcrypt_mount_container): Run mount if a + mountpoint is known. + +2016-02-23 Justus Winter + + tests/openpgp: Qualify executables with extension. + * tests/openpgp/Makefile.am (required_pgms): Qualify executables with + '$EXEEXT'. + + tests/openpgp: Reimplement 'pinentry.sh' in c. + * tests/openpgp/Makefile.am: Build new program. + * tests/openpgp/defs.inc: Use the new program. + * tests/openpgp/fake-pinentry.c: New file. + + tests/openpgp: Avoid dependency on source files. + * tests/openpgp/plain-largeo.asc: New file. + * tests/openpgp/version.test: Dearmor the new file instead of relying + on the source being present. + + tests/openpgp: Fix file removal. + * tests/openpgp/version.test: Fix file removal. + + common/exechelp: Provide a way to wait for multiple processes. + * common/exechelp-posix.c (gnupg_wait_process): Generalize to + 'gnupg_wait_processes'. + * common/exechelp-w32.c (gnupg_wait_process): Likewise. + * common/exechelp-w32ce.c (gnupg_wait_process): New function stub. + * common/exechelp.h (gnupg_wait_process): New prototype. + + common/exechelp: Add general pipe function. + * common/exechelp-posix.c (gnupg_create_pipe): New function. + * common/exechelp-w32.c (INHERIT_{READ,WRITE,BOTH}): New macros. + (create_inheritable_pipe): Generalize so that both ends can be + inherited. + (do_create_pipe): Rename argument accordingly. + (gnupg_create_{in,out}bound_pipe): Use new flags. + (gnupg_create_pipe): New function. + (gnupg_spawn_process): Use new flags. + * common/exechelp-w32ce.c (gnupg_create_pipe): New stub. + * common/exechelp.h (gnupg_create_pipe): New prototype. + + common/exechelp: Mute the Windows version. + * common/exechelp-w32.c (gnupg_wait_process): Do not print an error if + the exit code can be returned. This makes the Windows version behave + like the POSIX version. + + common/exechelp: Avoid magic numbers. + * common/exechelp-w32.c (do_create_pipe): Use symbolic names. + + common/exechelp: Disable debugging by default. + * common/exechelp-w32.c (DEBUG_W32_SPAWN): Set to 0. + + common/exechelp: Fix handle leak. + * common/exechelp-w32.c (gnupg_spawn_process_detached): Close process + handle. + + common/exechelp: Fix opening the 'nul' device. + * common/exechelp-w32.c (gnupg_spawn_process): Fix opening the 'nul' + device. + + common/exechelp: Fix error handling. + * common/exechelp-w32.c (gnupg_spawn_process): Close the right handle. + + common/exechelp: Fix pipe creation. + * common/exechelp-w32.c (gnupg_spawn_process): Fix the creation of the + input pipe. + + tools/mk-tdata: Fix data generation on Windows. + * tools/mk-tdata.c (main): Set stdout to binary mode to avoid newline + conversion. + +2016-02-19 Neal H. Walfield + + gpg: Systematically detect and fix signatures that are out of order. + * g10/keyedit.c (sig_comparison): New function. + (fix_key_signature_order): Merge functionality into... + (check_all_keysigs): ... this function. Rewrite to eliminate + duplicates and use a systematic approach to detecting and moving + signatures that are out of order instead of a heuristic. + (fix_keyblock): Don't call fix_key_signature_order. Call + check_all_keysigs instead after collapsing the uids. + + gpg: Split check_key_signature2. + * g10/sig-check.c (hash_uid_node): Rename from this... + (hash_uid_packet): ... to this. Take a PKT_user_id instead of a + KBNODE. + (check_key_signature2): Split the basic signature checking + functionality into... + (check_signature_over_key_or_uid): ... this new function. + + gpg: Split print_and_check_one_sig. + * g10/keyedit.c (print_and_check_one_sig): Split the print + functionality into... + (print_one_sig): ... this new function. + + gpg: Split the function check_signature_end. + * g10/sig-check.c (check_signature_end): Break the basic signature + check into... + (check_signature_end_simple): ... this new function. + + gpg: Use format_keyid rather than manually formatting the keyid. + * g10/keyedit.c (menu_addrevoker): Use format_keyid rather than + manually formatting the keyid. + * g10/keygen.c (card_write_key_to_backup_file): Likewise. + + gpg: Initialize the primary key when generating a key. + * g10/keygen.c (do_generate_keypair): Initialize + pri_psk->flags.primary, pri_psk->keyid and pri_psk->main_keyid. + + gpg: Add accessor & utility functions for pk->keyid and pk->main_keyid. + * g10/keydb.h (keyid_cmp): New function. + * g10/keyid.c (pk_keyid): New function. + (pk_main_keyid): New function. + (keyid_copy): New function. + (pk_keyid_str): New function. + * g10/packet.h (PKT_public_key): Update comments for main_keyid and + keyid. + +2016-02-18 Daniel Kahn Gillmor + + gpgparsemail: Allow weirdly-mixed pkcs7 signatures. + * tools/gpgparsemail.c: Add and check info->signing_protocol_2. + + gpg: Clean up dangling agent_open and agent_closed declarations. + * g10/keydb.h: Remove agent_open, agent_close declarations/ + * g10/migrate.c: #include for access() + +2016-02-16 Werner Koch + + w32: Make scdaemon build again due to libusb problem. + * configure.ac: Add hack to disable libusb for Windows. Also use + $host instead of $target in the switch + -- + + The new test for libusb does not support cross-compiling. As a quick + workaround we disable libusb for Windows because we can't use it anyway. + + w32: Do not error out if gpgconf is not installed. + * common/homedir.c (check_portable_app): Remove error message. + +2016-02-16 Neal H. Walfield + + gpg: Make ASCII armor decoding more robust to encoding errors. + * g10/armor.c (radix64_read): If the = is followed by the string "3D", + check if the following four characters are valid radix 64 and are + followed by a new line. If so, warn and ignore the '3D'. + +2016-02-16 Werner Koch + + doc: Add a gnupg-module-overview picture. + * doc/gnupg-module-overview.svg: New. + * doc/debugging.texi (Component interaction): New. + * doc/Makefile.am (EXTRA_DIST): Add PNG and PDF versions of + gnupg-module-overview.svg. Remove two eps files. + (BUILT_SOURCES): Add gnupg-module-overview.pdf and .png. Remove + gnupg-card-architecture.epsl + (gnupg_TEXINFOS): Add gnupg-module-overview.svg + (gnupg.dvi): New. + (DISTCLEANFILES): Remove build eps files. + +2016-02-15 NIIBE Yutaka + + common, g10: Fix indentation to silence GCC-6. + * common/iobuf.c (iobuf_ioctl): Fix. + * g10/encrypt.c (encrypt_filter): Likewise. + * g10/keyring.c (prepare_search): Likewise. + + dirmngr: fix for memory alignment. + * dirmngr/dns-stuff.c (get_dns_cert): Cast through void *. + (getsrv, get_dns_cname): Make sure it's aligned for HEADER. + +2016-02-14 Werner Koch + + gpg: Add hidden key-edit subcommand "change-usage". + * g10/keyedit.c (cmdCHANGEUSAGE): New. + (cmds): Add command "change-usage". + (keyedit_menu): Handle that command. + (menu_changeusage): New. + * g10/keygen.c (keygen_add_key_flags): New. + (ask_key_flags): Add optional arg current. + +2016-02-14 Neal H. Walfield + + gpg: Improve API documentation. + * g10/seskey.c (make_session_key): Improve documentation. + (encode_session_key): Improve documentation. + * g10/encrypt.c (encrypt_seskey): Remove gratuitous initialization. + * g10/dek.h (DEK): Improve documenation. + + gpg: Fix calc_header_length when LEN is 0 and improve documentation. + * g10/build-packet.c (calc_header_length): Return the correct haeder + size when LEN is 0. Fix documentation. + + gpg: Fix format_keyid when dynamically allocating the buffer. + * g10/keyid.c (format_keyid): Return a char *, not a const char *. If + BUFFER is NULL, then set LEN to the static buffer's size. + + common: Fix comment. + * common/iobuf.c (iobuf_flush_temp): Fix comment. + +2016-02-13 Werner Koch + + g13: Require a confirmation before g13 is used for DM-Crypt. + * g13/g13-syshelp.c (g13_syshelp_i_know_what_i_am_doing): + * g13/sh-dmcrypt.c (sh_dmcrypt_create_container): Call it. + (sh_dmcrypt_mount_container): Call it. + + g13: Second chunk of code to support dm-crypt. + * g13/be-dmcrypt.c, g13/be-dmcrypt.h: New. + * g13/Makefile.am (g13_SOURCES): Add them. + * g13/backend.c: Include be-dmcrypt.h and call-syshelp.h. + (no_such_backend): Rename to _no_such_backend and provide replacement + macro. + (be_is_supported_conttype): Support DM-Crypt. + (be_take_lock_for_create): Call set_segvice for DM-Crypt. + (be_create_new_keys): Make it a dummy for DM-Crypt. + (be_create_container): Call be_dmcrypt_create_container. + (be_mount_container): call be_dmcrypt_mount_container. + * g13/g13-syshelp.c (main): Enable verbose mode. + * g13/g13tuple.c (get_tupledesc_data): New. + * g13/g13tuple.h (unref_tupledesc): New. + * g13/g13.h (server_control_): Add field "recipients". + * g13/g13.c (main): Fix setting of recipients via cmdline. + (g13_deinit_default_ctrl): Release recipients list. + (g13_request_shutdown): New. Replace all direct update of + shutdown_pending by calls this function. + * g13/server.c (server_local_s): Remove field recipients which is now + part of CTRL. + (reset_notify, cmd_recipient, cmd_create): Adjust for this change. + * g13/create.c (encrypt_keyblob): Rename to g13_encrypt_keyblob. + (g13_create_container): Support DM-Crypt. + * g13/mount.c (parse_header): Allow for meta data copies. + (g13_mount_container): Support DM-Crypt. + * g13/sh-cmd.c (cmd_create): Make it work. + (cmd_mount): New. + * g13/sh-dmcrypt.c (sh_dmcrypt_create_container): Make it work. + (sh_dmcrypt_mount_container): New. + + g13: Improve dump_keyblob. + * g13/g13tuple.c: Include keyblob.h. + (find_tuple_uint): Factor code out to ... + (convert_uint): new. + (all_printable): New. + * g13/mount.c (dump_keyblob: Move and rename to ... + * g13/g13tuple.c (dump_tupledesc): here. Revamp and pretyy print uint + values. + + g13: Define 3 new tags. + * g13/keyblob.h (KEYBLOB_TAG_CONT_NSEC): New. + (KEYBLOB_TAG_ENC_NSEC): New. + (KEYBLOB_TAG_ENC_OFF): New. + + g13: Rename utils.c to g13tuple.c. + * g13/utils.c: Rename to g13tuple.c. + * g13/utils.h: Rename to g13tuple.h. Change all users. + * g13/Makefile.am: Adjust accordingly + + g13: Add functions to handle uint in a keyblob. + * g13/utils.c (append_tuple_uint): New. + (find_tuple_uint): New. + * g13/t-utils.c: New. + * g13/Makefile.am (noinst_PROGRAMS, TESTS): New. + (module_tests, t_common_ldadd): New. + (t_utils_SOURCES, t_utils_LDADD): New. + + g13: Re-factor high level create code. + * g13/create.c (g13_create_container): Factor some code out to ... + * g13/backend.c (be_take_lock_for_create): new. + + g13: Return an error for non-existing device. + * g13/sh-cmd.c (cmd_device): Set ERR. + + g13: Fix releasing of a syshelp context. + * g13/call-syshelp.c (call_syshelp_release): Allow a NULL arg. + + g13: Switch over to common/exectool.c. + * g13/sh-exectool.c: Remove. It has been replaced by common/exectool.c. + * g13/Makefile.am (g13_syshelp_SOURCES): Remove sh-exectool.c + * g13/sh-blockdev.c: Include exectool.h. Change sh_exec_tool to + gnupg_exec-tool. + * g13/sh-dmcrypt.c: Ditto. + + common: Make gnupg_exec_tool conform to spec. + * common/exectool.c (gnupg_exec_tool): Allocate extra byte. Allow + zero length read. Append hidden byte. Release memory on error. + + g13: First chunk of code to support dm-crypt. + * g13/call-syshelp.c, g13/call-syshelp.h: New. + * g13/g13-syshelp.c, g13/g13-syshelp.h: New. + * g13/sh-cmd.c: New. + * g13/sh-blockdev.c: New. + * g13/sh-exectool.c: New. + * g13/sh-dmcrypt.c: New. + * g13/Makefile.am (sbin_PROGRAMS): Add g13-syshelp.c + (g13_syshelp_SOURCES): New. + (g13_syshelp_LDADD): New. + + * g13/g13.c (opts): Add option --type. + (g13_deinit_default_ctrl): New. + (main): Implement that option. Call g13_deinit_default_ctrl. + * g13/g13.h (struct call_syshelp_s): New declaration. + (server_control_s): Add field syshelp_local. + * g13/keyblob.h (KEYBLOB_TAG_CREATED): New. + (KEYBLOB_TAG_ALGOSTR): New. + (KEYBLOB_TAG_HDRCOPY): New. + * g13/backend.c (be_parse_conttype_name): New. + (be_get_detached_name): Add CONTTYPE_DM_CRYPT. + + tests: Remove some harmless warnings in regression tests. + * tests/openpgp/gpg-agent.conf.tmpl: Remove --use-standard-socket. + +2016-02-12 Neal H. Walfield + + common: Change simple_query to ignore status messages. + * common/simple-pwquery.c (simple_query): Ignore status messages. + +2016-02-12 NIIBE Yutaka + + g10: Make sure to have the directory for trustdb. + * g10/tdbio.c (tdbio_set_dbname): Return earlier if !CREATE. Check + the directory and create it if none before calling take_write_lock. + +2016-02-02 Neal H. Walfield + + doc: Note that rngd can also be used to quickly generate insecure keys. + * doc/gpg-agent.texi (Agent Options): Add comment to the description + of --debug-quick-random that rngd can also be used to quickly generate + key. + +2016-01-27 Werner Koch + + scd: Fix size_t/int mismatch in libusb. + * scd/ccid-driver.c (bulk_in, abort_cmd, ccid_poll): Change msglen to + int. + + scd: Fix detection of libusb. + * configure.ac (HAVE_LIBUSB): Clear if no header file was found. + (LIBUSB_LIBS): Ditto. + + dirmngr: Build fix for FreeBSD (EAI macros) + * dirmngr/dns-stuff.c (map_eai_to_gpg_error): Map EAI_NODATA and + EAI_ADDRFAMILY only if defined. + +2016-01-27 NIIBE Yutaka + + scd: Migrate to new API of libusb 1.0. + * configure.ac (LIBUSB_CPPFLAGS): New. + * scd/Makefile.am (AM_CPPFLAGS): Add LIBUSB_CPPFLAGS. + * scd/ccid-driver.c: Use libusb 1.0 API. + +2016-01-26 Werner Koch + + Release 2.1.11. + +2016-01-26 Andre Heinecke + + gpgtar,w32: Fix gpgtar 8 bit encoding handling on W32. + * common/utf8conv.c (wchar_to_utf8): Factor code out to ... + (wchar_to_cp): new. + (utf8_to_wchar): Factor code out to ... + (cp_to_wchar): new. + (wchar_to_native): New. + (native_to_wchar): New. + * tools/gpgtar-create.c (fillup_entry_w32): Use native_to_wchar. + (scan_directory): Use wchar_to_native. + +2016-01-26 NIIBE Yutaka + + g10: Fix segfault on unsupported curve. + * g10/call-agent.c (learn_status_cb): Don't use NULL for strcmp. + + sm: small fix for GCC 6. + * sm/export.c (insert_duptable): Use unsigned 0. + +2016-01-25 Werner Koch + Daiki Ueno + + gpg: Print PROGRESS status lines during key generation. + * g10/call-agent.c (cache_nonce_status_cb): Rewrite by using + has_leading_keyword. Handle PROGRESS lines. + +2016-01-25 Werner Koch + + agent: Send PROGRESS status lines to the client. + * agent/gpg-agent.c (struct progress_dispatch_s): New. + (progress_dispatch_list): New. + (main): Register libgcrypt pogress handler. + (agent_libgcrypt_progress_cb): New. + (agent_set_progress_cb): New. + (unregister_progress_cb): New. + (agent_deinit_default_ctrl): Call unregister. + * agent/command.c (progress_cb): New. + (start_command_handler): Register progress callback. + + speedo: Allow use of SHA-256 checksums. + * build-aux/getswdb.sh: Add option --find-sha256sum. + * build-aux/speedo.mk (libgpg_error_sha2): New var. Also for all + other packages. + (SHA2SUM): New. + (SETVARS, SETVARS_W64): Prefer sha256sum over sha1sum. + (installer-from-source): Create swdb fragment. + +2016-01-22 Werner Koch + + dirmngr: Indicate that serial numbers are hexadecimal. + * dirmngr/misc.c (hexify_data): Add arg with_prefix. Adjust all + callers. + * dirmngr/crlcache.c (cache_isvalid): Print "0x" in front of the S/N. + + dirmngr: Provide the keyserver pool name even if there is no CNAME. + * dirmngr/ks-engine-hkp.c (map_host): Fix setting of r_poolname. + +2016-01-22 Daniel Kahn Gillmor + wk@gnupg.org + + dirmngr: Use sks-keyservers CA by default for the hkps pool. + * dirmngr/Makefile.am (dist_pkgdata_DATA): Add sks-keyservers.netCA.pem. + * dirmngr/http.c (http_session_new): Add optional arg + intended_hostname and set a default cert. + * dirmngr/ks-engine-hkp.c (send_request): Pass httphost to + http_session_new. + +2016-01-22 Werner Koch + + gpg: Allow new user ids with only the mail address. + * g10/keygen.c (ask_user_id): Allow empty name. + +2016-01-21 Werner Koch + + gpg: Improve header text of the auto-created revocations. + * g10/revoke.c (gen_standard_revoke): Improve header text for the + file. Add info output. + + gpg: Make --auto-key-retrieve work with dirmngr configured server. + * g10/call-dirmngr.c (gpg_dirmngr_ks_list): Make R_KEYSERVER optional. + * g10/keyserver.c (keyserver_any_configured): New. + (keyserver_put): Remove arg keyserver because this will always receive + opt.keyserver which is anyway used when connecting dirmngr. Do not + check opt.keyserver. + (keyserver_import_cert): Replace opt.keyserver by + keyserver_any_configured. + * g10/mainproc.c (check_sig_and_print): Ditto. + * g10/import.c (revocation_present): Ditto. + * g10/getkey.c (get_pubkey_byname): Ditto. + * g10/gpgv.c (keyserver_any_configured): Add stub. + * g10/test-stubs.c (keyserver_any_configured): Add stub. + +2016-01-20 Werner Koch + + gpg: Silence message about ignoring revoked user ids. + * g10/trustdb.c (tdb_get_validity_core): Print message only in debug + mode. + + agent: New option --pinentry-timeout. + * agent/gpg-agent.c (oPinentryTimeout): New. + (opts): Add new option. + (parse_rereadable_options): PArse that option. + (main): Tell gpgconf about this option. + * agent/call-pinentry.c (start_pinentry): Send option to Pinentry. + * tools/gpgconf-comp.c (gc_options_gpg_agent): Add Option. + +2016-01-19 Werner Koch + + gpg: Streamline use of error messages in tofu.c. + * g10/tofu.c: Make use of print_further_info to reduce the number of + different error messages to be translated. Also streamline some + messages. + + common: Add substitute code for libgpg-error < 1.22. + * common/util.h (GPG_ERR_DB_CORRUPTED): New. + + gpg: Add function print_further_info. + * g10/misc.c (print_further_info): New. + +2016-01-18 Werner Koch + + g10: Improve strings printed by tofu.c. + * g10/tofu.c: Include ttyio.h. Change many strings to help + translating. Make use of ngettext wehere needed. + (CONTROL_L): New. + (TIME_AGO_UNIT_SMALL_NAME): Remove this and all similar *_NAME macros. + (time_ago_unit): Remove. + (get_trust): Use tty_prints and cpr_get only for the actual prompt. + Add Ctrl-L hack. + (show_statistics): Use two English strings for singular and plural. + + * po/POTFILES.in: Add tofu.c. + + gpg: Use "days" in "...newer than..." diagnostics. + * g10/sig-check.c (check_signature_metadata_validity): Use days if + useful. + + Use ngettext for some strings. + * scd/app-openpgp.c (build_enter_admin_pin_prompt): Use ngettext for + some diagnostics. + (do_genkey): Ditto. + * g10/keyedit.c (check_all_keysigs, menu_delsig, menu_clean): Ditto. + * g10/keylist.c (print_signature_stats): Ditto. + * g10/keyserver.c (keyserver_refresh): Ditto. + * g10/sig-check.c (check_signature_metadata_validity): Ditto. + * g10/sign.c (do_sign): Ditto. + * g10/trustdb.c (reset_trust_records): Ditto. + (validate_keys): Use a table like diagnostic output. + +2016-01-15 Werner Koch + + kbx,w32: Use shorter retry intervals for keybox_file_rename. + * kbx/keybox-util.c (keybox_file_rename): Restart retry intervals + after 800ms. + +2016-01-14 Werner Koch + + w32: Fix deadlock introduced by keybox_file_rename. + * g10/keyring.c (keyring_lock) [W32]: Flush the close cache before + locking. + * kbx/keybox-init.c (keybox_lock) [W32]: Close the file before + locking. + + gpg: Detect race between pubring.gpg and pubring.kbx use. + * g10/keydb.c (maybe_create_keyring_or_box): Detect race condition. + + kbx: New function keybox_file_rename to replace rename. + * kbx/keybox-util.c: Include windows.h. + (keybox_file_rename): New. + * kbx/keybox-update.c (rename_tmp_file): Replace remove+rename by + keybox_file_rename. + * g10/keyring.c (rename_tmp_file): Ditto. + + kbx: Add function keybox_tmp_names to avoid code duplication. + * kbx/keybox-update.c (create_tmp_file): Move some code to... + * kbx/keybox-util.c (keybox_tmp_names): new. + * g10/keyring.c: Include keybox.h. + (create_tmp_file): Replace parts by keybox_tmp_names. + + gpg: Make --list-options show-usage the default. + * g10/gpg.c (main): Add LIST_SHOW_USAGE. + +2016-01-13 Werner Koch + + kbx: Change return type of search functions to gpg_error_t. + * kbx/keybox-search.c (keybox_search_reset): Change return type to + gpg_error_t. + (keybox_search): Ditto. Also handle GPG_ERR_EOF. + * sm/keydb.c (keydb_search_reset): Ditto. + + gpg: Improve error code from lock_all. + * g10/keydb.c (lock_all): Do not clobber RC during failur cleanup. + + kbx: Improve and fix keybox_lock. + * kbx/keybox-init.c (keybox_lock): Make sure ERR is initialized. Get + error codes from dotlock functions. + + common: Make sure dotlock functions set a proper ERRNO. + * common/dotlock.c (map_w32_to_errno): New. + (read_lockfile): Return a proper ERRNO. + (dotlock_create_unix): Do not let log functions clobber ERRNO. + (dotlock_take_unix): Ditto. + (dotlock_release_unix): Ditto. + (dotlock_create_w32): Set proper ERRNO. + (dotlock_take_w32): Ditto. + (dotlock_release_w32): Ditto. + + kbx: Implement keybox_lock for use by gpg. + * kbx/keybox-defs.h: Include dotlock.h and logging.h. + (CONST_KB_NAME): Remove. Replace usage by KB_NAME. + (struct keybox_name): Add field "lockhd". + * kbx/keybox-init.c (keybox_register_file): Init LOCKHD. + (keybox_lock): Chnage to return gpg_error_t. Implement locking. + + gpg: Make sure to mark a duplicate registered keybox as primary. + * kbx/keybox-init.c (keybox_register_file): Change interface to return + the token even if the file has already been registered. + * g10/keydb.c (primary_keyring): Rename to primary_keydb. + (maybe_create_keyring_or_box): Change return type to gpg_error_t. + (keydb_add_resource): Ditto. s/rc/err/. + (keydb_add_resource): Mark an already registered as primary. + * sm/keydb.c (maybe_create_keybox): Change return type to gpg_error_t. + (keydb_add_resource): Ditto. s/rc/err/. + (keydb_add_resource): Adjust for changed keybox_register_file. + +2016-01-13 NIIBE Yutaka + + Fix to support git worktree. + * autogen.sh, Makefile.am, doc/Makefile.am: Use -e for testing .git. + +2016-01-12 Werner Koch + + ssh: Accept OpenSSH *cert-v01 key variants. + * agent/command-ssh.c (SPEC_FLAG_WITH_CERT): New. + (ssh_key_types): Add OpenSSH cert types. + (stream_read_string): Allow a dummy read. + (ssh_receive_mpint_list): Pass SPEC by reference. + (ssh_receive_mpint_list): New arg CERT and use it. + (ssh_receive_key): Read certificate into an estream object and modify + parser to make use of that object. + +2016-01-12 NIIBE Yutaka + + common: Fix iobuf API of filter function for alignment. + * common/iobuf.h: Fix comment. + + common: Fix iobuf API of filter function for alignment. + * common/iobuf.h (IOBUFCTRL_DESC): Change the call semantics. + * common/iobuf.c (iobuf_desc): Add the second argument DESC. + (print_chain, iobuf_close, do_open, iobuf_sockopen, iobuf_ioctl) + (iobuf_push_filter2, pop_filter, iobuf_write_temp): Change calls + of iobuf_desc. + (file_filter, file_es_filter, sock_filter, block_filter): Fill the + description. + * common/t-iobuf.c (every_other_filter, double_filter): Likewise. + * g10/armor.c, g10/cipher.c, g10/compress-bz2.c, g10/compress.c, + g10/decrypt-data.c, g10/encrypt.c, g10/mdfilter.c, g10/progress.c, + g10/textfilter.c: Likewise. + +2016-01-11 Werner Koch + + gpg: Fix NULL de-ref for ambiguous key check in --export-ssh-keys. + * g10/getkey.c: Allow arg RET_KEYBLOCK to be NULL. + +2016-01-09 Werner Koch + + tools: Remove gpgkey2ssh. + * tools/gpgkey2ssh.c: Remove. + * tools/Makefile.am (bin_PROGRAMS): Ditto. + +2016-01-08 Werner Koch + + gpg: Support ECDSA keys with --export-ssh-key. + * g10/export.c (key_to_sshblob): Add hack for ECDSA. + + gpg: New command --export-ssh-key. + * g10/export.c: Include membuf.h and host2net.h. + (key_to_sshblob): New. + (export_ssh_key): New. + * g10/gpg.c (aExportSshKey): New. + (opts): Add command. + (main): Implement that command. + + gpg: Add an exact search flag to the PK struct. + * g10/getkey.c (merge_selfsigs_subkey): Clear exact flag. + (finish_lookup): Set exact flag. + * g10/packet.h (PKT_public_key): Add field flags.exact. + + Print warnings if old daemon versions are used. + * common/status.h (STATUS_WARNING): New. + * g10/call-agent.c (warn_version_mismatch): New. + (start_agent): Call warn function. + * g10/call-dirmngr.c: Include status.h. + (warn_version_mismatch): New. + (create_context): Call warn function. + * sm/call-agent.c (warn_version_mismatch): New. + (start_agent): Call warn function. + (gpgsm_agent_learn): Call warn function. + * sm/call-dirmngr.c (warn_version_mismatch): New. + (prepare_dirmngr): Call warn function. + + common: New function compare_version_strings. + * common/stringhelp.c (parse_version_number): New. + (parse_version_string): New. + (compare_version_strings): New. + * common/t-stringhelp.c (test_compare_version_strings): New. + (main): Call test. Return ERRCOUNT instead of 0. + + common: New function get_assuan_server_version. + * common/asshelp.c: Include membuf.h. + (get_assuan_server_version): New. + * g10/call-agent.c (agent_get_version): Use new function. + + common: New put_membuf_cb to replace static membuf_data_cb. + * common/membuf.c (put_membuf_cb): New. + * agent/call-scd.c (membuf_data_cb): Remove. Change callers to use + put_membuf_cb. + * common/get-passphrase.c (membuf_data_cb): Ditto. + * g10/call-agent.c (membuf_data_cb): Ditto. + * sm/call-agent.c (membuf_data_cb): Ditto. + +2016-01-07 Werner Koch + + gpg: Return an error code from keygrip_from_pk. + * g10/keyid.c (keygrip_from_pk): Return an error code. + + gpg: Avoid warnings about possible NULL deref. + * g10/getkey.c (cache_public_key): Protect deref of CE which actually + can't happen. + * g10/keygen.c (quickgen_set_para): s/sprintf/snprintf/. + * g10/tofu.c (end_transaction, rollback_transaction): Allow NULL for + DB. + * g10/trustdb.c (update_min_ownertrust): Remove useless clearling of + ERR. + + gpg: Fix warnings about useless assignments. + * g10/armor.c (parse_hash_header): Remove duplicate var assignment. + * g10/getkey.c (cache_user_id): Ditto. + * g10/keygen.c (ask_curve): Ditto. This also fixes a small memory + leak. + + * g10/keygen.c (proc_parameter_file): Remove useless assignment or + pointer increment. + (generate_keypair): Ditto. + * g10/getkey.c (finish_lookup, lookup): Ditto. + * g10/card-util.c (change_pin): Ditto. + * g10/gpg.c (main) : Ditto. + * g10/import.c (import): Ditto. + (print_import_check): Ditto + * g10/keyring.c (do_copy): Ditto. + * g10/tdbio.c (tdbio_read_record): Ditto. + * g10/trustdb.c (tdb_update_ownertrust): Ditto. + (update_validity): Ditto. + + * g10/server.c (cmd_passwd): Remove useless call to skip_options. + + sm: Avoid warnings about useless assignments. + * sm/call-dirmngr.c (prepare_dirmngr): Remove setting of ERR. + (unhexify_fpr): Remove useless computation on N. + * sm/certchain.c (do_validate_chain): Remove clearing of RC. Remove + useless setting of RC. + * sm/fingerprint.c (gpgsm_get_keygrip): Remove setting of RC. + * sm/gpgsm.c (build_list): Replace final stpcpy by strcpy. + * sm/keydb.c (keydb_clear_some_cert_flags): Remove clearing of RC. + * sm/server.c (cmd_getauditlog): Comment unused skip_options. + + kbx: Avoid warnings about useless assignments. + * kbx/keybox-dump.c (_keybox_dump_blob): Remove setting of IN_RANGE + and the last increment of P. + + gpg: Fix DNS cert lookup returning an URL. + * g10/call-dirmngr.c (dns_cert_status_cb): Store URL status in the URL + param. The old code was entirely buggy (c+p error). + +2016-01-06 Daniel Kahn Gillmor + + Fix keystrlen to work when OPT.KEYID_FORMAT is KF_DEFAULT. + * g10/keyid.c (keystrlen): If opt.keyid_format is KF_DEFAULT unset, + default to KF_SHORT. + (format_keyid): Default to KF_SHORT, not KF_0xLONG. + +2016-01-06 Werner Koch + + gpg: Silence some regression tests. + * g10/test.c (TEST): Print diagnostics only in verbose mode. + + gpg: Avoid using an uninitialized SALT on premature EOF. + * g10/parse-packet.c (parse_key): Check for premature end of salt. + + gpg: Silence warnings found by static analyzer. + * g10/keyedit.c (change_passphrase): Remove useless init of ANY. + (keyedit_quick_adduid): Remove useless setting of ERR. + * g10/parse-packet.c (parse_key): Remove PKTLEN from condition because + it has been checked before the loop. + (parse_plaintext): Remove useless init of PKTLEN. + + kbx: Avoid faulty fclose in an error case. + * kbx/keybox-update.c (blob_filecopy): Do not close an uninitialized + file pointer after a failure to create a temp file. + * kbx/keybox-openpgp.c (next_packet): Remove duplicate assignment of + PKTLEN. + + dirmngr: Silence one regression test. + * dirmngr/t-dns-stuff.c (main): Do not print info during standard + "make check". + + common: Avoid warnings about useless assignments. + * common/b64enc.c (b64enc_finish): Remove var assignment which is not + used later. + * common/iobuf.c (file_filter): Ditto. + * common/tlv.c (do_find_tlv): Ditto. + * common/userids.c (classify_user_id): Ditto. + + tests: Use info and error instead of a plain echo. + * tests/openpgp/4gb-packet.test: Use error and info. + + common: Do not deref vars in tests after a fail(). + * common/t-convert.c (test_bin2hex): Turn if conditions into if-else + chains to avoid accessing unchecked data. + (test_bin2hexcolon): Ditto. + * common/t-mapstrings.c (test_map_static_macro_string): Ditto. + * common/t-stringhelp.c (test_percent_escape): Ditto. + (test_make_filename_try): Ditto. + (test_make_absfilename_try): Ditto. + * common/t-timestuff.c (test_timegm): Ditto. + +2016-01-05 Werner Koch + + gpg: Align notes about minimal keysize with actual checks. + * g10/keygen.c (ask_keysize): Use 768 for the minimal value for DSA in + export mode. Improve readability. + +2016-01-05 NIIBE Yutaka + + agent: Fix RSA verification for card. + * agent/pksign.c (agent_pksign_do): Use S-exp of public key, instead + of shadowed key. + +2016-01-04 Neal H. Walfield + + gpg: Fix double free. + * g10/getkey.c (get_pubkeys): Fix double free. + +2015-12-24 NIIBE Yutaka + + agent: IMPORT_KEY with --force option fix. + * agent/cvt-openpgp.c (convert_from_openpgp_main): Add an option not + to check existing key. + (convert_from_openpgp): Ditto. + (convert_from_openpgp_native): Call convert_from_openpgp_main with + dontcare_exist=0. + * agent/command.c (cmd_import_key): Call with dontcare_exist=force. + + g10: Use --force when importing key for bkuptocard. + * g10/call-agent.c (agent_import_key): Add an argument FORCE. + * g10/import.c (transfer_secret_keys): Likewise. + (import_secret_one): Call transfer_secret_keys with FORCE=0. + * g10/keyedit.c (keyedit_menu): Call with FORCE=1. + + g10: Remove subcommand checkbkupkey for --key-edit. + * g10/keyedit.c (keyedit_menu): Remove cmdCHECKBKUPKEY support. + + g10: Allow relative path for specifying the file for bkuptocard. + * g10/keyedit.c (keyedit_menu): Assume the file is under GNUPGHOME. + Also support tilda expansion. + + g10: fix regression of bkuptocard subcommand in --edit-key. + * g10/keyedit.c (keyedit_menu): Call transfer_secret_keys. + * g10/import.c (transfer_secret_keys): Make it global function. + Allow stats==NULL. + + agent: Support --force option for IMPORT_KEY. + * agent/command.c (cmd_keywrap_key): New option --force. + +2015-12-23 Werner Koch + + gpg: Rename struct pubkey to pukey_s and add pubkey_t. + * g10/keydb.h (struct pubkey): Rename to pubkey_s. + (pubkey_t): New. Change all struct pubkey_s to use this type. + * g10/getkey.c (get_pubkeys): Rename arg keys to r_keys. + + gpg: Simplify status message code from commit b30c15bf. + * g10/keygen.c (card_write_key_to_backup_file): Simplify by using + hexfingerprint. + + gpg: Add standard free() semantic to pubkey_free. + * g10/getkey.c (pubkey_free): Check for NULL arg. + + gpg: Fix use of assert from commit dc417bf0. + * g10/keydb.c (keydb_update_keyblock): De-ref after the assert. Use + %zu for size_t. + + gpg: Do not translate debug output. + * g10/getkey.c (parse_def_secret_key): Do not make strings passed to + log_debug translatable. + +2015-12-23 NIIBE Yutaka + + scd: Fix commit b30c15bf (again). + * g10/keygen.c (do_generate_keypair): Clear the variable S. + +2015-12-22 Neal H. Walfield + + gpg: Fix type. + * g10/keygen.c (card_write_key_to_backup_file): Change n to a size_t. + + gpg: Fix error message. + * g10/getkey.c (parse_def_secret_key): Fix error message. + + gpg: Don't check for ambiguous keys. + * g10/gpg.c (struct result): Move from here... + * g10/keydb.h (struct pubkey): ... to here. Update users. + * g10/gpg.c (check_user_ids): Move from here... + * g10/getkey.c (get_pubkeys): ... to here. Update users. Use + get_pubkey_byname to look up the keys (this also prunes invalid keys). + (pubkey_free): New function. + (pubkeys_free): New function. + * g10/gpg.c (main): Don't check for ambiguous key specifications. + + gpg: Lazily evaluate --default-key. + * g10/gpg.c (main): If --encrypt-to-default-key is specified, don't + add --default-key's value to REMUSR here... + * g10/pkclist.c (build_pk_list): ... do it here. + * tests/openpgp/Makefile.am (TESTS): Add default-key.test. + * tests/openpgp/default-key.test: New file. + + gpg: Remove unused parameter. + * g10/pkclist.c (build_pk_list): Remove parameter use, which is always + called set to PUBKEY_USAGE_ENC. Update callers. + + gpg: Improve check for ambiguous keys. + * g10/gpg.c (check_user_ids): When checking for ambiguous keys, ignore + encryption-only keys when a signing key is needed and vice-versa. + + gpg: Fix TOCTTOU when updating keyblocks. + * g10/keydb.c (keydb_update_keyblock): Don't replace the record at the + current offset. After taking the lock, extract the fingerprint from + the keyblock, find it and then replace it. + + Only add the user supplied CFLAGS after running any autoconf tests. + * configure.ac: Only add the user supplied CFLAGS after running any + autoconf tests. + + gpg: Suppress a warning. + * dirmngr/dns-stuff.c (enable_dns_tormode): Reference new_circuit to + avoid a warning when ADNS is not available. + + gpg: Remove dead code. + * kbx/keybox-defs.h (struct keybox_found_s): Remove unused fields + offset and n_packets. + + gpg: Display the key that is invalid, not the search description. + * g10/getkey.c (parse_def_secret_key): Display the key that is + invalid, not the search description. + + gpg: Mark more options as coming from the config file (when this holds) + * g10/gpg.c (main): When --default-key or --encrypt-to-default-key is + taken from the config file, note this. + + gpg: Use enums instead of defines. + * g10/keydb.h (PK_LIST_ENCRYPT_TO): Change from a macro to an enum. + (PK_LIST_HIDDEN): Likewise. + (PK_LIST_CONFIG): Likewise. + (PK_LIST_SHIFT): Likewise.n + +2015-12-21 NIIBE Yutaka + + po: Update Japanese translation. + + g10: clean up of headers for card. + * g10/main.h (save_unprotected_key_to_card): Remove. + * g10/options.h (ctapi_driver, pcsc_driver, disable_ccid): Remove. + +2015-12-21 Werner Koch + + common: New file fwddecl.h. + * common/util.h (server_control_s, ctrl_t): Move to ... + * common/fwddecl.h: New file. + * common/call-gpg.h: Replace typedef by fwddecl.h. Change include + protection macro name. + * common/Makefile.am (common_sources): Add fwddecl.h. + +2015-12-18 Werner Koch + + build: Add required macro for pkg-config. + * configure.ac (PKG_PROG_PKG_CONFIG): New. + +2015-12-18 NIIBE Yutaka + + g10: Remove deprecated internal functions. + * g10/keygen.c (do_ask_passphrase, generate_raw_key) + (gen_card_key_with_backup, save_unprotected_key_to_card): Remove. + + g10: Fix a regression for generating card key with backup. + * g10/main.h (receive_seckey_from_agent): Declare. + * g10/keygen.c (card_write_key_to_backup_file): New. + (card_store_key_with_backup): New. + (do_generate_keypair): Create a key on host for encryption key when + backup is requested. Then, call card_store_key_with_backup. + +2015-12-17 NIIBE Yutaka + + g10: factor out a function for secret key retrieval. + * g10/export.c (receive_seckey_from_agent): New. + (do_export_stream): Use it. + +2015-12-16 Neal H. Walfield + + gpg: When checking for ambiguous keys, ignore invalid keys. + * g10/gpg.c (check_user_ids): When checking for ambiguous keys, ignore + disabled, revoked and expired keys (if appropriate for the provided + option). + +2015-12-15 Werner Koch + + common: Use default_errsource for call-gpg and exectool. + * common/call-gpg.c (my_error_from_syserror, my_error_from_errno): New. + Use these wrappers. + * common/exectool.c (my_error_from_syserror): New. Use these + wrappers. + + gpg: Reduce number of strings to translate. + * g10/getkey.c (parse_def_secret_key): Do not make debug messages + translatable. Make use of print_reported_error. + + gpg: New function to printed a detailed error code. + * g10/misc.c (print_reported_error): New. + +2015-12-15 Neal H. Walfield + + gpg: Improve the keyblock cache's transparency. + * kbx/keybox-search.c (keybox_seek): New function. + * g10/keydb.c (keydb_search): When reading from the cache, seek to + just after the cached record. + + gpg: Improve the keyblock cache's transparency. + * kbx/keybox-search.c (keybox_offset): New function. + * g10/keydb.c (struct keyblock_cache): Add fields resource and offset. + (keyblock_cache_clear): Reset HD->KEYBLOCK_CACHE.RESOURCE and + HD->KEYBLOCK_CACHE.OFFSET. + (keydb_search): Don't use the cached result if it comes before the + current file position. When caching an entry, also record the + position at which it was found. + + gpg: Use more descriptive names. + * g10/keyring.c (KR_NAME): Rename this... + (KR_RESOURCE): ... to this. Update users. + (struct keyring_name): Rename this... + (struct keyring_resource): ... to this. Update users. + (struct off_item): Rename this... + (struct key_present): ... to this. Update users. + (OffsetHashTable): Rename this... + (key_present_hash_t): ... to this. Update users. + (kr_offtbl): Rename this... + (key_present_hash): ... to this. Update users. + (kr_offtbl_ready): Rename this... + (key_present_hash_ready): ... to this. Update users. + (KEY_PRESENT_HASH_BUCKETS): New define. Replace use of literals + with this. + (new_offset_item): Rename this... + (key_present_value_new): ... to this. Update users. + (release_offset_items): Drop dead code. + (new_offset_hash_table): Rename this... + (key_present_hash_new): ... to this. Update users. + (release_offset_hash_table): Drop dead code. + (lookup_offset_hash_table): Rename this... + (key_present_hash_lookup): ... to this. Update users. + (update_offset_hash_table): Rename this... + (key_present_hash_update): ... to this. Drop unused parameter off. + Update users. + (update_offset_hash_table_from_kb): Rename this... + (key_present_hash_update_from_kb): ... to this. Drop unused parameter + off. Update users. + +2015-12-15 NIIBE Yutaka + + sm: Handle gcry_pk_encrypt return value. + * sm/encrypt.c (encrypt_dek): Don't ignore failure of gcry_pk_encrypt. + +2015-12-14 Werner Koch + + common: Change license of isascii.c to all-premissive, + * common/isascii.c: Change. + + common: Change license of some modules to LGPLv3+/GPLv2+. + * common/status.c: Change from GPLv3 to LGPLv3+/GPLv2+. + * common/status.h: Ditto. + * common/yesno.c: Ditto. + * common/common-defs.h: Ditto. + * common/gettime.h: Ditto. + * common/keyserver.h: Ditto. + + common: Change license for exectool to LGPLv3+/GPLv2+. + * common/exectool.c, common/exectool.h: Change license. + + common: Rename sh-exectool to exectool. + * common/sh-exectool.c: Rename to exectool.c. + * common/sh-exectool.h: Rename to exectool.h. + * common/Makefile.am (common_sources): Adjust for rename. + * common/exectool.c (sh_exec_tool_stream): Rename to + gnupg_exec-tool-stream. + (sh_exec_tool): Rename to gnupg_exec_tool. + * tools/gpgtar-create.c (gpgtar_create): Adjust for changes. + * tools/gpgtar-extract.c: Adjust for changes. + * tools/gpgtar-list.c: Adjust for changes. + +2015-12-14 Damien Goutte-Gattat + + gpg: Print ownertrust in TOFU+PGP trust model. + * g10/keyedit.c: Print ownertrust in TOFU+PGP trust model. + +2015-12-14 Neal H. Walfield + + gpg: Fix --default-key checks. + * g10/getkey.c (parse_def_secret_key): Don't just check if a secret + key is available for the public key, also consider subkeys. Also + check that the key has the signing capability, is not revoked, is not + expired and is not disabled. Print a warning if there was a least one + value passed to --default-key and all were ignored. + +2015-12-14 NIIBE Yutaka + + scd: Fix regression for generating RSA keys on card. + * scd/app-openpgp.c (do_genkey): Strip leading zeros for fingerprint + computation. + +2015-12-12 Werner Koch + + gpg: Use a regular type instead of a void* for import stats. + * g10/import.c (struct stats_s): Rename to import_stats_s. Change all + users. + * g10/main.h (import_stats_t): New. Change fucntions to use this + instead of a void pointer. + + Remove replacements for libgpg-error < 1.21. + * common/util.h: Remove replacement macros for libgpg-error<1.21. + * common/types.h: Ditto. + * common/mischelp.h: Ditto. + * common/t-mapstrings.c: Include t-support.h before stringhelp.h + * common/t-stringhelp.c: Ditto. + * common/t-support.h: Always include gpg-error.h. + * kbx/keybox-search.c: Do not include stringhelp.h so that keybox-defs + comes first. + +2015-12-11 Neal H. Walfield + + gpg: Fix buffer overflow. + * g10/keydb.c (keydb_search_desc_dump): Fix buffer overflow. + +2015-12-11 Justus Winter + + agent: Improve error handling. + * agent/pksign.c (agent_pksign_do): Improve error handling. + + Fix required libgpg-error version. + * configure.ac (NEED_GPG_ERROR_VERSION): We need version 1.21 for the + poll interface. + +2015-12-11 Neal H. Walfield + + gpg: Don't error out if a key occurs multiple times in the keyring. + * g10/gpg.c (check_user_ids): Don't error out if a key occurs multiple + times in the keyring. Instead, print a warning. When printing out + fingerprint prints, use format_hexfingerprint to format them. + +2015-12-10 Daniel Hoffend + + scd: Fix removal of unplugged usb readers on Windows. + * scd/apdu.c (pcsc_error_to_sw): map PCSC_E_NO_SERVICE and + PCSC_E_SERVICE_STOPPED to the internal SW_HOST_NO_READER error code. + +2015-12-07 Justus Winter + + tests: Add some more gpgtar tests. + * tests/openpgp/gpgtar.test: Add more tests. + + dirmngr: Initialize http status code. + * dirmngr/ks-action.c (ks_action_search): Initialize 'http_status' as + it is unused if LDAP is used to search for keys. + +2015-12-04 Daiki Ueno + + gpg: Write ERROR status on delete-key cancellation. + * g10/delkey.c (do_delete_key): Write ERROR status code with the error + location "delete_key.secret", when the user cancelled the operation on + Pinentry. + +2015-12-04 Justus Winter + + dirmngr: Stricter handling of http error codes. + * dirmngr/ks-action.c (ks_action_search): Only retry if the keyserver + responded with a '404 Not Found'. + * dirmngr/ks-engine-hkp.c (send_request): Return http status code. + (ks_hkp_search): Likewise. + (ks_hkp_{get,put}): Adapt call to 'send_request'. + * dirmngr/ks-engine.h (ks_hkp_search): Update prototype. + + dirmngr: Really search all keyservers for patterns. + * dirmngr/ks-action.c (ks_action_search): Search all configured + keyservers for the given patterns. + + dirmngr: Handle http status '501 Not Implemented'. + * dirmngr/ks-engine-hkp.c (send_request): Handle status 501 and return + GPG_ERR_NOT_IMPLEMENTED. + + tools/gpgtar: Implement symmetric encryption. + * tests/openpgp/gpgtar.test: Add test case. + * tools/gpgtar-create.c (gpgtar_create): Pass '--symmetric' flag to + gpg. + * tools/gpgtar.c (parse_arguments): We do handle the argument now. + + tools/gpgtar: Implement signing. + * tests/openpgp/gpgtar.test: Test signing. + * tools/gpgtar-create.c (gpgtar_create): Add 'sign' option, add the + appropriate gpg arguments to implement signing and selecting the local + user. + * tools/gpgtar.c (parse_options): We do handle '--local-user' now. + (main): Handle signing, encrypting, and doing both when creating an + archive. + * tools/gpgtar.h (gpgtar_create): Update prototype. + + tools/gpgtar: Use the new exectool helper. + * tools/Makefile.am: gpgtar now requires neither npth nor libassuan. + * tools/gpgtar-create.c (gpgtar_create): Use the new 'sh-exectool' + helper. + * tools/gpgtar-extract.c (gpgtar_extract): Likewise. + * tools/gpgtar-list.c (gpgtar_list): Likewise. + * tools/gpgtar.c (main): Set default gpg program. Drop the + initialization of npth and libassuan. + + common: Add a stream interface to 'sh-exectool'. + * common/sh-exectool.c (struct copy_buffer): Add infrastructure for + copying between streams. + (copy_buffer_{init,shred,do_copy,flush}): New functions. + (sh_exec_tool_stream): Rework 'sh_exec_tool' to operate on streams. + (nop_free): New function. + (sh_exec_tool): Express this in terms of 'sh_exec_tool_stream'. + * common/sh-exectool.h (sh_exec_tool_stream): New prototype. + + common: Add header file and build the new code. + * common/Makefile.am (common_sources): Add new files. + * common/sh-exectool.h: New file. + +2015-12-04 Werner Koch + + common: Add code to execute a helper. + * common/sh-exectool.c: New file. + + Release 2.1.10. + +2015-12-04 NIIBE Yutaka + + po: Japanese translation. + +2015-12-04 Werner Koch + + speedo,w32: Improve installer. + * build-aux/speedo/w32/inst.nsi (SEC_gnupg): Install dirmngr.conf and + distsigkey.gpg. + (un.gnupglast): Stop dirmngr. + + gpg: Do not pre-check keys given on the command line. + * g10/keydb.h (PK_LIST_ENCRYPT_TO, PK_LIST_HIDDEN, PK_LIST_CONFIG) + (PK_LIST_SHIFT): New. + * g10/pkclist.c (build_pk_list): Use them here. + * g10/gpg.c (check_user_ids, main): Ditto. + + * g10/gpg.c (main): Set PK_LIST_CONFIG for REMUSR and LOCUSR. + (check_user_ids): Skip check for command line specified options. + + dirmngr: Add command to print the resolver version. + * dirmngr/server.c (cmd_getinfo): Add sub-command "dnsinfo". + + gpg: Allow "help" as value for --tofu-policy. + * g10/gpg.c (parse_tofu_policy): Add keyword "help". + (parse_tofu_db_format): Ditto. + + Do not translate messages printed with log_debug. + * common/asshelp.c (start_new_gpg_agent): Do not i18n string. + (start_new_dirmngr): Ditto. + * g10/mainproc.c (proc_encrypted): Ditto. Print only if debug is + enabled. + +2015-12-04 NIIBE Yutaka + + scd: Fix for removing the prefix. + * scd/app-openopg.c (do_decipher): Fix the condition. + + scd: Simplify saving application context. + * scd/app.c (lock_table): Remove LAST_APP field. + (lock_reader, app_dump_state, application_notify_card_reset) + (release_application): Follow the change. + (check_conflict): New. + (check_application_conflict): Lock the slot and call check_conflict. + (select_application): Call check_conflict and not use LAST_APP. + + scd: More fix for Curve25519 prefix handling. + * scd/app-openpgp.c (do_decipher): Handle trancated cipher text. + Also fix xfree bug introduced. + +2015-12-03 Werner Koch + + scd: Another fix for Curve25519 prefix handling. + * scd/app-openpgp.c (do_decipher): Check 0x02 also for 16+1 byte long + INDATA. + (do_decipher): Fix integer arithmetic in void pointer. + (do_decipher): Add missing memcpy. + + build: Avoid dependecy problems in "make distcheck". + * doc/Makefile.am (gnupg.texi): Depend on defs.inc. + + build: Change how caller provided CFLAGS are used by configure. + * configure.ac: Append instead of prepend caller provided CFLAGS. + + gpg: Add variant of 'key "%s" not found: %s' error message. + * g10/gpg.c (check_user_ids): Change error message. + * g10/delkey.c (do_delete_key): Ditto. + + gpg: Make keyidlist more robust in case of errors. + * g10/keyserver.c (keyidlist): Clear *KLIST on error. + + gpg: Take care of keydb_new returning NULL. + * g10/keydb.c (keydb_new): Print an error message if needed. Also use + xtrycalloc because we return an error anyway. + * g10/delkey.c (do_delete_key): Handle error retruned by keydb_new. + * g10/export.c (do_export_stream): Ditto. + * g10/getkey.c (get_pubkey): Ditto. + (get_pubkey_fast): Ditto. + (get_pubkeyblock): Ditto. + (get_seckey): Ditto. + (key_byname): Ditto. + (get_pubkey_byfprint): Ditto. + (get_pubkey_byfprint_fast): Ditto. + (parse_def_secret_key): Ditto. + (have_secret_key_with_kid): Ditto. + * g10/import.c (import_one): Ditto. + (import_revoke_cert): Ditto. + * g10/keyedit.c (keyedit_quick_adduid): Ditto. + * g10/keygen.c (quick_generate_keypair): Ditto. + (do_generate_keypair): Ditto. + * g10/trustdb.c (validate_keys): Ditto. + * g10/keyserver.c (keyidlist): Ditto. + * g10/revoke.c (gen_desig_revoke): Ditto. + (gen_revoke): Ditto. + * g10/gpg.c (check_user_ids): Ditto. + (main): Do not print an error message for keydb_new error. + * g10/keylist.c (list_all): Use actual error code returned by + keydb_new. + + * g10/t-keydb-get-keyblock.c (do_test): Abort on keydb_new error. + * g10/t-keydb.c (do_test): Ditto. + + * g10/keyring.c (keyring_new): Actually return an error so that the + existing keydb_new error checking makes sense for a keyring resource. + (keyring_rebuild_cache): Take care of keyring_new returning an error. + + gpg: Change some error messages. + * g10/getkey.c (parse_def_secret_key): Change error message. Replace + log_debug by log_info. + * g10/gpg.c (check_user_ids): Make function static. Change error + messages. + (main): Change error messages. + * g10/revoke.c (gen_revoke): Ditto. + +2015-12-03 NIIBE Yutaka + + scd: Fix "Conflicting usage" bug. + * scd/apdu.c (apdu_close_reader): Call CLOSE_READER method even if we + got an error from apdu_disconnect. + * scd/app-common.h (no_reuse): Remove. + * scd/app.c (application_notify_card_reset): Deallocate APP here. + (select_application, release_application): Don't use NO_REUSE. + + scd: Fix for Curve25519 prefix handling. + * scd/app-openpgp.c (do_decipher): More condition for AES decipher. + Handle the prefix in cipher text. Always add the prefix in result. + +2015-12-03 Neal H. Walfield + + gpg: Use the matching key if the search description is exact. + * g10/gpg.c (check_user_ids): If the search description is for an + exact match (a keyid or fingerprint that ends in '!'), then use the + matching key, not the primary key. + * tests/openpgp/Makefile.am (TESTS): Add use-exact-key.test. + (priv_keys): Add privkeys/00FE67F28A52A8AA08FFAED20AF832DA916D1985.asc, + privkeys/1DF48228FEFF3EC2481B106E0ACA8C465C662CC5.asc, + privkeys/A2832820DC9F40751BDCD375BB0945BA33EC6B4C.asc, + privkeys/ADE710D74409777B7729A7653373D820F67892E0.asc and + privkeys/CEFC51AF91F68A2904FBFF62C4F075A4785B803F.asc. + (sample_keys): Add + samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc. + * tests/openpgp/privkeys/00FE67F28A52A8AA08FFAED20AF832DA916D1985.asc: + New file. + * tests/openpgp/privkeys/1DF48228FEFF3EC2481B106E0ACA8C465C662CC5.asc: + New file. + * tests/openpgp/privkeys/A2832820DC9F40751BDCD375BB0945BA33EC6B4C.asc: + New file. + * tests/openpgp/privkeys/ADE710D74409777B7729A7653373D820F67892E0.asc: + New file. + * tests/openpgp/privkeys/CEFC51AF91F68A2904FBFF62C4F075A4785B803F.asc: + New file. + * tests/openpgp/samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc: + New file. + * tests/openpgp/use-exact-key.test: New file. + * tests/openpgp/version.test: Install the new private keys. + +2015-12-02 Werner Koch + + build: Require at least Libassuan 2.4.1. + * configure.ac (NEED_LIBASSUAN_VERSION): Set to 2.4.1. + * agent/gpg-agent.c (create_server_socket): Remove check for + libassuan >= 2.3.0 and >= 2.1.4. + (main): Remove check for libassuan >= 2.1.4. + * scd/scdaemon.c (create_server_socket): Remove check for + libassuan >= 2.1.4. + * dirmngr/dirmngr.c (set_tor_mode): Remove check for + libassuan >= 2.3.0. + * dirmngr/http.c (http_raw_connect, send_request): Remove checks for + libassuan >= 2.3.0. + +2015-12-02 Neal H. Walfield + + gpg: Improve documentation. + * g10/tofu.c (initdb): Improve documentation. + + gpg: Fix type mismatch resulting in a buffer overflow. + * g10/tofu.c (record_binding): Change policy_old's type from an enum + tofu_policy to a long: this variable is passed by reference and a long + is expected. + +2015-12-02 Werner Koch + + dirmngr: Switch to an onion address if Tor is running. + * dirmngr/dirmngr.h (opt): Turn field 'keyserver' into an strlist. + * dirmngr/dirmngr.c (parse_rereadable_options): Allow multiple + --keyserver options. + * dirmngr/server.c (server_local_s): Add field 'tor_state'. + (release_uri_item_list): New. + (release_ctrl_keyservers): Use it. + (start_command_handler): Release list of keyservers. + (is_tor_running): New. + (cmd_getinfo): Re-implement "tor" subcommand using new fucntion. + (ensure_keyserver): Rewrite. + * g10/dirmngr-conf.skel: Add two keyserver options. + + http: Enhance parser to detect .onion addresses. + * dirmngr/http.h (parsed_uri_s): Add flag 'onion'. + * dirmngr/http.c (do_parse_uri): Set that flag. + * dirmngr/t-http.c (main): Print flags. + +2015-12-02 Neal H. Walfield + + common,gpg: Fix processing of search descriptions ending in '!'. + * g10/gpg.c (check_user_ids): If the search description describes a + keyid or fingerprint and ends in a '!', include the '!' in the + rewritten description. + * common/userids.c (classify_user_id): Accept keyids and fingerprints + ending in '!'. + +2015-12-01 Justus Winter + + dirmngr: Improve error handling. + * dirmngr/dns-stuff.c (getsrv): Avoid looking at 'header' before + checking for errors, but silently ignore errors when looking up SRV + records. + +2015-12-01 Werner Koch + + build: Let configure show the the status of Tor support. + * configure.ac (show_tor_support): New + +2015-11-30 Werner Koch + + doc: Make make distcheck work again. + * doc/Makefile.am (DISTCLEANFILES): Add gpgkey2ssh.1 + + yat2m: Add keyword @url. + * doc/yat2m.c (proc_texi_cmd): Add keyword @url. + + doc: Build man pages with the same date as the info files. + * doc/Makefile.am (yat2m-stamp): Use option --date. + + yat2m: New option --date. + * doc/yat2m.c (opt_date): new. + (isodatestring): Use it if set. + (main): New option --date. + +2015-11-27 Werner Koch + + gpg: Avoid extra translation strings. + * g10/keyedit.c (menu_expire): Use only one prompt. + + kbx: Include gpg-error prior to mischelp.h. + * kbx/keybox-init.c: Change order of includes. + + gpg,w32: Fix a format string error. + * g10/keyring.c (keyring_search): Fix format string for off_t. + + Silence compiler warnings related to not using assuan_fd_t. + * common/call-gpg.c (start_gpg): Use assuan_fd_t. Note that the + declaration was already fixed by a previous change. + * dirmngr/server.c (cmd_getinfo): Use assuan_fd_t. + + Avoid incompatible pointer assignment warnings on Windows. + * common/logging.c (fun_writer): Use gpgrt_ssize_t instead of ssize_t. + * dirmngr/server.c (data_line_cookie_write): Ditto. + * sm/certdump.c (format_name_writer): Ditto. + * sm/server.c (data_line_cookie_write): Ditto. + * dirmngr/http.c (cookie_read, cookie_write): Ditto. + + dirmngr: Avoid a declarations after statements. + * tools/gpgtar.c (parse_arguments): Use a block for a local varibale + definition. + + dirmngr: Avoid casting away a const from an char**. + * dirmngr/ldap.c (start_cert_fetch_ldap): Do not use pointers from + global variables. + + dirmngr: Allow testing for a running Tor via "getinfo tor". + * dirmngr/server.c (cmd_getinfo): Print an S line if Tor is not + running. + +2015-11-26 Werner Koch + + g13: Fix commit 1a045b13. + * g13/g13.c (main): Use existsing function. + + common: Fix off-by-one access in the new format_text. + * common/stringhelp.c (format_text): Use existsing fucntion to trim + trailing spaces. Fix off-by-one access. + + dirmngr: Improve output of "getinfo tor". + * dirmngr/server.c (cmd_getinfo): Print a message along with OK. + + dirmngr: Let Libassuan employ nPth wrappers for connect. + * dirmngr/http.c (my_unprotect, my_protect): Remove. + (connect_server): Do not use these wrappers. + +2015-11-26 Justus Winter + + tools/gpgtar: Add '--dry-run'. + * tools/gpgtar-extract.c (extract_{regular,directory}): Honor + '--dry-run'. + * tools/gpgtar.c (enum cmd_and_opt_values): New value. + (opts): Add '--dry-run'. + (parse_arguments): Handle '--dry-run'. + * tools/gpgtar.h (opt): Add field 'dry_run'. + + tools/gpgtar: Handle '--gpg-args'. + * tools/gpgtar-create.c (gpgtar_create): Use given arguments. + * tools/gpgtar-extract.c (gpgtar_extract): Likewise. + * tools/gpgtar-list.c (gpgtar_list): Likewise. + * tools/gpgtar.c (enum cmd_and_opt_values): New value. + (opts): Add 'gpg-args'. + (parse_arguments): Handle arguments. + * tools/gpgtar.h (opt): Add field 'gpg_arguments'. + * tests/openpgp/gpgtar.test: Simplify accordingly. + + common: Make the GPG arguments configurable in call-gpg. + * common/call-gpg.c (start_gpg): Add parameter 'gpg_arguments'. + (_gpg_encrypt, gpg_encrypt_blob, gpg_encrypt_stream): Likewise. + (_gpg_decrypt, gpg_decrypt_blob, gpg_decrypt_stream): Likewise. + * common/call-gpg.h: Adapt prototypes. + * g13/create.c (encrypt_keyblob): Adapt callsite. + * g13/g13-common.h (opt): Add field 'gpg_arguments'. + * g13/g13.c (main): Construct default arguments. + * g13/mount.c (decrypt_keyblob): Adapt callsite. + * tools/gpgtar-create.c (gpgtar_create): Likewise. + * tools/gpgtar-extract.c (gpgtar_extract): Likewise. + * tools/gpgtar-list.c (gpgtar_list): Likewise. + + tools/gpgtar: Handle '--tar-args' for compatibility with gpg-zip. + * tools/gpgtar.c (enum cmd_and_opt_values): New value. + (opts): Add new group for tar options, rearrange a little, add + '--tar-args'. + (tar_opts): New variable. + (shell_parse_stringlist): New function. + (shell_parse_argv): Likewise. + (parse_arguments): Add option argument, handle '--tar-args'. + (main): Fix invokation of 'parse_arguments'. + * tests/openpgp/gpgtar.test: Simplify decryption. + + tools/gpgtar: Rework argument parsing. + * tools/gpgtar.c (main): Move argument parsing into its own function. + +2015-11-25 Justus Winter + + tests: Add tests for gpgtar and gpg-zip. + * tests/openpgp/Makefile.am (TESTS): Add new file. + * tests/openpgp/gpgtar.test: New file. + + tools/gpgtar: Handle '--directory' argument. + * tools/gpgtar-extract.c (gpgtar_extract): Only generate a directory + name if none is given via arguments. + * tools/gpgtar.c (enum cmd_and_opt_values): New constant. + (opts): Add argument. + (main): Parse argument. + * tools/gpgtar.h (opt): New field 'directory'. + + tools/gpgtar: Handle '--gpg' argument. + * tools/gpgtar-create.c (gpgtar_create): Use given gpg program. + * tools/gpgtar-extract.c (gpgtar_extract): Likewise. + * tools/gpgtar-list.c (gpgtar_list): Likewise. + * tools/gpgtar.c (enum cmd_and_opt_values): New constant. + (opts): Add argument. + (main): Handle argument. + * tools/gpgtar.h (opt): Add field 'gpg_program'. + + tools/gpgtar: Improve error handling. + * tools/gpgtar-create.c (gpgtar_create): Return an error code, fix + error handling. + * tools/gpgtar-extract.c (gpgtar_extract): Likewise. + * tools/gpgtar-list.c (read_header): Return an error code. + (gpgtar_list): Return an error code, fix error handling. + (gpgtar_read_header): Return an error code. + * tools/gpgtar.c: Add missing include. + (main): Print an generic error message if a command failed and no + error has been printed yet. + * tools/gpgtar.h (gpgtar_{create,extract,list,read_header}): Fix the + prototypes accordingly. + + tools: Add encryption and decryption support to gpgtar. + * tools/Makefile.am: Amend CFLAGS and LDADD. + * tools/gpgtar-create.c (gpgtar_create): Add encrypt flag and encrypt + stream if requested. + * tools/gpgtar-extract.c (gpgtar_extract): Likewise for decryption. + * tools/gpgtar-list.c (gpgtar_list): Likewise. + * tools/gpgtar.c (main): Initialize npth and assuan. Parse recipient + and local user, and note which flags are currently ignored. Adapt + calls to gpgtar_list and friends. + (tar_and_encrypt): Drop stub function and prototype. + (decrypt_and_untar): Likewise. + (decrypt_and_list): Likewise. + * tools/gpgtar.h (gpgtar_{create,extract,list}): Add encryption or + decryption argument. + + common: Add stream interface to call-pgp. + * common/call-gpg.c (struct writer_thread_parms): Add field 'stream'. + (writer_thread_main): Support reading from a stream. + (start_writer): Add stream argument. + (struct reader_thread_parms): Add field 'stream'. + (reader_thread_main): Support writing to a stream. + (start_reader): Add stream argument. + (_gpg_encrypt): Add stream api. + (gpg_encrypt_blob): Adapt accordingly. + (gpg_encrypt_stream): New function. + (_gpg_decrypt): Add stream api. + (gpg_decrypt_blob): Adapt accordingly. + (gpg_decrypt_stream): New function. + * common/call-gpg.h (gpg_encrypt_stream): New prototype. + (gpg_decrypt_stream): Likewise. + + common: Refactor the call-gpg code. + * common/call-gpg.c (gpg_{en,de}crypt_blob): Move most of the code + into two new functions, _gpg_encrypt and _gpg_decrypt. + + g13: Move 'call-gpg.c' to common. + * common/Makefile.am (common_sources): Add files. + * g13/call-gpg.c: Move to 'common' and adapt slightly. Add a + parameter to let callees override the gpg program to execute. + * g13/call-gpg.h: Likewise. + * g13/Makefile.am (g13_SOURCES): Drop files. + * g13/create.c (encrypt_keyblob): Hand in the gpg program to execute. + * g13/mount.c (decrypt_keyblob): Likewise. + +2015-11-24 Neal H. Walfield + + gpg: When comparing keyids, use the keyid, not the fingerprint's suffix. + * g10/keyedit.c (menu_select_key): Use spacep and hexdigitp instead of + inline tests. Don't compare P to the suffix of the fingerprint. If P + appears to be a keyid, do an exact compare against the keyid. If it + appears to be a fingerprint, do an exact compare against the + fingerprint. + +2015-11-23 Neal H. Walfield + + gpg: Reflow long texts. + * common/stringhelp.c (format_text): New function. + * common/t-stringhelp.c (stresc): New function. + (test_format_text): New function. Test format_text. + * g10/tofu.c (get_trust): Use format_text to reflow long texts. + (show_statistics): Likewise. + + common: Extend utf8_charcount to include the string's length. + * common/stringhelp.c (utf8_charcount): Take additional parameter, + len. Process at most LEN bytes. + +2015-11-23 Justus Winter + + dirmngr: Fix http lookups when libadns is used. + * dirmngr/dns-stuff.c (resolve_name_adns): Fill in the port. + + dirmngr: Fix SRV record lookups when using the system resolver. + * dirmngr/dns-stuff.c (getsrv): Fix error handling. + + dirmngr: Honor ports specified in SRV records. + * dirmngr/ks-engine-hkp.c (struct hostinfo_s): New field 'port'. + (create_new_hostinfo): Initialize 'port'. + (add_host): Add host parameter and update the hosttable entry. + (map_host): Return port if known, adjust calls to 'add_host'. + (make_host_part): Let 'map_host' specify the port if known. + + dirmngr: Support hkp server pools using SRV records. + * dirmngr/ks-engine-hkp.c (map_host): Handle SRV records. + + dirmngr: Refactor 'map_host'. + * dirmngr/ks-engine-hkp.c (add_host): New function. + (map_host): Use the new function. + + dirmngr: Fix pool detection. + * dirmngr/ks-engine-hkp (arecords_is_pool): Fix counting IP addresses. + + dirmngr: Refactor 'map_host'. + * dirmngr/ks-engine-hkp.c (arecords_is_pool): New function. + (map_host): Use the new function. + + dirmngr: Start dirmngr on demand. + * common/asshelp.h: Include 'util.h'. + * dirmngr/dirmngr-client.c (main): Use 'start_new_dirmngr' to connect + to the dirmngr. + (start_dirmngr): Drop now unused declaration and function. + +2015-11-23 Neal H. Walfield + + gpg: If sqlite is not available, don't build things depending on it. + * configure.ac: Define the automake conditional SQLITE3. + * tests/openpgp/Makefile.am (TESTS): Move the sqlite3 dependent tests + to... + (sqlite3_dependent_tests): ... this new variable. If SQLITE3 is not + defined, then clear this variable. + + gpg: Allow updating the expiration time of multiple subkeys at once. + * g10/keyedit.c (menu_expire): Allow updating the expiration time of + multiple subkeys at once. + + gpg: Don't crash if key is not passed an argument. + * g10/keyedit.c (menu_select_key): Don't crash if P is NULL. + +2015-11-20 Neal H. Walfield + + gpg: Fail if the search description passed to --gen-revoke is ambiguous. + * g10/revoke.c (gen_revoke): Error out if the search description is + ambiguous. + + gpg: Refactor print_seckey_info. + * g10/keylist.c (print_seckey_info): Break formatting functionality + into... + (format_seckey_info): ... this new function. + + gpg: Improve an error message. + * g10/revoke.c (gen_revoke): Provide a more descriptive error message + if searching for a key fails. + +2015-11-19 Justus Winter + + dirmngr: Improve error handling. + * dirmngr/crlcache.c (crl_cache_cert_isvalid): Add missing break. + + dirmngr: Fix memory leak. + * dirmngr/ldap.c (start_cert_fetch_ldap): Avoid leaking all malloc'ed + arguments. + + agent: Improve error handling. + * agent/trustlist.c (istrusted_internal): Initialize 'err'. + + common: Avoid undefined behavior. + * common/iobuf.c (iobuf_esopen): Initialize 'len' as 'file_es_filter' + will make use of it. + + g10: Avoid undefined behavior. + * g10/trust.c (clean_one_uid): Avoid a computation involving an + uninitialized value. + + scd: Improve error handling. + * scd/app-openpgp.c (get_public_key): Improve error handling. + +2015-11-18 Justus Winter + + dirmngr: Gracefully handle premature termination of TLS streams. + * dirmngr/http.c (close_tls_session): New function. + (session_unref): Use the new function to close the TLS stream. + (cookie_read): If the stream terminated prematurely, close it and + return a short read. + +2015-11-17 Neal H. Walfield + Michael Mönch + + tools: Fix option parsing for gpg-zip. + * tools/gpg-zip.in: Correctly set GPG when --gpg is specified. + Correctly set TAR when --tar is specified. Pass TAR_ARGS to tar. + +2015-11-17 Neal H. Walfield + + gpg: Allow selecting subkeys using a keyid. + * g10/keyedit.c (menu_select_key): Take an additional argument, p. + Update callers. If P is a hex string, then assume that P is a key id + or fingerprint and select subkeys with matching key ids or + fingerprints. + * doc/gpg.texi: Update documentation for the key subcommand. + +2015-11-17 Justus Winter + + dirmngr: Fix specifying keyservers by IP address. + * dirmngr/ks-engine-hkp.c (map_host): Update the original 'hosttable' + entry instead of creating another one. + +2015-11-17 Neal H. Walfield + + gpg: Change keydb_search to not return legacy keys. + * g10/keyring.c (keyring_search): Take new argument, ignore_legacy. + If set, skip any legacy keys. Update callers. + * g10/keydb.c (keydb_search): Skip any legacy keys. + (keydb_search_first): Don't skip legacy keys. Treat them + as an error. + (keydb_search_next): Likewise. + (keydb_search_fpr): Likewise. + * g10/export.c (do_export_stream): Likewise. + * g10/getkey.c (lookup): Likewise. + (have_secret_key_with_kid): Likewise. + * g10/keylist.c (list_all): Likewise. + (keyring_rebuild_cache): Likewise. + * g10/keyserver.c (keyidlist): Likewise. + * g10/trustdb.c (validate_key_list): Likewise. + + gpg: Correctly handle an error. + * g10/keyring.c (keyring_search): If a compare function returns an + error, treat it as an error. + + gpg: Correctly handle keyblocks followed by legacy keys. + * g10/keyring.c (keyring_get_keyblock): If we encounter a legacy + packet after already having some non-legacy packets, then treat the + legacy packet as a keyblock boundary, not as part of the keyblock. + * g10/t-keydb-get-keyblock.c: New file. + * g10/t-keydb-get-keyblock.gpg: New file. + * g10/Makefile.am (EXTRA_DIST): Add t-keydb-get-keyblock.gpg. + (module_tests): Add t-keydb-get-keyblock. + (t_keydb_get_keyblock_SOURCES): New variable. + (t_keydb_get_keyblock_LDADD): Likewise. + + gpg: Make debugging search descriptors easier. + * g10/keydb.c (dump_search_desc): Rename from this... + (keydb_search_desc_dump): ... to this. Only process a single search + descriptor. Improve output. Don't mark as static. Update callers. + + gpg: Add function format_keyid. + * g10/options.h (opt.keyid_format): Add new value KF_DEFAULT. + * g10/keyid.c (format_keyid): New function. + (keystr): Use it. + + gpg: Use a more appropriate error code. + * g10/gpg.c (check_user_ids): Return a more appropriate error code if + a user id is ambiguous. + +2015-11-17 Justus Winter + + Fix typos found using codespell. + * agent/cache.c: Fix typos. + * agent/call-pinentry.c: Likewise. + * agent/call-scd.c: Likewise. + * agent/command-ssh.c: Likewise. + * agent/command.c: Likewise. + * agent/divert-scd.c: Likewise. + * agent/findkey.c: Likewise. + * agent/gpg-agent.c: Likewise. + * agent/w32main.c: Likewise. + * common/argparse.c: Likewise. + * common/audit.c: Likewise. + * common/audit.h: Likewise. + * common/convert.c: Likewise. + * common/dotlock.c: Likewise. + * common/exechelp-posix.c: Likewise. + * common/exechelp-w32.c: Likewise. + * common/exechelp-w32ce.c: Likewise. + * common/exechelp.h: Likewise. + * common/helpfile.c: Likewise. + * common/i18n.h: Likewise. + * common/iobuf.c: Likewise. + * common/iobuf.h: Likewise. + * common/localename.c: Likewise. + * common/logging.c: Likewise. + * common/openpgp-oid.c: Likewise. + * common/session-env.c: Likewise. + * common/sexputil.c: Likewise. + * common/sysutils.c: Likewise. + * common/t-sexputil.c: Likewise. + * common/ttyio.c: Likewise. + * common/util.h: Likewise. + * dirmngr/cdblib.c: Likewise. + * dirmngr/certcache.c: Likewise. + * dirmngr/crlcache.c: Likewise. + * dirmngr/dirmngr-client.c: Likewise. + * dirmngr/dirmngr.c: Likewise. + * dirmngr/dirmngr_ldap.c: Likewise. + * dirmngr/dns-stuff.c: Likewise. + * dirmngr/http.c: Likewise. + * dirmngr/ks-engine-hkp.c: Likewise. + * dirmngr/ks-engine-ldap.c: Likewise. + * dirmngr/ldap-wrapper.c: Likewise. + * dirmngr/ldap.c: Likewise. + * dirmngr/misc.c: Likewise. + * dirmngr/ocsp.c: Likewise. + * dirmngr/validate.c: Likewise. + * g10/encrypt.c: Likewise. + * g10/getkey.c: Likewise. + * g10/gpg.c: Likewise. + * g10/gpgv.c: Likewise. + * g10/import.c: Likewise. + * g10/keydb.c: Likewise. + * g10/keydb.h: Likewise. + * g10/keygen.c: Likewise. + * g10/keyid.c: Likewise. + * g10/keylist.c: Likewise. + * g10/keyring.c: Likewise. + * g10/mainproc.c: Likewise. + * g10/misc.c: Likewise. + * g10/options.h: Likewise. + * g10/packet.h: Likewise. + * g10/parse-packet.c: Likewise. + * g10/pkclist.c: Likewise. + * g10/pkglue.c: Likewise. + * g10/plaintext.c: Likewise. + * g10/server.c: Likewise. + * g10/sig-check.c: Likewise. + * g10/sqlite.c: Likewise. + * g10/tdbio.c: Likewise. + * g10/test-stubs.c: Likewise. + * g10/tofu.c: Likewise. + * g10/trust.c: Likewise. + * g10/trustdb.c: Likewise. + * g13/create.c: Likewise. + * g13/mountinfo.c: Likewise. + * kbx/keybox-blob.c: Likewise. + * kbx/keybox-file.c: Likewise. + * kbx/keybox-init.c: Likewise. + * kbx/keybox-search-desc.h: Likewise. + * kbx/keybox-search.c: Likewise. + * kbx/keybox-update.c: Likewise. + * scd/apdu.c: Likewise. + * scd/app-openpgp.c: Likewise. + * scd/app-p15.c: Likewise. + * scd/app.c: Likewise. + * scd/ccid-driver.c: Likewise. + * scd/command.c: Likewise. + * scd/iso7816.c: Likewise. + * sm/base64.c: Likewise. + * sm/call-agent.c: Likewise. + * sm/call-dirmngr.c: Likewise. + * sm/certchain.c: Likewise. + * sm/gpgsm.c: Likewise. + * sm/import.c: Likewise. + * sm/keydb.c: Likewise. + * sm/minip12.c: Likewise. + * sm/qualified.c: Likewise. + * sm/server.c: Likewise. + * tools/gpg-check-pattern.c: Likewise. + * tools/gpgconf-comp.c: Likewise. + * tools/gpgkey2ssh.c: Likewise. + * tools/gpgparsemail.c: Likewise. + * tools/gpgtar.c: Likewise. + * tools/rfc822parse.c: Likewise. + * tools/symcryptrun.c: Likewise. + +2015-11-16 Neal H. Walfield + + gpg: Fix error checking and improve error reporting. + * g10/gpg.c (check_user_ids): Differentiate between a second result + and an error. If the key specification is ambiguous or an error + occurs, set RC appropriately. + +2015-11-14 Werner Koch + + gpg: Use only one fingerprint formatting function. + * g10/gpg.h (MAX_FORMATTED_FINGERPRINT_LEN): New. + * g10/keyid.c (hexfingerprint): Add optional args BUFFER and BUFLEN. + Change all callers. + (format_hexfingerprint): New. + * g10/keylist.c (print_fingerprint): Change to use hexfingerprint. + * g10/tofu.c (fingerprint_format): Remove. Replace calls by + format_hexfingerprint. + +2015-11-13 Werner Koch + + gpg: Simplify the tofu interface by using the public key packet. + * g10/tofu.c (fingerprint_str): Remove. + (tofu_register): Take a public key instead of a fingerprint as arg. + Use hexfingerprint() to get a fpr from the PK. + (tofu_get_validity): Ditto. + (tofu_set_policy, tofu_get_policy): Simplify by using hexfingerprint. + * g10/trustdb.c (tdb_get_validity_core): Pass the primary key PK to + instead of the fingerprint to the tofu functions. + + gpg: Make trusted-key override for Tofu robust against swapped tofu.db. + * g10/tofu.c (get_trust): For the UTK check lookup the key by + fingerprint. + + gpg: Fix regression in --locate-keys (in 2.1.9). + * g10/getkey.c (getkey_ctx_s): Add field "extra_list". + (get_pubkey_byname): Store strings in the context. + (getkey_end): Free EXTRA_LIST. + +2015-11-12 Werner Koch + + gpg: Print a new EXPORTED status line. + * common/status.h (STATUS_EXPORTED): New. + * g10/export.c (print_status_exported): New. + (do_export_stream): Call that function. + + gpg: Print export statistics to the status-fd. + * common/status.h (STATUS_EXPORT_RES): New. + * g10/main.h (export_stats_t): New. + * g10/export.c (export_stats_s): New. + (export_new_stats, export_release_stats): New. + (export_print_stats): New. + (export_pubkeys, export_seckeys, export_secsubkeys) + (export_pubkey_buffer, do_export): Add arg "stats". + (do_export_stream): Add arg stats and update it. + * g10/gpg.c (main) : Create, + pass, and print a stats object to the export function calls. + + * g10/export.c (export_pubkeys_stream): Remove unused function. + + dirmngr: Do not block during ADNS calls. + * dirmngr/dns-stuff.c: Include npth.h + (my_unprotect, my_protect): New wrapper. + (resolve_name_adns): Put unprotect/protect around adns calls. + (get_dns_cert): Ditto. + (getsrv): Ditto. + (get_dns_cname): Ditto. + + dirmngr: New option --nameserver. + * dirmngr/dirmngr.c (oNameServer): New. + (opts): Add --nameserver. + (parse_rereadable_options): Act upon oNameServer. + * dirmngr/dns-stuff.c (DEFAULT_NAMESERVER): New. + (tor_nameserver): New. + (set_dns_nameserver): New. + (my_adns_init): Make name server configurable. + +2015-11-11 Neal H. Walfield + + gpg: Fix cache consistency problem. + g10/keyring.c (keyring_search): Only mark the cache as completely + filled if we start the scan from the beginning of the keyring. + +2015-11-10 Neal H. Walfield + + gpg: Default to the the PGP trust model. + * g10/trustdb.c (init_trustdb): If we can't read the trust model from + the trust DB, default to TM_PGP, not TM_TOFU_PGP. + + gpg: Default to the flat TOFU DB format. + * g10/tofu.c (opendbs): If the TOFU DB format is set to auto and there + is no TOFU DB, default to the flat format. + +2015-11-09 Werner Koch + + dirmngr: Change to new ADNS Tor mode init scheme. + * dirmngr/dns-stuff.c (tor_credentials): New. + (enable_dns_tormode): Add arg new_circuit and update tor_credentials. + (my_adns_init): Rework to set Tor mode using a config file options and + always use credentials. + * dirmngr/server.c (cmd_dns_cert): Improve error message. + * dirmngr/t-dns-stuff.c (main): Add option --new-circuit. + + dirmngr: Improve detection of ADNS. + * configure.ac (HAVE_ADNS_FREE): New ac_define. + +2015-11-09 NIIBE Yutaka + + scd: Add reder information to --card-status. + * g10/call-agent.h, g10/call-agent.c (agent_release_card_info) + g10/card-util.c (card_status): Add READER. + * scd/apdu.c (close_ccid_reader, open_ccid_reader): Handle RDRNAME. + (apdu_get_reader_name): New. + * scd/ccid-driver.c (ccid_open_reader): Add argument to RDRNAME_P. + * scd/command.c (cmd_learn): Return READER information. + +2015-11-06 Werner Koch + + gpg: Avoid new strings. + * g10/decrypt-data.c (decrypt_data): Use already translated strings. + + common: Fix commit f99830b. + * common/userids.c (classify_user_id): Avoid underflow. Use spacep to + also trim tabs. + +2015-11-06 Neal H. Walfield + + gpg: Fix formatting string. + * g10/decrypt-data.c (decrypt_data): Fix formatting string. + + gpg: Add new option --only-sign-text-ids. + * g10/options.h (opt): Add field only_sign_text_ids. + * g10/gpg.c (enum cmd_and_opt_values): Add value oOnlySignTextIDs. + (opts): Handle oOnlySignTextIDs. + (main): Likewise. + * g10/keyedit.c (sign_uids): If OPT.ONLY_SIGN_TEXT_IDS is set, don't + select non-text based IDs automatically. + (keyedit_menu): Adapt the prompt asking to sign all user ids according + to OPT.ONLY_SIGN_TEXT_IDS. + * doc/gpg.texi: Document the new option --only-sign-text-ids. + + common: When classifying keyids and fingerprints, reject trailing junk. + * common/userids.c (classify_user_id): Trim any trailing whitespace. + Before assuming that a hexstring corresponds to a key id or + fingerprint, make sure that it is NUL terminated. + + gpg: Check for ambiguous or non-matching key specs. + * g10/gpg.c (check_user_ids): New function. + (main): Check that any user id specifications passed to --local-user + and --remote-user correspond to exactly 1 user. Check that any user + id specifications passed to --default-key correspond to at most 1 + user. Warn if any user id specifications passed to --local-user or + --default-user are possible ambiguous (are not specified by long keyid + or fingerprint). + * g10/getkey.c (parse_def_secret_key): Don't warn about possible + ambiguous key descriptions here. + + common: Add new function strlist_rev. + * common/strlist.c (strlist_rev): New function. + * common/t-strlist.c: New file. + * common/Makefile.am (common_sources): Add strlist.c and strlist.h. + (module_tests): Add t-strlist. + (t_strlist_LDADD): New variable. + + common: Include required, but not included headers in t-support.h. + * common/t-support.h: Include and . + +2015-11-05 Neal H. Walfield + + gpg: Indicate which characters are invalid. + * g10/keygen.c (ask_user_id): Indicate which characters are invalid. + + gpg: Add support for unwrapping the outer level of encryption. + * g10/decrypt-data.c (decrypt_data): If OPT.UNWRAP_ENCRYPTION is set, + copy the data to the output file instead of continuing to process it. + * g10/gpg.c (enum cmd_and_opt_values): Add new value oUnwrap. + (opts): Handle oUnwrap. + (main): Likewise. + * g10/options.h (opt): Add field unwrap_encryption. + * g10/plaintext.c (handle_plaintext): Break the output file selection + functionality into ... + (get_output_file): ... this new function. + + common: Add a function for copying data from one iobuf to another. + * common/iobuf.c (iobuf_copy): New function. + + doc: Note that gpgkey2ssh is deprecated. + * doc/tools.texi (gpgkey2ssh): Note that gpgkey2ssh is deprecated. + + tools: Fix gpgkey2ssh's most gratuitous errors. Use gpg2, not gpg. + * tools/gpgkey2ssh.c (main): Add support for --help. Replace the most + gratuitous asserts with error messages. Invoke gpg2, not gpg. + +2015-11-05 Neal H. Walfield + Daniel Kahn Gillmor + + doc: Add documentation for gpgkey2ssh. + * doc/tools.texi: Add documentation for gpgkey2ssh. + +2015-11-04 Neal H. Walfield + + gpg: Print a better error message for --multifile --sign --encrypt. + * g10/gpg.c (main): Print a better error message for --multifile + --sign --encrypt. + + gpg: Add --encrypt-to-default-key. + * g10/getkey.c (parse_def_secret_key): Drop the static qualifier and + export the function. + * g10/gpg.c (enum cmd_and_opt_values): Add value oEncryptToDefaultKey. + (opts): Handle oEncryptToDefaultKey. + (main): Likewise. + * g10/options.h (opt): Add field encrypt_to_default_key. + + gpg: Allow multiple --default-key options. Take the last available key. + * g10/getkey.c (parse_def_secret_key): New function. + (get_seckey_default): Add parameter ctrl. Update callers. Use + parse_def_secret_key to get the default secret key, if any. + (getkey_byname): Likewise. + (enum_secret_keys): Likewise. + * g10/options.h (opt): Change def_secret_key's type from a char * to a + strlist_t. + * g10/gpg.c (main): When processing --default-key, add the key to + OPT.DEF_SECRET_KEY. + * g10/gpgv.c (get_session_key): Add parameter ctrl. Update callers. + * g10/mainproc.c (proc_pubkey_enc): Likewise. + (do_proc_packets): Likewise. + * g10/pkclist.c (default_recipient): Likewise. + * g10/pubkey-enc.c (get_session_key): Likewise. + * g10/sign.c (clearsign_file): Likewise. + (sign_symencrypt_file): Likewise. + * g10/skclist.c (build_sk_list): Likewise. + * g10/test-stubs.c (get_session_key): Likewise. + +2015-11-04 NIIBE Yutaka + + scd: Fix error handling with libusb-compat library. + * scd/ccid-driver.c (bulk_out): Use LIBUSB_ERRNO_NO_SUCH_DEVICE. + + scd: fix change_keyattr. + * scd/app-openpgp.c (change_keyattr_from_string): Fix parsing. + +2015-11-03 Werner Koch + + gpg: Change out of core error message. + * g10/tofu.c (fingerprint_str): Die with the error code returned by + the failed function. + (time_ago_str): Ditto. Do not make a comma translatable. + (fingerprint_format): Use "%zu" for a size_t. + + gpg: Make translation easier. + * g10/import.c (import_secret_one): Split info string for easier + translation. + +2015-11-03 Neal H. Walfield + + gpg: Also show when the most recently signed message was observed. + * g10/tofu.c (show_statistics): Also show when the most recently + signed message was observed. + + gpg: Split a utility function out of a large function. + * g10/tofu.c (show_statistics): Break the time delta to string code + into... + (time_ago_str): ... this new function. + + gpg: Fix message formatting. + * g10/tofu.c (get_trust): Fix message formatting. + + gpg: Don't store formatting fingerprints in the TOFU DB. + * g10/tofu.c (fingerprint_pp): Split this function into... + (fingerprint_str): ... this function... + (fingerprint_format): ... and this function. + (record_binding): Store the unformatted fingerprint in the DB. Only + use the formatting fingerprint when displaying a message to the user. + (get_trust): Likewise. + (show_statistics): Likewise. + (tofu_register): Likewise. + (tofu_get_validity): Likewise. + (tofu_set_policy): Likewise. + (tofu_get_policy): Likewise. + +2015-11-02 NIIBE Yutaka + + g10: notify a user when importing stub is skipped. + * g10/import.c (transfer_secret_keys): Return GPG_ERR_NOT_PROCESSED + when stub_key_skipped. + (import_secret_one): Notify a user, suggesting --card-status. + +2015-10-31 Neal H. Walfield + + gpg: Consider newlines to be whitespace in an SQL statement. + * g10/sqlite.c (sqlite3_stepx): When making sure that there is no + second SQL statement, ignore newlines. + +2015-10-30 Werner Koch + + common: Improve t-zb32 to be used for manual encoding. + * common/t-support.h (no_exit_on_fail, errcount): New. + (fail): Bump errcount. + * common/t-zb32.c (main): Add options to allow manual use. + + common: Add separate header for zb32.c. + * common/util.h (zb32_encode): Move prototype to ... + * common/zb32.h: new. Include this for all callers of zb32_encode. + +2015-10-29 Neal H. Walfield + + gpg: Display the correct error message. + * g10/trustdb.c (validate_keys): If tdbio_update_version_record fails, + RC does not contain the error code. Save the error code in rc2 and + use that. + + gpg: Eliminate a memory leak. + * g10/trustdb.c (validate_key_list): Don't leak the keyblocks on + failure. + + gpg: Remove unused prototype. + g10/keyring.h (keyring_locate_writable): Remove unused prototype. + + gpg: Eliminate a memory leak. + * g10/gpg.c (main): Don't leak OPT.DEF_RECIPIENT. + + gpg: Fix keyring support. + * g10/keydb.c (keydb_rebuild_caches): Only mark the cached as prepared + if it is actually prepared, which it only is if the resource is a + keybox. + + gpg: Change sqlite3_stepx to pass the sqlite3_stmt * to the callback. + * g10/sqlite.h (enum sqlite_arg_type): Add SQLITE_ARG_BLOB. + (sqlite3_stepx_callback): New declaration. + (sqlite3_stepx): Change the callback's type to sqlite3_stepx_callback, + which passes an additional parameter, the sqlite3_stmt *. Update + users. + + gpg: Move sqlite helper functions into their own file. + * g10/tofu.c (sqlite3_exec_printf): Move from here... + * g10/sqlite.c (sqlite3_exec_printf): ... to this new file. Don't + mark as static. + * g10/tofu.c (sqlite3_stepx): Move from here... + * g10/sqlite.c (sqlite3_stepx): ... to this new file. Don't + mark as static. + * g10/tofu.c (enum sqlite_arg_type): Move from here... + * g10/sqlite.h (enum sqlite_arg_type): ... to this new file. + +2015-10-29 NIIBE Yutaka + + doc: Don't install gpg-zip.1. + * doc/Makefile.am (myman_pages): Remove gpg-zip.1. + (DISTCLEANFILES): Add gpg-zip.1. + +2015-10-28 Werner Koch + + sm: Allow combination of usage flags --gen-key. + * sm/certreqgen.c (create_request): Re-implement building of the + key-usage extension. + +2015-10-28 Damien Goutte-Gattat + + doc: Document some changed default options. + * doc/gpg.texi: Update the description of some options which are + now enabled by default. + +2015-10-28 Werner Koch + + dirmngr: Fix NULL-deref while loading a CRL. + * dirmngr/crlcache.c (crl_parse_insert): Set error before leaping to + failure. + + dirmngr: Minor cleanup of the SRV RR code. + * dirmngr/dns-stuff.c: Include unistd.h. + (getsrv): Run srand only once. + * dirmngr/t-dns-stuff.c (main): Allow passing another name for --srv + and change output format. + + dirmngr: Add a getaddrinfo wrapper backend using ADNS. + * dirmngr/dns-stuff.c: Replace all use of default_errsource. + (my_adns_init): Move to top. + (resolve_name_adns): New. + (resolve_dns_name) [USE_ADNS]: Divert to new func. + +2015-10-26 Werner Koch + + gpg: Do not call an extra get_validity if no-show-uid-validity is used. + * g10/mainproc.c (check_sig_and_print): Do not call the informational + get_validity if we are not going to use it. + +2015-10-26 Daniel Kahn Gillmor + + gpg: Ensure all weak digest rejection notices are shown. + * g10/main.h: Add rejection_shown flag to each weakhash struct + * g10/misc.c (print_digest_algo_note, additional_weak_digest): Do not + treat MD5 separately; (print_digest_rejected_note): Use + weakhash.rejection_shown instead of static shown. + * g10/options.h (opt): Change from additional_weak_digests to + weak_digests. + * g10/sig-check.c: Do not treat MD5 separately. + * g10/gpg.c (main): Explicitly set MD5 as weak. + * g10/gpgv.c (main): Explicitly set MD5 as weak. + +2015-10-26 Werner Koch + + w32: Make it build again if Tofu support is not available. + * g10/keylist.c (public_key_list) [!USE_TOFU]: Do not call tofu + functions. + + dirmngr: Support Tor hidden services. + * dirmngr/dns-stuff.c (is_onion_address): New. + * dirmngr/ks-engine-hkp.c (hostinfo_s): Add field "onion". + (map_host): Special case onion addresses. + (ks_hkp_print_hosttable): Print an 'O' for an onion address. + * dirmngr/http.c (connect_server): Special case onion addresses. + + dirmngr,w32: Remove gethostbyname hack and make it build again. + * dirmngr/http.c (connect_server) [W32]: Remove gethostbyname hack; + we require getaddrinfo anyway. + * dirmngr/dns-stuff.c (AI_ADDRCONFIG): Add replacement if not defined. + (map_eai_to_gpg_error) [W32]: Take care of unsupported codes. + +2015-10-26 Neal H. Walfield + + gpg: Make sure we only have a single SQL statement. + * g10/tofu.c (sqlite3_stepx): Make sure SQL only contains a single SQL + statement. + + gpg: When the TOFU DB is in batch mode, periodically drop the locks. + * g10/tofu.c: Include . + (batch_update_started): New variable. + (begin_transaction): If we've been in batch mode for a while, then + commit any extant batch transactions. + (tofu_begin_batch_update): If we are not in batch mode, initialize + batch_update_started. + +2015-10-25 Werner Koch + + dirmngr: Add workaround for broken getaddrinfo. + * dirmngr/dns-stuff.c (resolve_name_standard): On failure retry by + first resolving the CNAME. + (get_dns_cname): New. + + * dirmngr/t-dns-stuff.c (main): Add option --cname. + + dirmngr: Better handle systems without IPv6 or IPv4. + * dirmngr/dns-stuff.c (resolve_name_standard): Use AI_ADDRCONFIG. + + dirmngr: Replace use of getnameinfo by resolve_dns_addr. + * dirmngr/ks-engine-hkp.c (my_getnameinfo): Remove. + (map_host): Use resolve_dns_addr. + + dirmngr: Implement a getnameinfo wrapper. + * dirmngr/dns-stuff.h (DNS_NUMERICHOST): New. + (DNS_WITHBRACKET): New. + * dirmngr/dns-stuff.c (resolve_name_standard): Factor code out to... + (map_eai_to_gpg_error): new. + (resolve_addr_standard): New. + (resolve_dns_addr): New. + + * dirmngr/ks-engine-hkp.c (is_ip_address): Move to ... + * dirmngr/dns-stuff.c (is_ip_address): here. Add support for non + bracketed v6 addresses. + + * dirmngr/t-dns-stuff.c: Remove header netdb.h. + (main): Add option --bracket. Use resolve_dns_name instead of + getnameinfo. + +2015-10-23 Neal H. Walfield + + gpg: Provide an interface to patch TOFU updates. + * g10/tofu.c (struct db): Rename begin_transaction to savepoint_batch. + Rename end_transaction to savepoint_batch_commit. Update users. + Remove field rollback. Add fields savepoint_inner and + savepoint_inner_commit. Add field batch_update. + (dump_cache): New function. + (batch_update): New variable. + (begin_transaction). New function. + (end_transaction): New function. + (rollback_transaction): New function. + (tofu_begin_batch_update): New function. + (tofu_end_batch_update): New function. + (closedb): End any pending batch transaction. + (closedbs): Assert that none of the DBs have a started batch + transaction if we not in batch mode. + (record_binding): Use the begin_transaction, end_transaction and + rollback_transaction functions instead of including the SQL inline. + Also start a batch mode transaction if we are using the flat format. + (tofu_register): Use the begin_transaction, end_transaction and + rollback_transaction functions instead of including the SQL inline. + * g10/gpgv.c (tofu_begin_batch_update): New function. + (tofu_end_batch_update): New function. + * g10/test-stubs.c (tofu_begin_batch_update): New function. + (tofu_end_batch_update): New function. + + gpg: Cache prepared SQL queries and open DB connections. + * g10/tofu.c: Include . + (prepares_saved) [DEBUG_TOFU_CACHE]: New variable. + (queries) [DEBUG_TOFU_CACHE]: New variable. + (struct db): Add fields prevp, begin_transaction, end_transaction, + rollback, record_binding_get_old_policy, record_binding_update, + record_binding_update2, get_policy_select_policy_and_conflict, + get_trust_bindings_with_this_email, get_trust_gather_other_user_ids, + get_trust_gather_other_keys, register_already_seen, and + register_insert. + [DEBUG_TOFU_CACHE]: Add field hits. + (STRINGIFY): New macro. + (STRINGIFY2): New macro. + (enum sqlite_arg_type): New enum. + (sqlite3_stepx): New function. + (combined_db): Remove variable. + (opendb): Don't cache the combined db. + (struct dbs): New struct. Update users to use this as the head of the + local DB list rather than overloading struct db. + (unlink_db): New function. + (link_db): New function. + (db_cache): New variable. + (db_cache_count): New variable. + (DB_CACHE_ENTRIES): Define. + (getdb): If the dbs specific cache doesn't include the DB, look at + DB_CACHE. Only if that also doesn't include the DB open the + corresponding DB. + (closedb): New function. + (opendbs): Don't open the combined DB. Just return an initialized + struct dbs. + (closedbs): Don't close the dbs specific dbs. Attach them to the + front of DB_CACHE. If DB_CACHE contains more than DB_CACHE_ENTRIES, + close enough dbs from the end of the DB_CACHE list such that DB_CACHE + only contains DB_CACHE_ENTRIES. Don't directly close the dbs, instead + use the new closedb function. + [DEBUG_TOFU_CACHE]: Print out some statistics. + (record_binding): Use sqlite3_stepx instead of sqlite3_exec or + sqlite3_exec_printf. + (get_policy): Likewise. + (get_trust): Likewise. + (tofu_register): Likewise. + + gpg: Return the DBs meta-handle rather than the sqlite3 handle. + * g10/tofu.c (getdb): Return a struct db * instead of an sqlite *. + Update users. + + gpg: Use the proper type. + * g10/options.h: Include "tofu.h". + (opt.tofu_default_policy): Change type to enum tofu_policy. + * g10/gpgv.c (enum tofu_policy): Don't redeclare. + * g10/test-stubs.c (enum tofu_policy): Likewise. + +2015-10-22 Werner Koch + + dirmngr: Implement Tor mode for SRV RRs. + * dirmngr/dns-stuff.c (get_dns_cert): Factor adns init out to... + (my_adns_init): new. + (getsrv)[USE_ADNS]: Use my_adns_init. + (getsrv)[!USE_ADNS]: Return an error if Tor mode is active. + + * dirmngr/t-dns-stuff.c: Add option --use-tor. + + dirmngr: Do not use MAXDNAME. + * dirmngr/dns-stuff.c (getsrv): Replace MAXDNAME. + * dirmngr/dns-stuff.h (MAXDNAME): Remove. + (struct srventry): Use a fixed value instead of MAXDNAME. + * dirmngr/http.c (connect_server): Use DIMof instead of MAXDNAME. + Malloc a helper array. + + Move SRV RR code from common/ to dirmngr/. + * common/srv.c: Merge into dirmngr/dns-stuff.c. Delete file. + * common/srv.h: Merge into dirmngr/dns-stuff.h. Delete file. + * common/Makefile.am (common_sources): Remove srv.c and srv.h. + * g10/keyserver.c: Do not include srv.h. The code using it is anyway + disabled. + * dirmngr/http.c: Remove header srv.h and stubs. + * dirmngr/t-dns-stuff.c: Add option --srv. + +2015-10-21 Werner Koch + + dirmngr: Use the new DNS wrapper for the HTTP module. + * dirmngr/t-http.c (main): Init assuan sockets. + * dirmngr/http.c: Include dns-stuff.h. + (connect_server)[!HAVE_GETADDRINFO]: Remove all code. + (connect_server): Change to use resolve_dns_name. + + dirmngr: Allow use of http.c if USE_NPTH is not defined. + * dirmngr/http.c (send_request): Always set the gnutls pull/push + functions. + (my_npth_read): Rename to ... + (my_gnutls_read) .. this. Use system read if !USE_NPTH. + (my_npth_write): Rename to ... + (my_gnutls_write) .. this. Use system write if !USE_NPTH. + + dirmngr: Check that getaddrinfo is available. + * dirmngr/Makefile.am (t_http_SOURCES): Add dns-stuff.c. + (t_ldap_parse_uri_SOURCES): Ditto. + * dirmngr/dns-stuff.c: Bail out if neither ADNS nor getaddrinfo is + available. + + dirmngr: Use the new DNS wrapper for the HKP engine. + * dirmngr/ks-engine-hkp.c (my_getnameinfo): Change arg type to + dns_addrinfo_t. + (map_host): Replace getaddrinfo by resolve_dns_name. + + dirmngr: Implement a getaddrinfo wrapper. + * dirmngr/dns-stuff.h: Include some header files. + (dns_addinfo_t, dns_addrinfo_s): New. + * dirmngr/dns-stuff.c: Always include DNS related headers. + (free_dns_addrinfo): New. + (resolve_name_standard): New. + (resolve_dns_name): New. + + * dirmngr/t-dns-stuff.c: Include netdb.h. + (main): Keep old default mode with no args but else print outout of + resolve_dns_name. Revamp option parser. + + common: Add more replacement error codes. + * common/util.h (GPG_ERR_SERVER_FAILED): New. + (GPG_ERR_NO_KEY): New. + (GPG_ERR_NO_NAME): New. + +2015-10-21 Neal H. Walfield + + gpg: If the saved trust model is unknown, default to tofu+pgp. + * g10/trustdb.c (init_trustdb): If the saved trust model is unknown, + default to tofu+pgp instead of pgp. + + gpg: Don't accidentally free UTK_LIST. + * g10/trustdb.c (validate_keys): Don't free UTK_LIST. + + gpg: When evaluating trust reg exps, treat tofu+pgp like pgp. + * g10/trustdb.c (validate_one_keyblock): When checking trust regular + expressions, treat the tofu+pgp trust model the same as the pgp trust + model. + + gpg: If a key is ultimate trusted, return that in the tofu model. + * g10/tofu.c (get_trust): If the policy is auto or none, check if the + key is ultimately trusted. If so, return that. + (tofu_register): If the key is ultimately trusted, don't show any + statistics. + (tofu_get_validity): Likewise. + + gpg: Keep the trust DB up to date for the tofu and tofu+pgp models. + * g10/trustdb.c (init_trustdb): Recognize tofu and tofu+pgp as + possibly saved trust models. Also register the ultimately trusted + keys if the trust model is tofu or tofu+pgp. + (check_trustdb): Don't skip if the trust model is tofu or tofu+pgp. + (update_trustdb): Likewise. + (tdb_check_trustdb_stale): Likewise. + (validate_keys): If the trust model is TOFU, just write out the + ultimately trusted keys. + + gpg: Factor out code into a standalone function. + * g10/trustdb.c (tdb_keyid_is_utk): New function. + (add_utk): Use it. + + dirmngr: Allow building with libassuan < 2.3. + * dirmngr/http.c (send_request): Use newer assuan function only if + available. + +2015-10-21 Neal H. Walfield + Andre Heinecke + + gpg: Make the tofu DB check and initialization atomic. + * g10/tofu.c (initdb): Make the version check and the database + initialization atomic. + +2015-10-21 Werner Koch + + build: Make --disable-g13 the default. + * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Add --enable-g13. Remove + --enable-gpgtar because that is enabled anyway. + * configure.ac: Do not build g13 by default. + + dirmngr: Rename file dns-cert.c. + * dirmngr/dns-cert.c: Rename to dirmngr/dns-stuff.c. + * dirmngr/dns-cert.h: Rename to dirmngr/dns-stuff.h and change + includers. + * dirmngr/t-dns-cert.c: Rename to dirmngr/t-dns-stuff.c. + * dirmngr/Makefile.am: Adjust. + + common: Add status code for use by g13. + * common/status.h (STATUS_PLAINTEXT_FOLLOWS): New. + +2015-10-20 Werner Koch + + dirmngr: Prefer ADNS over system resolver. + * configure.ac (HAVE_ADNS_IF_TORMODE): New ac_define. + (USE_DNS_CERT): Prefer ADNS over the system resolver. + * dirmngr/dns-cert.c (tor_mode): New global var. + (enable_dns_tormode): New func. + (get_dns_cert): Use DNS resolver at 8.8.8.8 in tor-mode. + * dirmngr/server.c (cmd_dns_cert): If supported allow DNS requests. + + w32: Allow building again. + * dirmngr/http.c (connect_server): Fix called function name. + + build: Allow building without SQLlite support. + * configure.ac: Add option --dsiable-tofu and --disable-sqlite. + (NEED_SQLITE_VERSION): New var. + (USE_TOFU): New ac_define and am_conditional. + * autogen.sh (build-w32): Add PKG_CONFIG_LIBDIR to configure so that + pkg-config find the correct .pc file. + + * g10/Makefile.am (tofu_source): New. Build only if enabled. + * g10/gpg.c (parse_trust_model)[!USE_TOFU]: Disable tofu models. + (parse_tofu_policy)[!USE_TOFU]: Disable all. + (parse_tofu_db_format)[!USE_TOFU]: Disable all. + (main) [!USE_TOFU]: Skip. + * g10/keyedit.c (show_key_with_all_names_colon)[!USE_TOFU]: Do not + call tofu functions. + * g10/keylist.c (list_keyblock_colon)[!USE_TOFU]: Ditto. + * g10/trustdb.c (tdb_get_validity_core)[!USE_TOFU]: Skip tofu + processing. + +2015-10-20 Neal H. Walfield + + gpg: Don't die immediately if the TOFU DB is locked. + * g10/tofu.c (opendb): Don't die immediately if the DB is locked. + + gpg: Improve output. + * g10/tofu.c (get_trust): Also show the binding when indicating a + conflict occurred. + + gpg: Synchronize translation template. + * g10/tofu.c (show_statistics): Synchronize translation template. + + gpg: When showing conflicts, also show bindings with no recorded sigs. + * g10/tofu.c (signature_stats_collect_cb): If the time_ago column is + NULL, then both time_ago and count should be 0. + (get_trust): Reverse the direction of the join so that we also get + statistics about bindings without any signatures. + + gpg: Improve text. + * g10/tofu.c (show_statistics): Improve text. + + gpg: Use the right variable to display the information. + * g10/tofu.c (get_trust): Use the right variable to display the + conflicting key. + + gpg: Make failing to create a directory a soft error. + * g10/tofu.c (getdb): Don't exit if we can't create the directory. + Just return an error. + + common: Make sure tilde expansion works for the mkdir functions. + * common/mkdir_p.c (gnupg_amkdir_p): Use make_filename_try on the + first directory component as well. + + gpg: Remove unused prototype digest_algo_from_sig. + * g10/packet.h (digest_algo_from_sig): Remove prototype without a + corresponding implementation. + +2015-10-19 Werner Koch + + dirmngr: Allow building with libassuan < 2.3. + * dirmngr/dirmngr.c (set_tor_mode): Use newer assuan function only if + available. + * dirmngr/http.c (http_raw_connect): Ditto. + +2015-10-19 Neal H. Walfield + + gpg: Fix --desig-revoke. + * g10/revoke.c (gen_desig_revoke): Add additional parameter ctrl. + Check that the secret key is available. If not, display an error + message. + + gpg: Improve function documentation and some comments. + * g10/main.h: Improve function documentation. + * g10/packet.h.h: Improve function documentation. + * g10/sig-check.c: Improve function documentation and some comments. + + gpg: Improve and regularize naming of signature checking functions. + * g10/packet.h (signature_check): Rename from this... + (check_signature): ... to this. Update users. + (signature_check2): Rename from this... + (check_signature2): ... to this. Update users. + * g10/sig-check.c (do_check): Rename from this... + (check_signature_end): ... to this. Update users. + (do_check_messages): Rename from this... + (check_signature_metadata_validity): ... to this. Update users. + + gpg: Mark local function as static. + * g10/tdbio.c (put_record_into_cache): Mark as static. + +2015-10-19 Daniel Kahn Gillmor + + gpg: Print warning when rejecting weak digests. + * g10/misc.c (print_md5_rejected_note): Rename to .. + (print_digest_rejected_note): this. Parameterize function to take an + enum gcry_md_algos. + * g10/sig-check.c: Use print_digest_rejected_note() when rejecting + signatures. + + gpg: Add option --weak-digest to gpg and gpgv. + * g10/options.h: Add additional_weak_digests linked list to opts. + * g10/main.h: Declare weakhash linked list struct and + additional_weak_digest() function to insert newly-declared weak + digests into opts. + * g10/misc.c: (additional_weak_digest): New function. + (print_digest_algo_note): Check for deprecated digests; use proper + gcry_md_algos type. + * g10/sig-check.c: (do_check): Reject weak digests in addition to MD5. + * g10/gpg.c: Add --weak-digest option to gpg. + * doc/gpg.texi: Document gpg --weak-digest option. + * g10/gpgv.c: Add --weak-digest option to gpgv. + * doc/gpgv.texi: Document gpgv --weak-digest option. + +2015-10-19 Werner Koch + + dirmngr: Make --use-tor work - still leaks DNS. + * dirmngr/dirmngr.c (set_tor_mode): New. + (main, reread_configuration): Call it. + * dirmngr/http.c (http_raw_connect, send_request): Check whether TOR + mode is enabled if the FORCE_TOR flag is given. + + dirmngr: Use Assuan socket wrappers for http.c. + * dirmngr/http.c: Include assuan.h. Changed all code taking a socket + descriptor from int to assuan_fd_t. + (my_unprotect, my_protect): New. + (my_connect): Remove. + (_my_socket_new, _my_socket_unref): use assuan_sock_close. + (connect_server): Use assuan_sock_connect, assuan_sock_new, and + assuan_sock_close. + * dirmngr/Makefile.am (t_common_ldadd): Add LIBASSUAN_LIBS. + +2015-10-19 Neal H. Walfield + + gpg: Fix formatting. + * g10/tofu.c (get_trust): Fix formatting. + + gpg: Don't forget to free some memory. + * g10/tofu.c (tofu_register): Free SIG_DIGEST before returning. + + gpg: If a conflict occurs in batch mode, record that. + * g10/tofu.c (get_trust): If a conflict occurs when MAY_ASK is false, + set conflict to the key. When prompting the user, don't show the + conflicting key if the conflicting key is the current key. + +2015-10-18 Werner Koch + + gpg: Silence two more warnings. + * g10/trustdb.c (tdb_get_validity_core): Silence a warning. + * g10/tofu.c (tofu_register): Move SIG_DIGEST computation to the top + so that it is not uninitialized in case of an early error. + + gpg: Fix harmless compiler warnings. + * g10/tofu.h (_tofu_GET_POLICY_ERROR): New. This avoids warnings + about undefined enum values in a switch. + * g10/trustdb.h (_tofu_GET_TRUST_ERROR): New. + * g10/tofu.c (TIME_AGO_FUTURE_IGNORE): Move to the top. + (opendbs): Avoid compiler warning (use braces). + (GET_POLICY_ERROR): Replace define by enum _tofu_GET_POLICY_ERROR. + (get_policy): Remove assert. + (GET_TRUST_ERROR): Replace by _tofu_GET_TRUST_ERROR macro. + (show_statistics): Undef MIN_SECS et al. after use. + + common: Avoid warning about const char ** assignment. + * common/mkdir_p.c (gnupg_amkdir_p): Also strdup first item. Return + an error on malloc failure. + (gnupg_mkdir_p): Fix type of dirs and tmp_dirs. + + Move http module from common/ to dirmngr/. + * common/http.c: Move to ../dirmngr/. + * common/http.h: Move to ../dirmngr/. + * common/t-http.c: Move to ../dirmngr/. + * common/tls-ca.pem: Move to ../dirmngr/. + * common/Makefile.am: Do not build libcommontls.a libcommontlsnpth.a. + Remove http.c related stuff. + * po/POTFILES.in: Move http.c to dirmngr/. + * dirmngr/Makefile.am (EXTRA_DIST): Add tls-ca.pem. + (module_maint_tests): New. + (noinst_PROGRAMS): Add module_maint_tests. + (dirmngr_SOURCES): Add http.c and http.h. + (dirmngr_LDADD): Remove libcommontlsnpth. + (t_common_ldadd): Ditto. + (t_http_SOURCES, t_http_CFLAGS, t_http_LDADD): New. + (t_ldap_parse_uri_SOURCES): Add http.c. + (t_ldap_parse_uri_CFLAGS): Build without npth. + ($(PROGRAMS)): Do not require libcommontls.a libcommontlsnpth.a. + * dirmngr/dirmngr.h, dirmngr/ks-engine.h: Fix include of http.h. + +2015-10-18 Neal H. Walfield + + g10: Fix assert. + * g10/tofu.c (get_trust): Fix assert. + + g10: Add TOFU support. + * configure.ac: Check for sqlite3. + (SQLITE3_CFLAGS): AC_SUBST it. + (SQLITE3_LIBS): Likewise. + * g10/Makefile.am (AM_CFLAGS): Add $(SQLITE3_CFLAGS). + (gpg2_SOURCES): Add tofu.h and tofu.c. + (gpg2_LDADD): Add $(SQLITE3_LIBS). + * g10/tofu.c: New file. + * g10/tofu.h: New file. + * g10/options.h (trust_model): Define TM_TOFU and TM_TOFU_PGP. + (tofu_db_format): Define. + * g10/packet.h (PKT_signature): Add fields digest and digest_len. + * g10/gpg.c: Include "tofu.h". + (cmd_and_opt_values): Declare aTOFUPolicy, oTOFUDefaultPolicy, + oTOFUDBFormat. + (opts): Add them. + (parse_trust_model): Recognize the tofu and tofu+pgp trust models. + (parse_tofu_policy): New function. + (parse_tofu_db_format): New function. + (main): Initialize opt.tofu_default_policy and opt.tofu_db_format. + Handle aTOFUPolicy, oTOFUDefaultPolicy and oTOFUDBFormat. + * g10/mainproc.c (do_check_sig): If the signature is good, copy the + hash to SIG->DIGEST and set SIG->DIGEST_LEN appropriately. + * g10/trustdb.h (get_validity): Add arguments sig and may_ask. Update + callers. + (tdb_get_validity_core): Add arguments sig and may_ask. Update + callers. + * g10/trust.c (get_validity) Add arguments sig and may_ask. Pass them + to tdb_get_validity_core. + * g10/trustdb.c: Include "tofu.h". + (trust_model_string): Handle TM_TOFU and TM_TOFU_PGP. + (tdb_get_validity_core): Add arguments sig and may_ask. If + OPT.TRUST_MODEL is TM_TOFU or TM_TOFU_PGP, compute the TOFU trust + level. Combine it with the computed PGP trust level, if appropriate. + * g10/keyedit.c: Include "tofu.h". + (show_key_with_all_names_colon): If the trust mode is tofu or + tofu+pgp, then show the trust policy. + * g10/keylist.c: Include "tofu.h". + (public_key_list): Also show the PGP stats if the trust model is + TM_TOFU_PGP. + (list_keyblock_colon): If the trust mode is tofu or + tofu+pgp, then show the trust policy. + * g10/pkclist.c: Include "tofu.h". + * g10/gpgv.c (get_validity): Add arguments sig and may_ask. + (enum tofu_policy): Define. + (tofu_get_policy): New stub. + (tofu_policy_str): Likewise. + * g10/test-stubs.c (get_validity): Add arguments sig and may_ask. + (enum tofu_policy): Define. + (tofu_get_policy): New stub. + (tofu_policy_str): Likewise. + * doc/DETAILS: Describe the TOFU Policy field. + * doc/gpg.texi: Document --tofu-set-policy, --trust-model=tofu, + --trust-model=tofu+pgp, --tofu-default-policy and --tofu-db-format. + * tests/openpgp/Makefile.am (TESTS): Add tofu.test. + (TEST_FILES): Add tofu-keys.asc, tofu-keys-secret.asc, + tofu-2183839A-1.txt, tofu-BC15C85A-1.txt and tofu-EE37CF96-1.txt. + (CLEANFILES): Add tofu.db. + (clean-local): Add tofu.d. + * tests/openpgp/tofu.test: New file. + * tests/openpgp/tofu-2183839A-1.txt: New file. + * tests/openpgp/tofu-BC15C85A-1.txt: New file. + * tests/openpgp/tofu-EE37CF96-1.txt: New file. + * tests/openpgp/tofu-keys.asc: New file. + * tests/openpgp/tofu-keys-secret.asc: New file. + +2015-10-16 Neal H. Walfield + + common: Prefix the mkdir functions with gnupg_. Make args const. + * common/mkdir_p.h (mkdir_p): Rename from this... + (gnupg_mkdir_p): ... to this. Change directory_component's type from + char * to const char *. + (amkdir_p): Rename from this... + (gnupg_amkdir_p): ... to this. Change directory_component's type from + char * to const char *. + * common/mkdir_p.c (mkdir_p): Rename from this... + (gnupg_mkdir_p): ... to this. Change directory_component's type from + char * to const char *. + (amkdir_p): Rename from this... + (gnupg_amkdir_p): ... to this. Change directory_component's type from + char * to const char *. + +2015-10-14 NIIBE Yutaka + + cleanup: Fix confusion between gpg_error_t and gpg_err_code_t. + * dirmngr/crlcache.c (hash_dbfile): Use gpg_error_t for ERR. + * kbx/keybox-update.c (keybox_set_flags): Call + gpg_err_code_from_syserror. + +2015-10-13 NIIBE Yutaka + + po: Update Japanese translation. + +2015-10-12 Werner Koch + + gpg: Try hard to use MDC also for sign+symenc. + * g10/encrypt.c (use_mdc): Make it a global func. + * g10/sign.c (sign_symencrypt_file): Use that function to decide + whether to use an MDC. + * tests/openpgp/conventional-mdc.test: Add a simple test case. + +2015-10-09 Werner Koch + + Release 2.1.9. + +2015-10-09 NIIBE Yutaka + + agent: simplify agent_get_passphrase. + * agent/call-pinentry.c (agent_get_passphrase): Simplify. + + agent: fix agent_askpin. + * agent/call-pinentry.c (agent_askpin): Fix off-by-one error. + + agent: Fix function return type for check_cb and agent_askpin. + * agent/call-pinentry.c (unlock_pinentry): Return gpg_error_t. + (start_pinentry, setup_qualitybar): Likewise. + (agent_askpin): Fix return value check of check_cb. + * agent/command-ssh.c (reenter_compare_cb): Return gpg_error_t. + (ssh_identity_register): Fix return value check of agent_askpin. + * agent/cvt-openpgp.c (try_do_unprotect_cb): Return gpg_error_t. + * agent/findkey.c (try_unprotect_cb): Likewise. + * agent/genkey.c (reenter_compare_cb): Return gpg_error_t. + (agent_ask_new_passphrase): Fix return value check of agent_askpin. + +2015-10-08 Andre Heinecke + + dirmngr: Default to http protocol for http-proxy. + * common/http.c (send_request): Fix handling for hostname:port string. + +2015-10-08 Werner Koch + + common: Allow building of mkdir_p.c for Windows. + * common/mkdir_p.c: Change license and comment debug statements. + (amkdir_p, mkdir_p): Fail on malloc error and use default_errsource to + build an error code. Change return value to gpg_error_t. + (amkdir_p): Use gnupg_mkdir. + + * common/membuf.c: Include util.h first to avoid redefined macro + warnings. + + gpg: Add option --print-dane-records. + * g10/options.h (opt): Add field "print_dane_records". + * g10/gpg.c (oPrintDANERecords): new. + (opts): Add --print-dane-records. + (main): Set that option. + * g10/export.c (do_export): Remove EXPORT_DANE_FORMAT handling. + (do_export_stream): Add EXPORT_DANE_FORMAT handling. + * g10/keylist.c (list_keyblock_pka): Implement DANE record printing. + + * g10/gpgv.c (export_pubkey_buffer): New stub. + * g10/test-stubs.c (export_pubkey_buffer): New stub. + + gpg: Pass CTRL parameter to all key listing functions. + * g10/keylist.c (public_key_list): Add arg CTRL. + (secret_key_list): Ditto. + (list_all, list_one): Ditto. + (locate_one): Ditto. + (list_keyblock_pka): Ditto. + (list_keyblock): Ditto. + (list_keyblock_direct): Ditto. + * g10/keygen.c (proc_parameter_file): Add arg CTRL. + (read_parameter_file): Ditto. + (quick_generate_keypair): Ditto. + (do_generate_keypair): Ditto. + (generate_keypair): Pass arg CTRL. + * g10/gpg.c (main): Pass arg CTRL to quick_generate_keypair. + +2015-10-07 Werner Koch + + gpg: Remove unfinished experimental code to export as S-expressions. + * g10/options.h (EXPORT_SEXP_FORMAT): Remove. + (EXPORT_DANE_FORMAT): New. + * g10/export.c (parse_export_options): Remove "export-sexp-format". + (export_seckeys): Adjust for removed option. + (export_secsubkeys): Ditto. + (do_export): Prepare for DANE format. + (build_sexp, build_sexp_seckey): Remove. + (do_export_stream): Remove use of removed functions. + +2015-10-06 Werner Koch + + gpg: Add new --auto-key-locate mechanism "dane". + * g10/call-dirmngr.c (gpg_dirmngr_dns_cert): Allow fetching via DANE. + * g10/keyserver.c (keyserver_import_cert): Add arg "dane_mode". + * g10/options.h (AKL_DANE): New. + * g10/getkey.c (get_pubkey_byname): Implement AKL_DANE. + (parse_auto_key_locate): Ditto. + + dirmngr: Addlow fetching keys using OpenPGP DANE. + * dirmngr/server.c (cmd_dns_cert): Add option --dane. + + dirmngr: Improve DNS code to retrieve arbitrary records. + * dirmngr/dns-cert.c (get_dns_cert): Add hack to retrieve arbitrary + resource records. + * dirmngr/dns-cert.h (DNS_CERTTYPE_RRBASE): New. + (DNS_CERTTYPE_RR61): New. + + dirmngr: Change DNS code to make additions easier. + * dirmngr/dns-cert.c (get_dns_cert) [!USE_ADNS]: Change loop to allow + adding more resource types. + + dirmngr: Make commands RELOADDIRMNGR and KILLDIRMNGR work properly. + * dirmngr/server.c (cmd_killdirmngr): Set assuan close flag. + (cmd_reloaddirmngr): Use check_owner_permission. + + dirmngr: Do tilde expansion for --hkp-cacert. + * dirmngr/dirmngr.c (parse_rereadable_options): Do tilde expansion and + check for cert file existance in option --hkp-cacert. + + gpg: Fail decryption for AES etc message w/o MDC. + * g10/mainproc.c (proc_encrypted): Fail for modern messages w/o MDC. + +2015-10-06 NIIBE Yutaka + + agent: Fix verification of signature for smartcard. + * agent/pksign.c (agent_pksign_do): Use public key smartcard. + + agent: Fix non-allocation for pinentry_loopback. + * agent/call-pinentry.c (agent_get_passphrase): Don't allocate, it will + be allocated by pinentry_loopback. + +2015-10-05 Werner Koch + + gpg: Install a dirmngr.conf file. + * g10/dirmngr-conf.skel: New. + * g10/Makefile.am (EXTRA_DIST): Add file. + (install-data-local, uninstall-local): Install that file. + * g10/openfile.c (copy_options_file): Add arg "name", return a value, + simplify with xstrconcat, and factor warning message out to: + (try_make_homedir): here. Also install dirmngr.conf. + * g10/options.skel: Remove --keyserver entry. + + gpg: Deprecate the --keyserver option. + * g10/keyserver.c (keyserver_refresh): Change return type to + gpg_error_t. Use gpg_dirmngr_ks_list to print the name of the + keyserver to use. + (keyserver_search): Do not print the "no keyserver" error + message. The same error is anyway returned from dirmngr. + * g10/call-dirmngr.c (ks_status_parm_s): Add field "keyword". + (ks_status_cb): Handle other status keywords. + (gpg_dirmngr_ks_list): New. + * tools/gpgconf-comp.c (gc_options_gpg): Deprecate "keyserver". + (gc_options_dirmngr): Add "Keyserver" group and "keyserver". + + dirmngr: Add option --keyserver. + * dirmngr/dirmngr.c (oKeyServer): New. + (opts): Add "keyserver". + (parse_rereadable_options): Parse that options + (main): Add option to the gpgconf list. + * dirmngr/dirmngr.h (opt): Add field "keyserver". + * dirmngr/server.c (ensure_keyserver): New. + (make_keyserver_item): New. Factored out from + (cmd_keyserver): here. Call ensure_keyserver. + (cmd_ks_search): Call ensure_keyserver. + (cmd_ks_get): Ditto. + (cmd_ks_fetch): Ditto. + (cmd_ks_put): Ditto. + + dirmngr: Make clear that --use-tor is not yet ready for use. + * dirmngr/dirmngr.c (main): Print a warning if --use-tor has been + given. + * tools/gpgconf-comp.c (gc_options_dirmngr): Make --use-tor invisible. + + gpgconf: Change displayed name of Dirmngr to "Key Acquirer". + * tools/gpgconf-comp.c (gc_component): Change printed name. + +2015-10-02 Werner Koch + + dirmngr: Fix use-after-free due to a realloc shrinking. + * dirmngr/ks-engine-hkp.c (map_host): Do not use original pointer + after realloc. + + agent: Fix alignment problem with the second passphrase struct. + * agent/genkey.c (agent_ask_new_passphrase): Use a separate malloc for + PI2. Check return value of the malloc function. + * agent/command-ssh.c (ssh_identity_register): Use a separate malloc + for PI2. Wipe PI2. + +2015-10-01 Werner Koch + + gpg: Fix a practical hang after use of --faked-system-time. + * g10/sign.c (update_keysig_packet): Bail out if we would need to long + for a new timestamp. + + gpg: Print more info with "check selfsig". + * g10/keyedit.c (print_and_check_one_sig): Print more Some sigsub + packets. + + gpg: Add debug helper to --edit-keys's check sub-command. + * g10/keyedit.c (print_and_check_one_sig): Add arg "extended" and + print an asterisk for the chosen selfsig. + (check_all_keysigs): Add arg "only_selfsig" + (keyedit_menu) : Add optional arg "selfsig". + +2015-10-01 NIIBE Yutaka + + common: Fix strsplit. + * common/stringhelp.c (strsplit): Fix arguments order. + +2015-09-30 Neal H. Walfield + + common: Add mkdir_p. + * common/mkdir_p.c: New file. + * common/mkdir_p.h: New file. + * common/Makefile.am (common_sources): Add mkdir_p.c and mkdir_p.h. + + common: Remove unused files. + * common/xmalloc.c: Remove file. + * common/xmalloc.h: Remove file. + + common: Include . + * common/logging.h: Include . + +2015-09-29 Neal H. Walfield + + g10: Remove unused struct cmp_help_context_s. + * g10/sig-check.c (struct cmp_help_context_s) Remove unused struct. + + g10: Avoid an unnecessary copy. + * g10/sig-check.c (signature_check2): Avoid copying PK to RET_PK. + Instead, directly use the provided storage. If none is provided + allocate some. + +2015-09-29 NIIBE Yutaka + + ssh: Fix fingerprint computation for EdDSA key. + * common/ssh-utils.c (get_fingerprint): Handle the prefix of 0x40. + * common/t-ssh-utils.c (sample_keys): Add a new key. + + agent: RSA signature verification by gpg-agent. + * g10/sign.c (do_sign): Let verify signature by gpg-agent. + * agent/pksign.c (agent_pksign_do): Call gcry_pk_verify for RSA. + +2015-09-28 Werner Koch + + common: Provide two new error code replacements. + * common/util.h (GPG_ERR_FALSE, GPG_ERR_TRUE): Rew replcements. + + common: Change calling convention for gnupg_spawn_process. + * common/exechelp.h (GNUPG_SPAWN_NONBLOCK): New. + (GNUPG_SPAWN_RUN_ASFW, GNUPG_SPAWN_DETACHED): Macro to replace the + numbers. + * common/exechelp.h (gnupg_spawn_process): Change function to not take + an optional stream for stdin but to return one. + * common/exechelp-posix.c (gnupg_spawn_process): Implement change. + (create_pipe_and_estream): Add args outbound and nonblock. + * common/exechelp-w32.c (gnupg_spawn_process): Implement change. + +2015-09-28 NIIBE Yutaka + + scd: Handle error correctly. + * scd/apdu.c (apdu_connect): Initialize variables and check an error + of apdu_get_status_internal. + +2015-09-22 Werner Koch + + ssh: Add 256, 384 and 521 bit test keys for the fingerprint. + * common/t-ssh-utils.c (sample_keys): Add 3 new keys. + + ssh: Fix fingerprint computation for 384 bit ECDSA keys. + * common/ssh-utils.c (get_fingerprint): Fix hashed string. + +2015-09-19 NIIBE Yutaka + + agent: Fix importing ECC key. + * agent/cvt-openpgp.c (convert_from_openpgp_main): Only encrypted + parameters are stored as opaque. + (apply_protection): ARRAY members are all normal, non-opaque MPI. + (extract_private_key): Get public key as normal, non-opaque MPI. + Remove support of ECC key with '(flags param)'. + Remove support of "ecdsa" and "ecdh" keys of our experiment. + + scd: Fix KEYTOCARD handling for ECC key. + * scd/app-openpgp.c (ecc_writekey): Only public key can be native + format. + +2015-09-19 Neal H. Walfield + + common: Add new function strlist_length. + * common/strlist.c (strlist_length): New function. + +2015-09-18 Werner Koch + + gpgconf: Change displayed name of Dirmngr to "Network Manager". + * tools/gpgconf-comp.c (gc_component): Change printed name. + + dirmngr: Add option --use-tor as a stub. + * dirmngr/dirmngr.h (opt): Add field "use_tor". + * dirmngr/dirmngr.c (oUseTor): New. + (opts): Add --use-tor. + (parse_rereadable_options): Set option. + (main): Tell gpgconf about that option. + + * dirmngr/crlfetch.c (crl_fetch): Pass TOR flag to the http module and + return an error if LDAP is used in TOR mode. + (ca_cert_fetch): Return an error in TOR mode. + (start_cert_fetch): Ditto. + * dirmngr/ks-engine-finger.c (ks_finger_fetch): Pass TOR flag to the + http module. + * dirmngr/ks-engine-hkp.c (send_request): Ditto. + * dirmngr/ks-engine-http.c (ks_http_fetch): Ditto. + * dirmngr/ks-engine-ldap.c (ks_ldap_get): Return an error in TOR mode. + (ks_ldap_search): Ditto. + (ks_ldap_put): Ditto. + * dirmngr/ocsp.c (do_ocsp_request): Ditto. Also pass TOR flag to the + http module. + + * dirmngr/server.c (option_handler): Add "honor-keyserver-url-used". + (cmd_dns_cert): Return an error in TOR mode. + (cmd_getinfo): Add subcommand "tor" + * tools/gpgconf-comp.c (gc_options_dirmngr): Add TOR group. + + gpg: Report a conflict between honor-keyserver-url and TOR. + * g10/call-dirmngr.c (create_context): Send option and print a verbose + error. + + http: Add flag to force use of TOR (part 1) + * common/http.h (HTTP_FLAG_FORCE_TOR): New. + * common/http.c (http_raw_connect, send_request): Detect flag and + return an error for now. + +2015-09-17 NIIBE Yutaka + + po: Update Japanese translation. + + scd: Fix ccid-driver timeout for OpenPGPcard v2.1. + * scd/ccid-driver.c (CCID_CMD_TIMEOUT): New. + (ccid_transceive_apdu_level, ccid_transceive): Use. + +2015-09-16 Werner Koch + + agent: New option --pinentry-invisible-char. + * agent/gpg-agent.c (oPinentryInvisibleChar): New. + (opts): Add option. + (parse_rereadable_options): Set option. + * agent/agent.h (opt): Add field pinentry_invisible_char. + * agent/call-pinentry.c (start_pinentry): Pass option to pinentry. + + g13: Move some code to a separate module. + * g13/g13-common.c, g13/g13-common.h: New. + * g13/Makefile.am (g13_SOURCES): Add new files. + * g13/g13.c (g13_errors_seen): Move to g13-common.c. + (cmdline_conttype): New. + (main): Use g13_init_signals and g13_install_emergency_cleanup. + (emergency_cleanup, g13_exit): Move to g13-common.c. + * g13/g13.h: Move OPT and some other code to g13-common.h. + + gpg: Fix skip function dummy parameter. + * g10/trustdb.c (search_skipfnc): Fix dummy argument + + gpg: Change last commit to avoid extra translations. + * g10/keyedit.c (keyedit_menu): Do not print usage hints in expert + mode. + +2015-09-16 Neal H. Walfield + + g10: Improve error message. + * g10/keyedit.c (keyedit_menu): When complaining that a user ID or key + must be selected, indicate what command to use to do this. + + g10: Be more careful when merging self-signed data. + * g10/getkey.c (merge_selfsigs_main): Stop looking for self-signed + data belonging to the public key when we encounter an attribute packet + or a subkey packet, not just a user id packet. When looking for + self-signed data belonging to a user id packet, stop when we see a + user attribute packet. + + g10: Simplify some complicated boolean expressions. + * g10/getkey.c (finish_lookup): Simplify logic. + + g10: Also mark revoked and expired keys as unusable. + * g10/getkey.c (skip_unusable): Also mark the key as unusable if it + has been revoked or has expired. + + g10: Release resources when returning an error in get_seckey. + * g10/getkey.c (get_seckey): If the key doesn't have a secret key, + release *PK. + + g10: Improve documentation and comments for getkey.c. + * g10/getkey.c: Improve documentation and comments for most + functions. Move documentation for public functions from here... + * g10/keydb.h: ... to here. + + g10: Remove unused function have_any_secret_key. + * g10/getkey.c (have_any_secret_key): Remove function. + + g10: Bring cache semantics closer to non-cache semantics. + * g10/getkey.c (get_pubkey_fast): When reading from the cache, only + consider primary keys. + + g10: Break out of the loop earlier. + * g10/getkey.c (have_secret_key_with_kid): Once we find the relevent + key or subkey, stop searching. + + g10: Don't skip legacy keys if the search mode is KEYDB_SEARCH_MODE_NEXT + * g10/getkey.c (lookup): Also don't skip legacy keys if the search + mode is KEYDB_SEARCH_MODE_NEXT. + + g10: Remove unused function get_seckeyblock_byfprint. + * g10/keydb.h (get_seckeyblock_byfprint): Remove prototype. + * g10/getkey.c (get_seckeyblock_byfprint): Remove function. + + g10: Remove unused function get_seckey_byfprint. + * g10/keydb.h (get_seckey_byfprint): Remove prototype. + * g10/getkey.c (get_seckey_byfprint): Remove function. + + g10: Simplify get_seckey_byname: it was never called with NAME not NULL. + * g10/keydb.h (get_seckey_byname): Rename from this... + (get_seckey_default): ... to this. Drop the parameter name. Update + users. + * g10/getkey.c (get_seckey_byname): Rename from this... + (get_seckey_default): ... to this. Drop the parameter name. Drop the + code which assumed that NAME is not NULL. + + g10: Eliminate the redundant function get_keyblock_byfprint. + * g10/keydb.h (get_keyblock_byfprint): Remove prototype. Replace use + of this function with get_pubkey_byfprint. + * g10/getkey.c (get_pubkey_byname): Remove function. + + g10: Simplify semantics of get_pubkey_byname. + * g10/getkey.c (get_pubkey_byname): If R_KEYBLOCK is not NULL, return + the keyblock in R_KEYBLOCK independent of whether PK is set or not. + + g10: Eliminate the redundant function get_pubkey_byname. + * g10/getkey.c (get_pubkey_byname): Remove function. + (lookup): Replace use of get_pubkey_byname by get_pubkey_byfprint. + + g10: Eliminate the redundant function get_pubkey_end. + * g10/keydb.h (get_pubkey_end): Remove declaration. Replace use of + function with getkey_end. + * g10/getkey.c (get_pubkey_byname): Remove function. + + g10: Eliminate the redundant function get_pubkey_next. + * g10/keydb.h (get_pubkey_next): Remove prototype. + * g10/getkey.c (get_pubkey_next): Remove function. + * g10/keylist.c (locate_one): Use getkey_next instead of + get_pubkey_next. + + kbx: Change skipfnc's prototype so that we can provide all information. + * kbx/keybox-search-desc.h (struct keydb_search_desc.skipfnc): Change + third parameter to be the index of the user id packet in the keyblock + rather than the packet itself. Update users. + + g10: Remove unused prototype (get_pubkey_byfpr). + * g10/keydb.h (get_pubkey_byfpr): Remove unused prototype. + + g10: Remove unused function (get_pubkey_bynames). + * g10/keydb.h (get_pubkey_bynames): Remove prototype. + * g10/getkey.c (get_pubkey_bynames): Remove function. + + g10: Simplify code. Turn struct getkey_ctx_s.found_key into an argument + * g10/getkey.c (struct getkey_ctx_s): Remove field found_key. + (lookup): Add argument ret_found_key. If not NULL, set it to the + found key. Update callers. + (pk_from_block): Add argument found_key. Use it instead of + CTX->FOUND_KEY. Update callers. + (finish_lookup): Return a KBNODE (the found key) instead of an int. + Don't set CTX->FOUND_KEY. Return the found key instead. + + g10: Remove unused field struct getkey_ctx_s.kbpos. + * g10/getkey.c (struct getkey_ctx_s): Remove field kbpos. + (getkey_end): Don't clear CTX->KBPOS. + + g10: Simplify code: remove field struct getkey_ctx_s.keyblock. + * g10/getkey.c (struct getkey_ctx_s): Remove field keyblock. + (finish_lookup): Add parameter keyblock. Update caller to pass this. + (lookup): Add new local variable keyblock. Use this instead of + ctx->keyblock for referencing the keyblock. + +2015-09-16 NIIBE Yutaka + + agent: Fix registering SSH Key of Ed25519. + * agent/command-ssh.c (stream_read_string): Add the prefix of 0x40. + +2015-09-15 NIIBE Yutaka + + po: Update Japanese translation. + +2015-09-10 Werner Koch + + Release 2.1.8. + + tests: Silence the 5gb-packet test. + * tests/openpgp/4gb-packet.test: Send output to /dev/null. + + g10: Fix make distcheck problem. + * g10/test.c: Include string.h. + (prepend_srcdir): New. Taken from Libgcrypt. + (test_free): New. + * g10/t-keydb.c (do_test): Malloc the filename. + * g10/Makefile.am (AM_CPPFLAGS): Remove -DSOURCE_DIR + (EXTRA_DIST): Add t-keydb-keyring.kbx. + + g10: Improve portability of the new test driver. + * g10/test.c: Include stdio.h and stdlib.h. + (verbose): New. + (print_results): Rename to exit_tests. + (main): Remove atexit and call exit_tests. Set verbose. + (ASSERT, ABORT): Call exit_tests instead of exit. + +2015-09-09 Werner Koch + + dirmngr: Allow sending much larger keyblocks. + * dirmngr/server.c (MAX_CERT_LENGTH): Increase to 16k. + (MAX_KEYBLOCK_LENGTH): Increase to 20M. + +2015-09-07 NIIBE Yutaka + + scd: Force key attribute change for writekey. + * scd/app-openpgp.c (change_rsa_keyattr): New. + (change_keyattr_from_string): Use change_rsa_keyattr. + (rsa_writekey): Call change_rsa_keyattr when different size. + (ecc_writekey): Try to change key attribute. + + scd: KEYNO cleanup. + * scd/app-openpgp.c (get_public_key, send_keypair_info, do_readkey) + (change_keyattr, change_keyattr_from_string, ecc_writekey, do_genkey) + (compare_fingerprint, check_against_given_fingerprint): KEYNO starts + from 0. + +2015-09-02 Neal H. Walfield + + g10: Remove unused field req_algo. + * g10/packet.h (PKT_public_key): Remove unused field req_algo. Remove + users. + * g10/getkey.c (struct getkey_ctx_s): Remove unused field req_algo. + Remove users. + + g10: Use a symbolic constant instead of a literal. + * g10/trustdb.c (KEY_HASH_TABLE_SIZE): Define. + (new_key_hash_table): Use KEY_HASH_TABLE_SIZE instead of a literal. + (release_key_hash_table): Likewise. + (test_key_hash_table): Likewise. + (add_key_hash_table): Likewise. + + g10: Add test for keydb as well as new testing infrastructure. + * g10/Makefile.am (EXTRA_DIST): Add test.c. + (AM_CPPFLAGS): Add -DSOURCE_DIR="\"$(srcdir)\"". + (module_tests): Add t-keydb. + (t_keydb_SOURCES): New variable. + (t_keydb_LDADD): Likewise. + * g10/t-keydb.c: New file. + * g10/t-keydb-keyring.kbx: New file. + * g10/test-stubs.c: New file. + * g10/test.c: New file. + + g10: Make the keyblock cache per-handle rather than global. + * g10/keydb.c (keyblock_cache): Don't declare this variable. Instead... + (struct keyblock_cache): ... turn its type into this first class + object... + (struct keydb_handle): ... and instantiate it once per database + handle. Update all users. + (keydb_rebuild_caches): Don't invalidate the keyblock cache. + + g10: If iobuf_seek fails when reading from the cache, do a hard read. + * g10/keydb.c (keydb_get_keyblock): If the iobuf_seek fails when + reading from the cache, then simply clear the cache and try reading + from the database. + + iobuf: Reduce verbosity of test. + * common/t-iobuf.c (main): Reduce verbosity. + + iobuf: Add the IOBUF_INPUT_TEMP type to improve input temp handling. + * common/iobuf.h (enum iobuf_use): Add new member, IOBUF_INPUT_TEMP. + * common/iobuf.c (iobuf_temp_with_content): Create the iobuf as an + IOBUF_INPUT_TEMP, not an IOBUF_INPUT buffer. Assert that LENGTH == + A->D.SIZE. + (iobuf_push_filter2): If A is an IOBUF_INPUT_TEMP, then make the new + filter an IOBUF_INPUT filter and set its buffer size to + IOBUF_BUFFER_SIZE. + (underflow): If A is an IOBUF_INPUT_TEMP, then just return EOF; don't + remove already read data. + (iobuf_seek): If A is an IOBUF_INPUT_TEMP, don't discard the buffered + data. + (iobuf_alloc): Allow USE == IOBUF_INPUT_TEMP. + (pop_filter): Allow USE == IOBUF_INPUT_TEMP. + (iobuf_peek): Allow USE == IOBUF_INPUT_TEMP. + (iobuf_writebyte): Fail if USE == IOBUF_INPUT_TEMP. + (iobuf_write): Fail if USE == IOBUF_INPUT_TEMP. + (iobuf_writestr): Fail if USE == IOBUF_INPUT_TEMP. + (iobuf_flush_temp): Fail if USE == IOBUF_INPUT_TEMP. + + iobuf: Rename IOBUF_TEMP to IOBUF_OUTPUT_TEMP. + * common/iobuf.h (enum iobuf_use): Rename IOBUF_TEMP to + IOBUF_OUTPUT_TEMP. Update users. + + iobuf: Use a first-class enum. + * common/iobuf.h (enum iobuf_use): Name the IOBUF_OUTPUT, etc. enum. + (struct iobuf_struct): Change the field use's type to it. + + iobuf: Fix test. + * common/t-iobuf.c (content_filter): If there is nothing to read, + don't forget to set *LEN to 0. + (main): Fix checks. + +2015-09-01 Werner Koch + + agent: Protect commit 135b1e3 against misbehaving Libgcrypt. + * agent/command-ssh.c (ssh_key_to_blob): Check DATALEN. + + gpg: Remove option --no-sig-create-check. + * g10/gpg.c (opts): Remove --no-sig-create-check. + * g10/options.h (struct opt): Remove field no_sig_create_check. + * g10/sign.c (do_sign): Always check unless it is RSA and we are using + Libgcrypt 1.7. + + common: Assume an utf-8 locale on iconv errors. + * common/utf8conv.c (handle_iconv_error): Use utf-8 as fallback. + + common: Fix regression in building argpase.c standalone. + * common/argparse.c (is_native_utf8) [GNUPG_MAJOR_VERSION]: New. + +2015-08-31 Neal H. Walfield + + g10: Don't leak memory if we fail to initialize a new database handle. + * g10/keydb.c (keydb_new): If we fail to open a keyring or keybox + correctly release all resources. + + g10: Improve interface documentation of the keydb API. + * g10/keydb.c: Improve code comments and documentation of internal + interfaces. Improve documentation of public APIs and move that to... + * g10/keydb.h: ... this file. + + g10: Don't cache search results if the search didn't scan the whole DB. + * g10/keydb.c (struct keydb_handle): Add new field is_reset. + (keydb_new): Initialize hd->is_reset to 1. + (keydb_locate_writable): Set hd->is_reset to 1. + (keydb_search): Set hd->is_reset to 0. Don't cache a key not found if + the search started from the beginning of the database. + + g10: Have keydb_search_first call keydb_search_reset before searching. + * g10/keydb.c (keydb_search_first): Reset the handle before starting + the search. + + g10: Remove unused parameter. + * g10/keydb.h (keydb_locate_writable): Remove unused parameter + reserved. Update users. + +2015-08-31 NIIBE Yutaka + + agent: Fix SSH public key for EdDSA. + * agent/command-ssh.c (ssh_key_to_blob): Remove the prefix 0x40. + +2015-08-26 Neal H. Walfield + + g10: Simplify cache. Only include data that is actually used. + * g10/keydb.c (struct kid_list_s): Rename from this... + (struct kid_not_found_cache_bucket): ... to this. Update users. + Remove field state. + (kid_list_t): Remove type. + (KID_NOT_FOUND_CACHE_BUCKETS): Define. Use this instead of a literal. + (kid_found_table): Rename from this... + (kid_not_found_cache_bucket): ... to this. Update users. + (kid_found_table_count): Rename from this... + (kid_not_found_cache_count): ... to this. Update users. + (kid_not_found_p): Only return whether a key with the specified key id + is definitely not in the database. + (kid_not_found_insert): Remove parameter found. Update callers. + (keydb_search): Only insert a key id in the not found cache if it is + not found. Rename local variable once_found to already_in_cache. + +2015-08-25 Werner Koch + + Add configure option --enable-build-timestamp. + * configure.ac (BUILD_TIMESTAMP): Set to "" by default. + + gpg: Emit ERROR status for key signing failures. + * g10/keyedit.c (sign_uids): Write an ERROR status for a signing + failure. + (menu_adduid, menu_addrevoker, menu_revsig): Ditto. + (menu_revuid, menu_revkey, menu_revsubkey): Ditto. + + gpg: Print a new FAILURE status after most commands. + * common/status.h (STATUS_FAILURE): New. + * g10/cpr.c (write_status_failure): New. + * g10/gpg.c (main): Call write_status_failure for all commands which + print an error message here. + * g10/call-agent.c (start_agent): Print an STATUS_ERROR if we can't + set the pinentry mode. + +2015-08-24 Neal H. Walfield + + agent: Raise the maximum password length. Don't hard code it. + * agent/agent.h (MAX_PASSPHRASE_LEN): Define. + * agent/command-ssh.c (ssh_identity_register): Use it instead of a + hard-coded literal. + * agent/cvt-openpgp.c (convert_from_openpgp_main): Likewise. + * agent/findkey.c (unprotect): Likewise. + * agent/genkey.c (agent_ask_new_passphrase): Likewise. + +2015-08-24 Werner Koch + + sm: Support secret key export via the Assuan interface. + * sm/server.c (cmd_export): Add options --secret, --raw, and --pkcs12. + +2015-08-23 Werner Koch + + dirmngr: Allow sending of Zack's key. + * dirmngr/server.c (MAX_KEYBLOCK_LENGTH): Increase to 1 MiB. + + gpg: Fix regression in packet parser from Aug 19. + * g10/parse-packet.c (parse): Use an int to compare to -1. Use + buf32_to_ulong. + + gpg: Show not found keys with --locate-key --verbose. + * g10/keylist.c (locate_one): Print a diagnostic for a not-found key. + +2015-08-21 Neal H. Walfield + + common: Don't incorrectly reject 4 GB - 1 sized packets. + * g10/parse-packet.c (parse): Don't reject 4 GB - 1 sized packets. + Add the constraint that the type must be 63. + * kbx/keybox-openpgp.c (next_packet): Likewise. + * tests/openpgp/4gb-packet.asc: New file. + * tests/openpgp/4gb-packet.test: New file. + * tests/openpgp/Makefile.am (TESTS): Add 4gb-packet.test. + (TEST_FILES): Add 4gb-packet.asc. + + common: Don't assume on-disk layout matches in-memory layout. + * g10/packet.h (PKT_signature): Change revkey's type from a struct + revocation_key ** to a struct revocation_key *. Update users. + + common: Don't incorrectly copy packets with partial lengths. + * g10/parse-packet.c (parse): We don't handle copying packets with a + partial body length to an output stream. If this occurs, log an error + and abort. + + common: Check parameters more rigorously. + * g10/parse-packet.c (dbg_copy_all_packets): Check that OUT is not + NULL. + (copy_all_packets): Likewise. + + common: Don't continuing processing on error. + * g10/parse-packet.c (dbg_parse_packet): Also return if parse returns + an error. + (parse_packet): Likewise. + (dbg_search_packet): Likewise. + (search_packet): Likewise. + + common: Better respect the packet's length when reading it. + * g10/parse-packet.c (parse_signature): Make sure PKTLEN doesn't + underflow. Be more careful that a read doesn't read more data than + PKTLEN says is available. + +2015-08-20 Werner Koch + + po: Add lost translation of validity strings. + * po/POTFILES.in (trust.c): Add missing file. + * po/de.po: Changed German validity strings. + * doc/help.de.txt: Ditto. + +2015-08-20 Neal H. Walfield + + g10/parse-packet.c:parse: Try harder to not ignore an EOF. + * g10/parse-packet.c (parse): Be more robust: make sure to process any + EOF. + + g10/parse-packet.c: Replace literal with symbolic expression. + * g10/parse-packet.c (dump_hex_line): Use sizeof rather than the + buffer's size. + + Add documentation for g10/parse-packet.c. + * g10/packet.h: Add documentation for functions defined in + parse-packet.c. + * g10/parse-packet.c: Improve comments for many functions. + + g10/packet.h: Remove unused argument from enum_sig_subpkt. + * g10/packet.h (enum_sig_subpkt): Remove argument RET_N. Update + callers. + * g10/parse-packet.c (enum_sig_subpkt): Remove argument RET_N. + + g10/parse-packet.c:mpi_read: Detect EOF and correct boundary conditions. + * g10/parse-packet.c (mpi_read): Improve documentation. Correctly + handle an EOF. On overflow, correctly return the number of bytes read + from the pipeline. + + common/iobuf.c: Make control flow more obvious. + * common/iobuf.c (iobuf_read): Make control flow more obvious. + (iobuf_get_filelength): Likewise. + (iobuf_get_fd): Likewise. + (iobuf_seek): Likewise. + + common/iobuf.c: Add some sanity checks to catch programmer bugs. + * common/iobuf.c (iobuf_alloc): Check that BUFSIZE is not 0. + (iobuf_readbyte): Check that A is an input filter. Check that the + amount of read data is at most the amount of buffered data. + (iobuf_read): Check that A is an input filter. + (iobuf_writebyte): Check that A is not an input filter. + (iobuf_writestr): Check that A is not an input filter. + (iobuf_flush_temp): Check that A is not an input filter. + + common/iobuf.c:iobuf_write_temp: Elide redundant code. + * common/iobuf.c (iobuf_write_temp): Don't repeat iobuf_flush_temp. + Use it directly. + + common/iobuf.c: Have iobuf_writestr use iobuf_write, not iobuf_writebyte + * common/iobuf.c (iobuf_write): Don't write a byte at a time. Use + iobuf_write. + + common/iobuf: Improve documentation and code comments. + common/iobuf.h: Improve documentation and code comments. + common/iobuf.c: Likewise. + + common/iobuf.c: Adjust buffer size of filters in front of temp filters. + * common/iobuf.c (iobuf_push_filter2): If the head filter is a temp + filter, use IOBUF_BUFFER_SIZE for the new filter. + + common/iobuf.c: Buffered data should not be processed by new filters. + * common/iobuf.c (iobuf_push_filter2): If the pipeline is an output or + temp pipeline, the new filter shouldn't assume ownership of the old + head's internal buffer: the data was written before the filter was + added. + * common/t-iobuf.c (double_filter): New function. + (main): Add test cases for the above bug. + + common/iobuf.c: Flush the pipeline in iobuf_temp_to_buffer. + * common/iobuf.c (iobuf_temp_to_buffer): Flush each filter in the + pipeline and copy the data from the last (not the first) filter's + internal buffer. + + common/iobuf.c: Combine iobuf_open, iobuf_create and iobuf_openrw. + * common/iobuf.c (do_open): New function, which is a generalization of + iobuf_open, iobuf_Create, iobuf_openrw. + (iobuf_open): Call do_open. + (iobuf_create): Likewise. + (iobuf_openrw): Likewise. + + common/iobuf.h: Remove iobuf_open_fd_or_name. + * common/iobuf.h (iobuf_open_fd_or_name): Remove prototype. Replace + use with either iobuf_open or iobuf_fdopen_nc, as appropriate. + * common/iobuf.c (iobuf_open): Remove function. + + common/iobuf.c: Rename iobuf_flush and make it a static function. + * common/iobuf.h (iobuf_flush): Remove prototype. + * common/iobuf.c (filter_flush): New static prototype. + (iobuf_flush): Rename... + (filter_flush): ... to this. Make static. Simplify code. Update + callers. + + common/iobuf.c: Don't abort freeing a pipeline if freeing a filter fails + * common/iobuf.c (iobuf_cancel): Don't abort freeing a pipeline if + freeing a filter fails. This needs to a memory leak. Instead, keep + freeing and return the error code of the first filter that fails. + + common/iobuf.c: Improve iobuf_peek. + * common/iobuf.c (underflow): Take additional parameter + clear_pending_eof. If not set, don't clear a pending eof when + returning EOF. Update callers. + (iobuf_peek): Fill the internal buffer, if needed, to be able to + better satisfy any request. + + common/iobuf.c: When requested, fill the buffer even if it is not empty. + * common/iobuf.c (underflow): Don't require that the buffer be empty. + When called, fill any available space. + + common/t-iobuf.c: Add a test case for multiple EOFs. + common/t-iobuf.c (main): Add a test case for multiple EOFs in an INPUT + pipeline. + + common/iobuf.c: Better respect boundary conditions in iobuf_read_line. + * common/iobuf.c (iobuf_read_line): Be more careful with boundary + conditions. + * common/iobuf.h: Include . + * common/t-iobuf.c: New file. + * common/Makefile.am (module_tests): Add t-iobuf. + (t_mbox_util_LDADD): New variable. + + common/iobuf.c: Fix filter type for iobuf_temp_with_content. + * common/iobuf.c (iobuf_temp_with_content): Set the filter type to + IOBUF_INPUT, not IOBUF_TEMP, which is only for output filters that + write into a dynamic buffer. + + common/iobuf.h: Remove unimplemented prototypes. + * common/iobuf.h (iobuf_unread): Remove unimplemented prototype. + (iobuf_clear_eof): Likewise. + (iobuf_append): Likewise. + + common/iobuf.c: Refactor code to not need the desc field. + * common/iobuf.h (struct iobuf_struct): Remove field desc. + * common/iobuf.c (iobuf_desc): New function. When a filter's + description is needed, use this instead of the filter's desc field. + + common/iobuf.h: Clarify semantics of nofast. Simplify implementation. + * common/iobuf.h (struct iobuf_struct): Clarify semantics of nofast. + Simplify use of nofast to implement just these semantics. + + common/iobuf.c: Remove dead code (directfp). + * common/iobuf.h (struct iobuf_struct): Remove field directfp. Remove + all uses of it. + + common/iobuf.c: Remove dead code (opaque). + * common/iobuf.h (struct iobuf_struct): Remove field opaque. Remove + all uses of it. + + common/iobuf.h: Replace further use of literals with symbolic constants. + * common/iobuf.c: Move BLOCK_FILTER_INPUT, + BLOCK_FILTER_OUTPUT_BLOCK_FILTER_TEMP from here... + * common/iobuf.h: ... to here and rename to IOBUF_INPUT, IOBUF_OUTPUT + and IOBUF_TEMP, respectively. Where appropriate, use these macros + instead of a literal. + +2015-08-17 Werner Koch + + gpg: Avoid linking to Libksba. + * kbx/keybox.h (KEYBOX_WITH_X509): Do not define. + * sm/Makefile.am (AM_CPPFLAGS): Define it here. + (common_libs): Change to libkeybox509.a + * g10/Makefile.am (AM_CFLAGS): remove KSBA_CFLAGS. + (gpg2_LDADD, gpgv2_LDADD): Remove KSBA_LIBS + * kbx/Makefile.am (noinst_LIBRARIES): Add libkeybox509.a. + (libkeybox509_a_SOURCES): New. + (libkeybox_a_CFLAGS): New. + (libkeybox509_a_CFLAGS): New. + (kbxutil_CFLAGS): New. + * kbx/keybox-search.c (has_keygrip) [!KEYBOX_WITH_X509]: Declare args + as unused. + +2015-08-16 Ben Kibbey + + Fix pinentry loopback and passphrase contraints. + * agent/command.c (cmd_get_passphrase): Don't repeat passphrase for + pinentry loopback mode. + * agent/genkey.c (check_passphrase_constraints): Immediately return when + pinentry mode is loopback. + + Fix sending INQUIRE_MAXLEN for symmetric data. + * g10/passphrase.c (passphrase_to_dek_ext): Write the status message. + +2015-08-15 Ben Kibbey + + Inform a user about inquire length limit. + * common/status.h (INQUIRE_MAXLEN): New. + * g10/call-agent.c (default_inquire_cb): Send STATUS_INQUIRE_MAXLEN. + client when inquiring a passphrase over pinentry-loopback. + + Allow --gen-key to inquire a passphrase. + * g10/gpg.c (main): test for --command-fd during --gen-key parse. + + When --command-fd is set then imply --batch to let gpg inquire a + passphrase rather than requiring a pinentry. + +2015-08-11 Werner Koch + + Release 2.1.7. + +2015-08-10 NIIBE Yutaka + + agent: fix ECC key handling. + * agent/cvt-openpgp.c (get_keygrip, convert_secret_key) + (convert_transfer_key): CURVE is the name of curve. + +2015-08-08 Neal H. Walfield + + common/iobuf.c: Replace use of literals with symbolic constants. + * common/iobuf.c (BLOCK_FILTER_INPUT): Define. Where appropriate, use + this instead of a literal. + (BLOCK_FILTER_OUTPUT): Likewise. + (BLOCK_FILTER_TEMP): Likewise. + +2015-08-07 Werner Koch + + gpg: Allow gpgv to work with a trustedkeys.kbx file. + * g10/keydb.h (KEYDB_RESOURCE_FLAG_GPGVDEF): New. + * g10/keydb.c (keydb_add_resource): Take care of new flag. + * g10/gpgv.c (main): Use new flag. + + agent: Add option --force to the DELETE_KEY command. + * agent/findkey.c (agent_delete_key): Add arg "force". + * agent/command.c (cmd_delete_key): Add option --force. + + common: Change alias for Curve25519 to "cv25519". + * common/openpgp-oid.c (oidtable): Change alias. + +2015-08-06 Werner Koch + + gpg: Remove duplicated printing of the curve name in "sub" lines. + * g10/keylist.c (list_keyblock_print): Do not print extra curve name. + + gpg: Add commands "fpr *" and "grip" to --edit-key. + * g10/keyedit.c (cmdGRIP): New. + (cmds): Add command "grip". + (keyedit_menu) : Print subkeys with argument "*". + (keyedit_menu) : Print keygrip. + (show_key_and_fingerprint): Add arg "with_subkeys". + (show_key_and_grip): New. + * g10/keylist.c (print_fingerprint): Add mode 4. + + gpg: Adjust UID line indentation for common key algos. + * g10/keylist.c (list_keyblock_print): Change UID line indentation + * g10/mainproc.c (list_node): Ditto. + +2015-08-06 NIIBE Yutaka + + Curve25519 support. + * agent/cvt-openpgp.c (get_keygrip): Handle Curve25519. + (convert_secret_key, convert_transfer_key): Ditto. + * common/openpgp-oid.c (oidtable): Add Curve25519. + (oid_crv25519, openpgp_oid_is_crv25519): New. + * common/util.h (openpgp_oid_is_crv25519): New. + * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Handle the case + with Montgomery curve which uses x-only coordinate. + * g10/keygen.c (gen_ecc): Handle Curve25519. + (ask_curve): Change the API and second arg is to return subkey algo. + (generate_keypair, generate_subkeypair): Follow chage of ask_curve. + * g10/keyid.c (keygrip_from_pk): Handle Curve25519. + * g10/pkglue.c (pk_encrypt): Handle Curve25519. + * g10/pubkey-enc.c (get_it): Handle the case with Montgomery curve. + * scd/app-openpgp.c (ECC_FLAG_DJB_TWEAK): New. + (send_key_attr): Work with general ECC, Ed25519, and Curve25519. + (get_public_key): Likewise. + (ecc_writekey): Handle flag_djb_tweak. + + common: extend API of openpgp_oid_to_curve for canonical name. + * common/openpgp-oid.c (openpgp_oid_to_curve): Add CANON argument. + * common/util.h: Update. + * g10/import.c (transfer_secret_keys): Follow the change. + * g10/keyid.c (pubkey_string): Likewise. + * g10/keylist.c (list_keyblock_print, list_keyblock_colon): Likewise. + * parse-packet.c (parse_key): Likewise. + * scd/app-openpgp.c (send_key_attr, get_public_key): Likewise. + +2015-08-05 NIIBE Yutaka + + scd: Fix ecc_oid. + * scd/app-openpgp.c (ecc_oid): Call with OIDBUF. + + scd: Fix ECC support. + * scd/app-openpgp.c (send_key_attr): Send KEYNO. + (get_public_key): Fix SEXP composing. + (ecc_writekey): Fix OID length calculation. + (ecc_oid): Prepend the length before query. + (parse_algorithm_attribute): Handle the case the curve is not available. + +2015-08-04 Werner Koch + + gpg: Fix duplicate key import due to legacy key in keyring. + * g10/keydb.c (keydb_search_fpr): Skip legacy keys. + + gpg: Properly handle legacy keys while looking for a secret key. + * g10/getkey.c (have_secret_key_with_kid): Skip legacy keys. + +2015-07-31 Werner Koch + + gpg: Fix endless loop for expired keys given by fpr. + * g10/getkey.c (lookup): Disable keydb caching when continuing a + search. + +2015-07-29 Werner Koch + + gpg: Do not return "Legacy Key" from lookup if a key is expired. + * g10/getkey.c (lookup): Map GPG_ERR_LEGACY_KEY. + + gpg: Indicate secret keys and cards in a key-edit listing. + * g10/keyedit.c (sign_uids): Add arg "ctrl". + (show_key_with_all_names_colon): Ditto. + (show_key_with_all_names): Ditto. + + * g10/keyedit.c (show_key_with_all_names): Print key record + indicators by checking with gpg-agent. + (show_key_with_all_names): Ditto. May now also print sec/sbb. + +2015-07-28 Werner Koch + + gpg: Remove the edit-key toggle command. + * g10/keyedit.c (cmds): Remove helptext from "toggle". + (keyedit_menu): Remove "toggle" var and remove the sub/pub check + against toggle. + + common,w32: Avoid unused var warning about msgcache. + * common/i18n.c (USE_MSGCACHE): New. + (msgcache) [!USE_MSGCACHE]: Do not define. + (i18n_localegettext): Repalce #if conditions by USE_MSGCACHE. + + w32: Try more places to find an installed Pinentry. + * common/homedir.c (get_default_pinentry_name): Re-implement to + support several choices for Windows. + +2015-07-26 Werner Koch + + scd: Fix size_t/unsigned int mismatch. + * scd/app-openpgp.c (ecc_writekey): Use extra var n. + + Replace GNUPG_GCC_A_ macros by GPGRT_ATTR_ macros. + * common/util.h: Provide replacement for GPGRT_ATTR_ macros when using + libgpg-error < 1.20. + * common/mischelp.h: Ditto. + * common/types.h: Ditto. + +2015-07-25 NIIBE Yutaka + + scd: support any curves defined by libgcrypt. + * g10/call-agent.h (struct agent_card_info_s): Add curve field. + * g10/call-agent.c (learn_status_cb): Use curve name. + * g10/card-util.c (card_status): Show pubkey name. + * scd/app-openpgp.c (struct app_local_s): Record OID and flags. + (store_fpr): Use ALGO instead of key type. + (send_key_attr): Use curve name instead of OID. + (get_public_key): Clean up by OID to curve name. + (ecc_writekey): Support any curves in libgcrypt. + (do_genkey, do_auth, ): Follow the change. + (ecc_oid): New. + (parse_algorithm_attribute): Show OID here. + +2015-07-23 Peter Wu + + build: ignore scissor line for the commit-msg hook. + * build-aux/git-hooks/commit-msg: Stop processing more lines when the + scissor line is encountered. + +2015-07-23 NIIBE Yutaka + + scd: Format change to specify "rsa2048" for KEY-ATTR. + * g10/card-util.c (do_change_keysize): Put "rsa". + * scd/app-openpgp.c (change_keyattr, change_keyattr_from_string): + Change the command format. + (rsa_writekey): Check key type. + (do_writekey): Remove "ecdh" and "ecdsa" support which was available + in experimental libgcrypt before 1.6.0. + +2015-07-22 Werner Koch + + Avoid a leading double slash in make_filename. + * common/stringhelp.c (do_make_filename): Special case leading '/'. + +2015-07-21 NIIBE Yutaka + + scd: change_keyattr_from_string for ECC. + * scd/app-openpgp.c (change_keyattr, change_keyattr_from_string): + Support ECC. + (rsa_writekey): Don't change key attribute. + +2015-07-17 NIIBE Yutaka + + scd: Use openpgpdefs.h for constants. + * scd/app-openpgp.c: Include openpgpdefs.h. + +2015-07-16 Neal H. Walfield + + Don't segfault if the first 'auto-key-locate' option is 'clear'. + * g10/getkey.c (free_akl): If AKL is NULL, just return. + +2015-07-10 NIIBE Yutaka + + agent: Support non-NLS build. + * agent/agent.h: Use ENABLE_NLS and define L_() macro. + +2015-07-09 NIIBE Yutaka + + scd: Remove unused files. + * scd/Makefile.am (sc_copykeys_*): Remove. + * scd/sc-copykeys.c: Remove. + * scd/pcsc-wrapper.c: Remove. + * scd/{card-common.h,card-dinsig.c,card-p15.c,card.c}: Remove. + +2015-07-08 NIIBE Yutaka + + g10: Use canonical name for curve. + * g10/import.c (transfer_secret_keys): Use canonical name. + * common/openpgp-oid.c (openpgp_curve_to_oid): Return NULL on error. + * g10/keyid.c (pubkey_string): Follow change of openpgp_curve_to_oid. + * g10/keylist.c (list_keyblock_print, list_keyblock_colon): Ditto. + * g10/parse-packet.c (parse_key): Ditto. + +2015-07-03 Daniel Kahn Gillmor + + drop long-deprecated gpgsm-gencert.sh. + * tools/gpgsm-gencert.sh: remove deprecated script entirely. It is + fully replaced by gpgsm --gen-key + * doc/tools.texi: remove gpgsm-gencert.sh documentation + * .gitignore: no longer ignore gpgsm-gencert.sh manpage + * doc/Makefile.am: quit making the manpage + * tools/Makefile.am: quit distributing the script + * doc/howto-create-a-server-cert.texi: overhaul documentation to use + gpgsm --gen-key and tweak explanations + +2015-07-02 NIIBE Yutaka + + po: Update Japanese translation. + + scd: Support AES decryption for OpenPGPcard v3.0. + * scd/app-openpgp.c (do_decipher): Support AES decryption. + +2015-07-01 Werner Koch + + Release 2.1.6. + +2015-07-01 Daiki Ueno + + agent: Unset INSIDE_EMACS on gpg-agent startup. + * agent/gpg-agent.c (main): Unset INSIDE_EMACS envvar. + +2015-07-01 Werner Koch + + common: Implement i18n_localegettext. + * common/i18n.c (msg_cache_s, msg_cache_head_s): New. + (msgcache): New. + (i18n_localegettext): Implement locale dependent lookup. + +2015-06-30 Daniel Kahn Gillmor + + Pass DBUS_SESSION_BUS_ADDRESS for gnome3. + * common/session-env.c (stdenvnames): Add DBUS_SESSION_BUS_ADDRESS. + +2015-06-30 Werner Koch + + Flag the L_() function with attribute format_arg. + * agent/agent.h (LunderscorePROTO): New. + * common/util.h (GNUPG_GCC_ATTR_FORMAT_ARG): New. + * common/i18n.h (GNUPG_GCC_ATTR_FORMAT_ARG): New. Use for + i18n_localegettext. Expand LunderscorePROTO. + * agent/genkey.c (check_passphrase_constraints): Use xtryasprintf + again to keep the old translations. + + agent: Use different translation func for Pinentry strings. + * po/Makevars (XGETTEXT_OPTIONS): Add keyword "L_". + * common/i18n.c (i18n_localegettext): New stub. + * common/i18n.h: Expand the LunderscoreIMPL macro. + * agent/agent.h (L_): New. + (LunderscoreIMPL): New. + * agent/call-pinentry.c (setup_qualitybar): Add arg ctrl anc change + caller. + * agent/findkey.c (try_unprotect_cb): Add local var ctrl. + * agent/genkey.c (check_passphrase_constraints): Replace xtryasprintf + by xtrystrdup to avoid gcc warning. Unfortinately this changes the + string. + (agent_ask_new_passphrase): Cleanup the use of initial_errtext. + + gpg: Make show-sig-subpackets work again. + * g10/gpg.c (parse_list_options): Fix offset for subpackets. + +2015-06-29 Werner Koch + + agent: Prepare for Libassuan with Cygwin support. + * agent/gpg-agent.c (create_server_socket): Add arg "cygwin". Call + assuan_sock_set_flag if Assuan version is recent enough. + (main): Create ssh server socket with Cygwin flag set. + +2015-06-29 Neal H. Walfield + + Show passphrase constraints errors as password prompt errors. + * agent/agent.h (check_passphrase_constraints): Add parameter + failed_constraint and remove parameter silent. Update callers. + * agent/genkey.c (check_passphrase_constraints): Add parameter + failed_constraint and remove parameter silent. If FAILED_CONSTRAINT + is not NULL and OPT.ENFORCE_PASSPHRASE_CONSTRAINTS is FALSE, save the + error text in *FAILED_CONSTRAINT and don't call take_this_one_anyway + or take_this_one_anyway2. If FAILED_CONSTRAINT is NULL, act as if + SILENT was set. + (agent_ask_new_passphrase): Change initial_errtext's type from a const + char * to a char *. Pass it to check_passphrase_constraints. If it + contains malloc's memory, free it. + +2015-06-29 Neal H. Walfield + + Improve documentation for default-cache-ttl and default-cache-ttl-ssh. + * doc/gpg-agent.texi (Agent Options): Improve documentation for + default-cache-ttl and default-cache-ttl-ssh. + + Don't raise max-cache-ttl to default-cache-ttl. + * agent/gpg-agent.c (finalize_rereadable_options): Don't raise + max-cache-ttl to default-cache-ttl. Likewise for max-cache-ttl-ssh + and default-cache-ttl-ssh. + + Improve the description of old packets with an indeterminate length. + * g10/parse-packet.c (parse): Make the description more accurate when + listing packets: old format packets don't support partial lengths, + only indeterminate lengths (RFC 4880, Section 4.2). + +2015-06-29 Werner Koch + + agent: Add --verify to the PASSWD command. + * agent/command.c (cmd_passwd): Add option --verify. + + agent,w32: Do not create a useless socket with --enable-putty-support. + * agent/agent.h (opt): Remove field ssh_support. + * agent/gpg-agent.c (ssh_support): New. Replace all opt.ssh_support + by this. + (main): Do not set ssh_support along with setting putty_support. + + gpgsm: Add command option "offline". + * sm/server.c (option_handler): Add "offline". + (cmd_getinfo): Ditto. + * sm/certchain.c (is_cert_still_valid): + (do_validate_chain): + * sm/gpgsm.c (gpgsm_init_default_ctrl): Default "offline" to the value + of --disable-dirmngr. + * sm/call-dirmngr.c (start_dirmngr_ext): Better also check for + ctrl->offline. + +2015-06-26 NIIBE Yutaka + + scd: Support button flag and AES key data for OpenPGPcard v3.0. + * scd/app-openpgp.c (do_getattr, show_caps, app_select_openpgp): + Support button and symmetric decryption. + (do_setattr): Support setting AESKEY. + +2015-06-25 Andre Heinecke + + sm: Fix cert storage for ephemeral certs. + * sm/keydb.c (keydb_store_cert): Clear ephemeral flag for + existing certs if store should not be ephemeral. + +2015-06-23 Werner Koch + + Allow use of debug flag names for all tools. + * g13/g13.c: Make use of debug_parse_flag. + * scd/scdaemon.c: Ditto. + * sm/gpgsm.c: Ditto + * agent/gpg-agent.c: Ditto. But do not terminate on "help" + * dirmngr/dirmngr.c: Ditto. + + common: Improve fucntion parse_debug_flag. + * common/miscellaneous.c (parse_debug_flag): Add hack not to call + exit. Add "none" and "all" flags. + +2015-06-23 NIIBE Yutaka + + scd: pinpad workaround for PC/SC implementations. + * scd/adpu.c (pcsc_pinpad_verify, pcsc_pinpad_modify): Bigger buffer + for TPDU card reader. + +2015-06-22 Werner Koch + + gpg: Allow debug flag names for --debug. + * g10/gpg.c (opts): Change arg for oDebug to a string. + (debug_flags): New; factored out from set_debug. + (set_debug): Remove "--debug-level help". Use parse_debug_flag to + print the used flags. + (main): Use parse_debug_flag for oDebug. + + common: Add function parse_debug_flag. + * common/miscellaneous.c (parse_debug_flag): New. + * common/util.h (struct debug_flags_s): New. + + common: Add function strtokenize. + * common/stringhelp.c: Include assert.h. + (strtokenize): New. + * common/t-stringhelp.c (test_strtokenize): New. + + gpg: Fix regression due to recent commit 6500f33. + * g10/keydb.c (kid_list_s): Keep a state in the table. + (kid_not_found_table): Rename to kid_found_table. + (n_kid_not_found_table): Rename to kid_found_table_count. + (kid_not_found_p): Return found state. + (kid_not_found_insert): Add arg found. + (keydb_search): Store found state in the table. + +2015-06-22 NIIBE Yutaka + + scd: Fix Cherry ST-2000 support for pinpad input. + * scd/apdu.c (pcsc_vendor_specific_init): Set pinmax to 15. + * scd/ccid-driver.c (ccid_transceive_secure): Add zero for the + template of APDU. + +2015-06-20 Werner Koch + + gpg: Print number of good signatures with --check-sigs. + * g10/keylist.c (keylist_context): Add field good_sigs. + (list_keyblock_print): Updated good_sigs. + (print_signature_stats): Print number of good signatures and use + log_info instead of tty_printf. + + gpg: Improve speed of --check-sigs and --lish-sigs. + * g10/keydb.c (kid_list_t): New. + (kid_not_found_table, n_kid_not_found_table): New. + (kid_not_found_p, kid_not_found_insert, kid_not_found_flush): New. + (keydb_insert_keyblock): Flush the new cache. + (keydb_delete_keyblock): Ditto. + (keydb_update_keyblock): Ditto. + (keydb_search): Use the new cache. + (keydb_dump_stats): New. + * g10/gpg.c (g10_exit): Dump keydb stats. + +2015-06-19 Werner Koch + + gpg: Add more log_clock calls to keydb.c. + * g10/keydb.c (keydb_get_keyblock): Add log_clock calls. + + gpg: Print available debug flags using "--debug-level help". + * g10/gpg.c (set_debug): Add "help" option and use a table for the + flags. + + gpg: Fix export problem in case an old keyring has PGP-2 keys. + * g10/export.c (do_export_stream): Skip legacy keys. + +2015-06-18 Werner Koch + + dirmngr: Fix the cleanup zombies fix (685b782). + * dirmngr/ldap-wrapper.c (ldap_wrapper_thread): Do not close the + stdout reader after EOF from read_log_data. + * dirmngr/crlcache.c (crl_cache_reload_crl): Close the reader before + the next iteration. + +2015-06-17 Werner Koch + + agent: Print a warning for obsolete options. + * g10/misc.c (obsolete_scdaemon_option): Move to + * common/miscellaneous.c (obsolete_option): ... here. + * agent/gpg-agent.c (main): Use obsolete_option for the 3 obsolete + options. + +2015-06-16 Werner Koch + + dirmngr: Cleanup zombies and fix hang on shutdown. + * dirmngr/ldap-wrapper.c (ldap_wrapper_thread): Move nfds computation + into the loop. Check the queue also on timeout. Close log_fd and + reader context on EOF or error. + + dirmngr: Avoid accessing uninitialized memory in log callback. + * dirmngr/dirmngr.c (pid_suffix_callback): Clear int_and_ptr_u before + use. + (start_connection_thread): Ditto. + (handle_connections): Ditto. + +2015-06-16 Neal H. Walfield + + Don't prompt for the password multiple times in pinentry loopback mode. + * g10/gpg.c (main): If OPT.PINENTRY_MODE is PINENTRY_MODE_LOOPBACK, + clear OPT.PASSPHRASE_REPEAT. + +2015-06-16 NIIBE Yutaka + + po: Update Japanese Translation. + +2015-06-15 Werner Koch + + doc: Add defs.inc to BUILT_SOURCES. + +2015-06-11 Werner Koch + + Release 2.1.5. + + agent: Fix --extra-socket on Windows. + * agent/gpg-agent.c (start_connection_thread): Rename to ... + (do_start_connection_thread): this. Factor nonce checking out to ... + (start_connection_thread_std): this, + (start_connection_thread_extra): this, + (start_connection_thread_browser): and this. + + agent: Add experimental option --browser-socket. + * agent/agent.h (opt): Add field "browser_socket". + * agent/command.c (cmd_setkeydesc): Use a different message for + restricted==2. + * agent/gpg-agent.c (oBrowserSocket): New. + (opts): Add --browser-socket. + (socket_name_browser, redir_socket_name_browser): New. + (socket_nonce_browser): New. + (cleanup): Cleanup browser socket. + (main): Implement option. + (start_connection_thread_browser): New. + (handle_connections): Add arg listen_fd_browser and use it. + +2015-06-10 Daiki Ueno + + agent: Add option --allow-emacs-pinentry. + * agent/agent.h (opt): Add field allow_emacs_pinentry. + * agent/call-pinentry.c (start_pinentry): Act upon new var. + * agent/gpg-agent.c (oAllowEmacsPinentry): New. + (opts): Add option --allow-emacs-pinentry. + (parse_rereadable_options): Set this option. + * tools/gpgconf-comp.c (gc_options_gpg_agent): Add new option. + +2015-06-09 Werner Koch + + doc: Do not used fixed file names in the manuals. + * doc/mkdefsinc.c: New. + * doc/Makefile.am: Include cmacros.am. + (EXTRA_DIST): Add mkdefsinc.c defsincdate. + (BUILT_SOURCES): Add defsincdate + (CLEANFILES): Add mkdefsinc and defs.inc. + (mkdefsinc): New rule. + (yat2m-stamp): Depend on defs.inc. + ($(myman_pages) gnupg.7): Ditto. + (gnupg.texi): Remove rule to touch itself. + (dist-hook): New. + (defsincdate): New. + (defs.inc): New. + * doc/gnupg.texi: Remove inclusion of version.texi. Include defs.inc. + Also include defs.inc in all files used to build man files. Change + fixed directory names to those from defs.inc. + + dirmngr: Avoid crash due to an empty crls.d/DIR.txt. + * dirmngr/crlcache.c (check_dir_version): Avoid segv. + +2015-06-08 Werner Koch + + doc: Change the manual source to be only for GnuPG 2.1. + + Convey envvar INSIDE_EMACS to the pinentry. + * common/session-env.c (stdenvnames): Add it. + + agent: Add command "getinfo std_env_names". + * agent/command.c (cmd_getinfo): Add new sub-command. + +2015-06-05 NIIBE Yutaka + + scd: do_decipher change for OpenPGPcard v3.0. + * scd/app-openpgp.c (do_decipher): Add a header for ECDH. + +2015-06-04 Werner Koch + + gpg: Replace -1 by GPG_ERR_NOT_FOUND in tdbio.c. + * g10/tdbio.c (lookup_hashtable): Return GPG_ERR_NOT_FOUND. + * g10/tdbdump.c (import_ownertrust): Test for GPG_ERR_NOT_FOUND. + * g10/trustdb.c (read_trust_record): Ditto. + (tdb_get_ownertrust, tdb_get_min_ownertrust): Ditto. + (tdb_update_ownertrust, update_min_ownertrust): Ditto. + (tdb_clear_ownertrusts, update_validity): Ditto. + (tdb_cache_disabled_value): Ditto. + + gpg: Cleanup error code path in case of a bad trustdb. + * g10/tdbio.c (tdbio_read_record): Fix returning of the error. + + gpg: Fix output in case of a corrupted trustdb. + * g10/tdbdump.c (list_trustdb): Add arg FP and change callers to pass + es_stdout. + * g10/tdbio.c (upd_hashtable): On a corrupted trustdb call + list_trustdb only in verbose > 1 mode and let it dump to stderr. + +2015-05-29 NIIBE Yutaka + + scd: Fix key template of ECC. + * scd/app-openpgp.c (build_ecc_privkey_template): Use correct value. + +2015-05-28 NIIBE Yutaka + + g10: Fix a race condition initially creating trustdb. + * g10/tdbio.c (take_write_lock, release_write_lock): New. + (put_record_into_cache, tdbio_sync, tdbio_end_transaction): Use + new lock functions. + (tdbio_set_dbname): Fix the race. + (open_db): Don't call dotlock_create. + +2015-05-27 NIIBE Yutaka + + g10: Remove g10/signal.c. + * g10/signal.c: Remove. + * g10/main.h: Remove old function API. + * g10/tdbio.c: Use new API, even in the dead code. + +2015-05-20 Werner Koch + + agent: Cleanup caching code for command GET_PASSPHRASE. + * agent/command.c (cmd_get_passphrase): Read from the user cache. + +2015-05-19 Neal H. Walfield + + agent: When the password cache is cleared, also clear the ext. cache. + * agent/agent.h (agent_clear_passphrase): New declaration. + * agent/call-pinentry.c (agent_clear_passphrase): New function. + * agent/command.c (cmd_clear_passphrase): Call agent_clear_passphrase. + + agent: Modify agent_clear_passphrase to support an ext. password cache. + * agent/agent.h (agent_get_passphrase): Add arguments keyinfo and + cache_mode. Update callers. + * agent/call-pinentry.c (agent_get_passphrase): Add arguments keyinfo + and cache_mode. If KEYINFO and CACHE_MODE describe a cachable key, + then send SETKEYINFO to the pinentry. + +2015-05-19 NIIBE Yutaka + + g10: detects public key encryption packet error properly. + g10/mainproc.c (proc_pubkey_enc): Only allow relevant algorithms for + encryption. + +2015-05-15 Werner Koch + + build: Make --disable-gpgsm work. + * Makefile.am: Always build kbx/ + * g10/Makefile.am (AM_CFLAGS): Include KSBA_CFLAGS. + +2015-05-12 Werner Koch + + Release 2.1.4. + + speedo: Add make option SELFCHECK=0 to build a new release. + * build-aux/getswdb.sh: Add option --skip-selfcheck. + * build-aux/speedo.mk: Add option SELFCHECK. + +2015-05-11 Werner Koch + + common: Cope with AIX problem on number of open files. + * common/exechelp-posix.c: Limit returned value for too hight values. + + gpg-connect-agent: Fix quoting of internal percent+ function. + * tools/gpg-connect-agent.c (get_var_ext) + + gpg: Avoid cluttering stdout with trustdb info in verbose mode. + * g10/trustdb.c (validate_keys): Call dump_key_array only in debug + mode. + + gpg: Fix wrong output in list mode. + * g10/parse-packet.c (parse_gpg_control): Replace puts by es_fputs to + LISTFP. + + gpg: New command --quick-adduid. + * g10/keygen.c (ask_user_id): Factor some code out to ... + (uid_already_in_keyblock): new. + (generate_user_id): Add arg UIDSTR. Fix leaked P. + * g10/keyedit.c (menu_adduid): Add new arg uidstring. Adjust caller. + (keyedit_quick_adduid): New. + * g10/gpg.c (aQuickAddUid): New. + (opts): Add command --quick-adduid. + (main): Implement that. + + gpg: Add push/pop found state feature to keydb. + * g10/keydb.c (keydb_handle): Add field saved_found. + (keydb_new): Init new field. + (keydb_push_found_state, keydb_pop_found_state): New. + * g10/keyring.c (kyring_handle): Add field saved_found. + (keyring_push_found_state, keyring_pop_found_state): New. + + gpg: Minor code merging in keyedit. + * g10/keyedit.c (fix_keyblock): Rename to fix_key_signature_order. + (fix_keyblock): New. Call fix_key_signature_order and other fix + functions. + (keyedit_menu): Factor code out to new fix_keyblock. + (keyedit_quick_sign): Ditto. Check for primary fpr before calling + fix_keyblock. + +2015-05-07 Werner Koch + + agent: Minor change for 56b5c9f. + * agent/call-pinentry.c (agent_askpin): Move option setting to ... + (start_pinentry): here. Fix error code check. + +2015-05-07 Kristian Fiskerstrand + + dirmngr: Fix segfault in ldap engine. + (ks-engine-ldap.c) Fix segfault caused by missing check whether uri is + initialized + +2015-05-07 Neal H. Walfield + + agent: Improve support for externally cached passwords. + * agent/call-pinentry.c (PINENTRY_STATUS_PASSWORD_FROM_CACHE): New + constant. + (pinentry_status_cb): Add it to *FLAGS if PASSWORD_FROM_CACHE was + provided. + (agent_askpin): Pass "OPTION allow-external-password-cache" to the + pinentry. Always pass SETKEYINFO to the pinentry. If there is no + stable identifier, then use "--clear". If the password is incorrect + and PINENTRY_STATUS_PASSWORD_FROM_CACHE is set in *PINENTRY_STATUS, + then decrement PININFO->FAILED_TRIES. + + agent: Or in the value; don't overwrite the variable. + * agent/call-pinentry.c (pinentry_status_cb): Or in + PINENTRY_STATUS_CLOSE_BUTTON; don't overwrite *FLAG. + + agent: Avoid magic numbers. Use more accurate names. + * agent/call-pinentry.c (PINENTRY_STATUS_CLOSE_BUTTON): New constant. + (PINENTRY_STATUS_PIN_REPEATED): Likewise. + (close_button_status_cb): Rename from this... + (pinentry_status_cb): ... to this. Use the constants. + (agent_askpin): Rename local variable from close_button to + pinentry_status. Use symbolic constants rather than magic numbers. + +2015-05-07 Werner Koch + + gpg: Improve 'General key info' line of --card-status. + * g10/keylist.c (print_pubkey_info): Print either "pub" or "sub". + + * g10/getkey.c (get_pubkey_byfprint): Add optional arg R_KEYBLOCK. + * g10/keyid.c (keyid_from_fingerprint): Adjust for change. + * g10/revoke.c (gen_desig_revoke): Adjust for change. + * g10/card-util.c (card_status): Simplify by using new arg. Align + card-no string. + + * g10/card-util.c (card_status): Remove not used GnuPG-1 code. + + gpg: Fix regression not displaying the card serial number. + * g10/call-agent.c (keyinfo_status_cb): Detect KEYINFO. + +2015-05-06 Werner Koch + + speedo,w32: Install a native pinentry. + * build-aux/speedo.mk: Always build pinentry for w32. + (speedo_pkg_pinentry_configure): Adjust to modern pinentry. + * build-aux/speedo/w32/inst.nsi: Install native pinentry under the + name pinentry-basic.exe. + +2015-05-01 NIIBE Yutaka + + g10: fix cmp_public_key. + * g10/free-packet.c (cmp_public_keys): Compare opaque + data at the first entry of the array when it's unknown algo. + +2015-04-30 NIIBE Yutaka + + scd: PC/SC reader selection by partial string match. + * scd/apdu.c (open_pcsc_reader_direct): Partial string match. + +2015-04-24 Werner Koch + + common: Remove JNLIB from boiler plate (jnlib merge). + * common/README.jnlib: Remove. + + common: Rename log and gcc attribute macros (jnlib merge). + * common/logging.h: Rename JNLIB_LOG_* to GPGRT_LOG_*. + * common/mischelp.h: Rename JNLIB_GCC_* to GPGRT_GCC_*. + + common: Remove two JNLIB_ macros (jnlib merge). + * configure.ac: Merge seperate jnlib checks. + (HAVE_JNLIB_LOGGING): Remove. + * common/logging.c, common/simple-pwquery.c (JNLIB_NEED_AFLOCAL): + Rename to GNUPG_COMMON_NEED_AFLOCAL. Change all tests. + + common: Remove libjnlib-config.h (jnlib merge). + * common/libjnlib-config.h: Remove. + * common/common-defs.h (getenv) [HAVE_GETENV]: New. From removed + header. + (getpid) [HAVE_W32CE_SYSTEM]: New. From removed header. + * common/argparse.c: Include util.h and common-defs.h. Replace + jnlib_ macro names for non-GNUPG builds by x* names. + * common/dotlock.c: Ditto. + * common/logging.c: Include util.h and common-defs.h. Replace jnlib_ + symbol names by x* names. + * common/strlist.c: Ditto. + * common/utf8conv.c: Ditto. + * common/w32-reg.c: Ditto. + * common/mischelp.c: Ditto. Also remove _jnlib_free. + * common/stringhelp.c: Ditto. + (JNLIB_LOG_WITH_PREFIX): Do not depend on this macro. + * common/logging.h (JNLIB_LOG_WITH_PREFIX): Do not depend on this + macro. + +2015-04-23 Werner Koch + + gpg: Move all DNS access to Dirmngr. + * common/dns-cert.h: Move to ../dirmngr/. + * common/dns-cert.c: Move to ../dirmngr/. Change args to return the + key as a buffer. + * common/t-dns-cert.c: Move to ../dirmngr/. + * common/pka.c, common/pka.h, common/t-pka.c: Remove. + + * dirmngr/server.c (data_line_cookie_write): Factor code out to + data_line_write and make it a wrapper for that. + (data_line_write): New. + (cmd_dns_cert): New. + (register_commands): Register new command. + + * g10/Makefile.am (LDADD): Remove DNSLIBS. + * g10/call-dirmngr.c (dns_cert_parm_s): New. + (dns_cert_data_cb, dns_cert_status_cb): New. + (gpg_dirmngr_dns_cert): New. + (gpg_dirmngr_get_pka): New. + * g10/gpgv.c (gpg_dirmngr_get_pka): New dummy function. + * g10/keyserver.c (keyserver_import_cert): Replace get_dns_cert by + gpg_dirmngr_dns_cert. + (keyserver_import_pka): Replace get_pka_info by gpg_dirmngr_get_pka. + * g10/mainproc.c: Include call-dirmngr.h. + (pka_uri_from_sig): Add CTX arg. Replace get_pka_info by + gpg_dirmngr_get_pka. + + common: Minor change of hex2str to allow for embedded nul. + * common/convert.c (hex2str): Set ERRNO. Return adjusted COUNT. + +2015-04-23 NIIBE Yutaka + + common: removal of t-support.c from t_jnlib_src. + * common/Makefile.am (t_jnlib_src): Remove t-support.c. + +2015-04-21 Werner Koch + + gpg: Make keyserver-option http_proxy work. + * g10/options.h (opt): Add field keyserver_options.http_proxy. + * g10/keyserver.c (warn_kshelper_option): Add arg noisy. + (parse_keyserver_options): Parse into new http_proxy field. + * g10/call-dirmngr.c (create_context): Send the http-proxy option. + + common: Make proper use of http proxy parameter. + * common/http.c (is_hostname_port): New. + (send_request): Fix proxy name parsing. + + dirmngr: Add http proxy support for keyservers. + * dirmngr/dirmngr.h (server_control_s): Add field http_proxy. + * dirmngr/dirmngr.c (dirmngr_init_default_ctrl): Copy http_proxy value + from OPT. + (dirmngr_deinit_default_ctrl): New. + (main): Call dirmngr_deinit_default_ctrl. + * dirmngr/server.c (start_command_handler): Ditto. + (option_handler): Add option "http-proxy". + * dirmngr/crlfetch.c (crl_fetch): Take http_proxy from CTRL. + * dirmngr/ocsp.c (do_ocsp_request): Ditto. + * dirmngr/ks-engine-hkp.c (send_request): Add proxy support. + * dirmngr/ks-engine-http.c (ks_http_fetch): Ditto. + + gpg: Do not use honor-keyserver-url sub-option by default. + + gpg: Make preferred keyservers work. + * g10/call-dirmngr.c (dirmngr_local_s): Add field set_keyservers_done. + (create_context): Move keyserver setting to ... + (open_context): here. + (clear_context_flags): New. + (gpg_dirmngr_ks_get): Add arg override_keyserver. + * g10/keyserver.c (keyserver_refresh): Improve diagnostics. + (keyserver_get_chunk): Ditto. Pass OVERRIDE_KEYSERVER to ks_get. + + gpg: Update sub-options of --keyserver-options. + * g10/options.h (KEYSERVER_HTTP_PROXY): New. + (KEYSERVER_USE_TEMP_FILES, KEYSERVER_KEEP_TEMP_FILES): Remove. + (KEYSERVER_TIMEOUT): New. + * common/keyserver.h (KEYSERVER_TIMEOUT): Remove. + * g10/keyserver.c (keyserver_opts): Remove obsolete "use-temp-files" + and "keep-temp-files". Add "http-proxy" and "timeout". + (parse_keyserver_options): Remove 1.2 compatibility option + "honor-http_proxy". Remove "use-temp-files" and "keep-temp-files" + code. + +2015-04-14 Werner Koch + + agent: Send the new SETKEYINFO command to the Pinentry. + * agent/call-pinentry.c (agent_askpin): Add args keyinfo and + cache_mode. Change all callers to pass (NULL,0) for them. Send + SETKEYINFO command. + * agent/findkey.c (unprotect): Pass the keygrip and the cache_mode for + the new args. + +2015-04-14 NIIBE Yutaka + + scd: better handling of extended APDU. + * scd/apdu.c (send_le): Bug fix for not append Z when lc<0&&le<0. + * scd/app-common.h (struct app_ctx_s): Use bit fields for flags. + * scd/ccid-driver.c (CCID_MAX_BUF): New. Only for OpenPGPcard. + (struct ccid_driver_s): New field of max_ccid_msglen. + Remove ifsd field. + (parse_ccid_descriptor): Initialize max_ccid_msglen. + (ccid_transceive_apdu_level): Implement sending extended APDU in + chain of CCID message. + +2015-04-13 Werner Koch + + gpg: Fix NULL-segv due to invalid imported data. + * g10/free-packet.c (my_mpi_copy): New. + (copy_public_key, copy_signature): Use instead of mpi_copy. + +2015-04-13 Neal H. Walfield + + dirmngr: If LDAP is not enable, don't build the LDAP bits. + * dirmngr/Makefile.am (dirmngr_SOURCES): Only include + ks-engine-ldap.c, ldap-parse-uri.c and ldap-parse-uri.h if USE_LDAP + is TRUE. + (module_tests): Only add t-ldap-parse-uri if USE_LDAP is TRUE. + * dirmngr/ks-action.c: Only include "ldap-parse-uri.h" if USE_LDAP is + TRUE. + (ks_action_help): Don't invoke LDAP functionality if USE_LDAP is not + TRUE. + (ks_action_search): Likewise. + (ks_action_get): Likewise. + (ks_action_put): Likewise. + * dirmngr/server.c: Only include "ldap-parse-uri.h" if USE_LDAP is + TRUE. + (cmd_keyserver): Don't invoke LDAP functionality if USE_LDAP is not + TRUE. + +2015-04-13 Werner Koch + + common: Do without nested fucntions to support non-gcc. + * common/t-stringhelp.c (test_strsplit): Remove nested function. + +2015-04-11 Werner Koch + + Release 2.1.3. + +2015-04-11 Yuri Chornoivan + + po: Update Ukrainian translation. + +2015-04-11 Ineiev + + po: Update and review Russian translation. + +2015-04-10 Werner Koch + + dirmngr,w32: Make it build for Windows. + * dirmngr/Makefile.am (t_common_ldadd): Add missing libs. + + Remove obsolete directories from AM_CPPFLAGS. + + dirmngr,w32: Replace functions not available under Windows. + * dirmngr/ks-engine-ldap.c (extract_attributes): Replace isoptime and + gmtime_r. + + common: Add new function gnupg_gmtime. + * common/gettime.c (gnupg_gmtime): New. + (gnupg_get_isotime): Use it. Also take care of an gmtime_t returning + an error. + + common: Add new function isodate_human_to_tm. + * common/gettime.c (isotime_human_p): Add arg date_only. + (isodate_human_to_tm): New. + * common/t-gettime.c (test_isodate_human_to_tm): New. + (main): Call new test. + + dirmngr,w32: Avoid name clash with existing function. + * dirmngr/ks-engine-ldap.c (ldap_connect): Rename to my_ldap_connect. + + gpgparsemail: Fix last commit (3f2bdac) + * tools/rfc822parse.c (parse_field): Replace break by goto. + +2015-04-09 Werner Koch + + gpgparsemail: Fix case of zero length continuation lines. + * tools/rfc822parse.c (parse_field): Loop after continuation line. + +2015-04-08 Werner Koch + + sm: Fix certificate lookup in dirmngr cache. + * sm/call-dirmngr.c (get_cached_cert): Fix typo in LOOKUP command. + +2015-04-06 Werner Koch + + gpg: Print the user id in --fast-list-mode. + * g10/keylist.c (list_keyblock_print, list_keyblock_colon): Change. + + gpg: Prepare to pass additional context to the list functions. + * g10/keylist.c (struct sig_stats): Rename to keylist_context and add + field check_sigs. + (keylist_context_release): New. + (list_all): Set listctx.check_sigs and call release func. + (list_one): Ditto. + (locate_one): Ditto. + (list_keyblock_print): Use .check_sigs field. Repalce arg opaque by + listctx. + (list_keyblock): Ditto. Make static. + (list_keyblock_direct): New. + * g10/keygen.c (do_generate_keypair): Replace list_keyblock by + list_keyblock_direct. + + gpg: Merge duplicated code for get_user_id et al. + * g10/getkey.c (get_user_id_string): Add args mode and r_LEN. + (get_user_id_string_native): Add new args. + (get_long_user_id_string, get_user_id): Rewrite using + get_user_id_string. + + gpg: Add new option --debug-iolbf. + * g10/gpg.c (oDebugIOLBF): new. + (opts): Add --debug-iolbf. + (main): Set option. + + Rename DBG_ASSUAN to DBG_IPC and add separate DBG_EXTPROG. + * g10/options.h (DBG_EXTPROG_VALUE): Separate from DBG_IPC_VALUE. + + Fix use of DBG_CACHE and DBG_LOOKUP. + * dirmngr/dirmngr.h (DBG_LOOKUP_VALUE): Change to 8192. + * g10/options.h (DBG_LOOKUP_VALUE, DBG_LOOKUP): New. + * g10/getkey.c: Use DBG_LOOKUP instead of DBG_CACHE at most places. + + gpg: Rename a debug macro. + * g10/options.h (DBG_CIPHER_VALUE): Rename to DBG_CRYPTO_VALUE. + (DBG_CIPHER): Rename to DBG_CRYPTO. + +2015-04-05 Werner Koch + + gpg: Fix DoS while parsing mangled secret key packets. + * g10/parse-packet.c (parse_key): Check PKTLEN before calling mpi_read + et al. + +2015-04-03 NIIBE Yutaka + + g10: Fix keytocard. + g10/call-agent.h (agent_scd_learn): Add FORCE option. + g10/call-agent.c (agent_scd_learn): Implement FORCE option. + g10/keygen.c (gen_card_key): Follow the change of option. + g10/card-util.c (change_pin, card_status, factory_reset): Likewise. + g10/keyedit.c (keyedit_menu): Update private key storage by + agent_scd_learn. + + agent: Add --force option for LEARN. + * agent/command.c (cmd_learn): Handle --force option. + (cmd_keytocard): Don't update key storage file. + * agent/agent.h (agent_handle_learn): Add FORCE. + * agent/learncard.c (agent_handle_learn): Implement FORCE to update + key stroage file. + +2015-03-31 Neal H. Walfield + + dirmngr: Don't use alloca. + * dirmngr/ks-engine-ldap.c (ks_ldap_put): Replace use of alloca with + xmalloc and xfree. + + dirmngr: Simplify truncation of long strings in debug code. + * dirmngr/ks-engine-ldap.c (modlist_dump): Simplify truncation of long + strings. + + dirmngr: Use a better error code. + * dirmngr/ldap-parse-uri.c (ldap_parse_uri): On error, return + GPG_ERR_GENERAL, not GPG_ERR_ASS_GENERAL. + + dirmngr: Better encapsulate the keyservers variable. + * dirmngr/dirmngr.h (struct server_control_s): Move field keyservers + from here... + * dirmngr/server.c (struct server_local_s): ... to here. Update + users. + * dirmngr/ks-action.h (ks_action_resolve): Add argument keyservers. + (ks_action_search): Likewise. + (ks_action_get): Likewise. + (ks_action_put): Likewise. + * dirmngr/ks-action.c (ks_action_resolve): Add argument keyservers. + Use it instead of ctrl->keyservers. + (ks_action_search): Likewise. + (ks_action_get): Likewise. + (ks_action_put): Likewise. + +2015-03-28 Neal H. Walfield + + gpg: Only use the last specified keyserver. + * g10/gpg.c (main): Only use the last specified keyserver. + +2015-03-25 Werner Koch + + dirmngr: Fix resource leaks and check rare errors. + * dirmngr/ks-engine-ldap.c (keyspec_to_ldap_filter): Fix resource + leak. + (ks_ldap_search): Check error from es_fopenmem. Use LDAP_ERR where + required. + (modlist_dump): Check error from es_fopenmem. + (uncescape): s/int/size_t/. Use existing macros. + (extract_attributes): Use existing trim function. + (ks_ldap_put): Do not segv on error from modlist_dump. + + dirmngr: Minor cleanups. + * dirmngr/ks-engine-ldap.c [__riscos__]: Remove doubled util.h. + (ldap_to_gpg_err): s/GPG_ERR_GENERAL/GPG_ERR_INTERNAL/. + (tm2ldaptime): Use snprintf. + (ldap_connect): Get error code prior to log_error and and use modern + function. Use xfree, xtrustrdup etc. + (modlist_lookup): Use GNUPG_GCC_A_USED. + (modlist_free): Use xfree. + + common: Add macro GNUPG_GCC_A_USED. + * common/util.h (GNUPG_GCC_A_USED): New. + + sm: Change default algos to SHA256 (CSR) and AES128 (bulk encryption). + * sm/certreqgen.c (create_request): Change default hash algo. + * sm/gpgsm.c (DEFAULT_CIPHER_ALGO): Change default bulk cipher algo. + +2015-03-24 Werner Koch + + gpg,w32: Handle forward slash in --keyring option. + * g10/keydb.c (keydb_add_resource): Allow forward slash under Windows. + +2015-03-23 Neal H. Walfield + + Improve documentation for ks_hkp_get. + * dirmngr/ks-engine-hkp.c (ks_hkp_get): Improvement documentation. + + Improve documenation of http_parse_uri. + * common/http.c (http_parse_uri): Improve documentation. + + Add support to talking to LDAP key servers. + * g10/call-dirmngr.c (record_output): New function. + (ks_put_inq_cb): Use it here to generate a --with-colons like output + instead of a custom format. + * dirmngr/ks-action.c: Include "ldap-parse-uri.h". + (ks_action_help): If the provided URI is an LDAP URI, then use + ldap_parse_uri to parse. Call ks_ldap_help. + (ks_action_search): If passed an LDAP URI, then call ks_ldap_search. + (ks_action_get): Likewise. + (ks_action_put): Likewise. Also, change data from a 'const void *' to + a 'void *' and add info and infolen parameters. Add note that + function may modify DATA. + * dirmngr/ks-action.h (ks_action_put): Update declaration accordingly. + * dirmngr/server.c: Include "ldap-parse-uri.h". + (cmd_keyserver): If ITEM->URI is an LDAP URI, parse it using + ldap_parse_uri. + (hlp_ks_put): Improve documentation. + (cmd_ks_put): Also pass info and infolen to ks_action_put. Improve + documentation. + * dirmngr/ks-engine.h (ks_ldap_help): New declaration. + (ks_ldap_search): Likewise. + (ks_ldap_get): Likewise. + (ks_ldap_put): Likewise. + * dirmngr/ks-engine-ldap.c: New file. + * dirmngr/Makefile.am (dirmngr_SOURCES): Add ks-engine-ldap.c, + ldap-parse-uri.c and ldap-parse-uri.h. + (dirmngr_LDADD) [USE_LDAP]: Add $(ldaplibs). + + Import _gpgme_parse_timestamp from gpgme as parse_timestamp. + * common/gettime.h (parse_timestamp): New declaration. + * common/gettime.c (_win32_timegm): New function imported from + gpgme/src/conversion.c:_gpgme_timegm. + (parse_timestamp): New function imported from + gpgme/src/conversion.c:_gpgme_parse_timestamp. + + Move copy_stream function to misc.c. + * dirmngr/ks-action.c (copy_stream): Move function from here... + * dirmngr/misc.c (copy_stream): ... to here and drop the static + qualifier. + * dirmngr/misc.h (copy_stream): Add declaration. + + Move armor_data to misc.c. + * dirmngr/ks-engine-hkp.c (armor_data): Move function from here... + * dirmngr/misc.c (armor_data): ... to here and drop static qualifier. + * dirmngr/misc.h: New declaration. + + Add new LDAP utility functions. + * dirmngr/Makefile.am (module_tests): New variable. + (noinst_PROGRAMS): New primary. Set it to $(module_tests). + (TESTS): New variable. Set it to $(module_tests). + (t_common_src): New variable. + (t_common_ldadd): Likewise. + (t_ldap_parse_uri_SOURCES): New primary. + (t_ldap_parse_uri_LDADD): Likewise. + * dirmngr/ldap-parse-uri.c: New file. + * dirmngr/ldap-parse-uri.h: Likewise. + * dirmngr/t-ldap-parse-uri.c: Likewise. + * dirmngr/t-support.h: Likewise. + + Add new function uri_query_lookup. + * common/http.h (uri_query_lookup): New declaration. + * common/http.c (uri_query_lookup): The corresponding implementation. + + Add new function strlist_find. + * common/strlist.h (strlist_find): New declaration. + * common/strlist.c (strlist_find): New function. + + common: Add new helper function, strsplit. + * common/stringhelp.h (strsplit): New declaration. + * common/stringhelp.c (strsplit): New function. + * common/t-stringhelp.c (test_strsplit): New function. + (main): Call it here. + +2015-03-20 Werner Koch + + gpg: Consider a mailbox only userid in mail search mode. + * kbx/keybox-search.c: Include mbox-util.h. + (blob_cmp_mail): Improve OpenPGP uid parsing. + + common: Add function is_valid_mailbox_mem. + * common/mbox-util.c (mem_count_chr): New. + (my_memstr): New. + (has_invalid_email_chars): Change args to work on a buffer. + (is_valid_mailbox_mem): New. + (is_valid_mailbox): Rewrite to use is_valid_mailbox_mem. + + gpg: Find keys using mail addresses with garbage after the '>' + * kbx/keybox-search.c (blob_cmp_mail): Stop comparing at the '>'. + + common: Fix syntax error when building with gnutls. + * common/http.c (send_request): Add missing comma. + +2015-03-19 Werner Koch + + gpg: Emit status line NEWSIG before signature verification starts. + * g10/mainproc.c (check_sig_and_print): Emit STATUS_NEWSIG. + + agent: Compute correct MPI length header for protected ECC keys. + * agent/cvt-openpgp.c (apply_protection): Strip leading zeroes from + opaque MPIs to comply with the OpenPGP spec. + + hkps: Fix host name verification when using pools. + * common/http.c (send_request): Set the requested for SNI. + * dirmngr/ks-engine-hkp.c (map_host): Return the poolname and not + the selecting a host. + + Define replacement error codes from libgpg-error 1.19. + * common/util.h: Add GPG_ERR_LDAP codes for libgpg-error < 1.19. + +2015-03-17 Andre Heinecke + + gpgtar: Fix extracting files with !(size % 512) + * tools/gpgtar-extract.c (extract_regular): Handle size multiples + of RECORDSIZE. + +2015-03-17 Werner Koch + + common: Add feature to ease using argparse's usage(). + * common/argparse.c (show_help): Take care of flag value + (usage): Ditto. + + common: Allow standalone build of argparse.c. + * common/argparse.h: Remove types.h - not required. + * common/argparse.c: Change to allow standalone use. + +2015-03-16 Werner Koch + + gpg: Create all MPIs with RFC-4880 correct length headers. + * g10/build-packet.c (gpg_mpi_write): Strip leading zeroes. + + gpg: Allow printing of MPI values in --list-mode. + * g10/parse-packet.c (set_packet_list_mode): Set mpi_print_mode. + * g10/misc.c (mpi_print): Do not print an extra leading zero. + + gpg: Fix broken write of opaque MPI length header. + * g10/build-packet.c (gpg_mpi_write): Use a char array for the length. + +2015-03-15 Werner Koch + + gpg: Fix possible dead code elimination. + * g10/encrypt.c: Change condition for detecting a real file. + + g13: Fix pointer wrap check. + * g13/utils.c (find_tuple, next_tuple): Cast pointer to size_t before + doing an overflow check. + + agent: Remove useless conditions in command.c. + * agent/command.c (cmd_setkeydesc): Remove NULL check. + (cmd_get_passphrase): Ditto. + (cmd_clear_passphrase): Ditto. + (cmd_get_confirmation): Ditto. + (cmd_getval): Ditto. + (cmd_putval): Ditto. + + agent: Fix length test in sshcontrol parser. + * agent/command-ssh.c (ssh_search_control_file): Check S before + upcasing it. + + agent: Remove useless conditions. + * agent/genkey.c (agent_ask_new_passphrase): Remove useless condition. + * agent/command-ssh.c (ssh_identity_register): Ditto. + + gpg: Remove useless condition. + * g10/keylist.c (list_keyblock_colon): Remove useless condition (PK). + (list_keyblock_print): Likewise. + + scd: Fix possible NULL deref in apdu.c. + * scd/apdu.c (control_pcsc_direct): Take care of BUFLEN being NULL. + (control_pcsc_wrapped): Ditto. + + common: Make openpgp_oid_to_str more robust. + * common/openpgp-oid.c (openpgp_oid_to_str): Take care of + gcry_mpi_get_opaque returning NULL. Remove useless condition !BUF. + +2015-03-11 Werner Koch + + agent: Improve error reporting from Pinentry. + * agent/call-pinentry.c (unlock_pinentry): Add error logging. Map + error source of uncommon errors to Pinentry. + +2015-03-10 Werner Koch + + gpg: Change --print-pka-records into an option. + * g10/gpg.c (aPrintPKARecords): Rename to oPrintPKARecords and do not + use it as a command. + * g10/keylist.c (list_keyblock): List PKA rceords also for secret + keys. + + gpg: Add --list-gcrypt-config and "curve" item for --list-config. + * common/openpgp-oid.c (curve_supported_p): New. + (openpgp_enum_curves): New. + * common/t-openpgp-oid.c (test_openpgp_enum_curves): New. + (main): Add option --verbose. + * g10/gpg.c (opts): Add --list-gcrypt-config. + (list_config): Add items "curve" and "curveoid". Remove unused code. + +2015-03-09 NIIBE Yutaka + + scd: fix for 64-bit arch. + * agent/pksign.c (agent_pksign_do): Use int. + * scd/app-openpgp.c (get_public_key): Likewise. + +2015-03-04 Daniel Kahn Gillmor + + gpg: avoid chatter about trustdb when --quiet. + * g10/trustdb.c (tdb_check_trustdb_stale): avoid log_info() when + opt.quiet + +2015-02-26 Werner Koch + + gpg: Lowercase mailbox for PKA lookups. + * common/stringhelp.c (ascii_strlwr): New. + * common/mbox-util.c (mailbox_from_userid): Downcase result. + + gpg: Fix memory leak due to PKA lookup. + * g10/keyserver.c (keyserver_import_pka): Move the xfree. + +2015-02-25 Werner Koch + + gpg: Switch to a hash and CERT record based PKA system. + * common/dns-cert.c (get_dns_cert): Make r_key optional. + * common/pka.c: Rewrite for the new hash based lookup. + * common/t-pka.c: New. + * configure.ac: Remove option --disable-dns-pka. + (USE_DNS_PKA): Remove ac_define. + * g10/getkey.c (parse_auto_key_locate): Always include PKA. + + common: Allow requesting a specific certtype with get_dns_cert() + * common/dns-cert.c (get_dns_cert): Add arg want_certtype. Change all + callers. + (CERTTYPE_): Move constants to ... + * common/dns-cert.h: here as DNS_CERTTYPE_. + + Move new mailbox.c source file to common/. + * g10/mailbox.c: Move to ... + * common/mbox-util.c: new file. + * common/mbox-util.h: New. Include where needed. + * g10/t-mailbox.c: Move to ... + * common/t-mbox-util.c: new file. + +2015-02-24 Werner Koch + + gpg: Add command --print-pka-records. + * g10/gpg.c (main): Add command --print-pka-records. + * g10/options.h (struct opt): Add field "print_pka_records". + * g10/keylist.c (list_keyblock_pka): New. + (list_keyblock): Call it if new option is set. + (print_fingerprint): Add mode 10. + + gpg: Add function to extract the mailbox. + * g10/misc.c (has_invalid_email_chars, is_valid_mailbox) + (is_valid_user_id): Move to ... + * g10/mailbox.c: new file. + (string_has_ctrl_or_space, has_dotdot_after_at): New. + (has_invalid_email_chars): New. + + * g10/t-mailbox.c: New. + * g10/Makefile.am (module_tests): Add t-mailbox. + (t_mailbox_SOURCES, t_mailbox_LDADD): New. + +2015-02-23 Werner Koch + + gpg: Add option to print fingerprints in ICAO spelling. + * g10/gpg.c: Add option --with-icao-spelling. + * g10/options.h (struct opt): Add with_icao_spelling. + * g10/keylist.c (print_icao_hexdigit): New. + (print_fingerprint): Print ICAO spelling. + + gpg: Skip legacy keys while searching keyrings. + * g10/getkey.c (search_modes_are_fingerprint): New. + (lookup): Skip over legacy keys. + + common: Fix regression due to commit 2183683b. + * common/dns-cert.c (get_dns_cert): Remove cruft. + +2015-02-19 Werner Koch + + gpg: Replace remaining uses of stdio by estream. + * g10/sign.c (sign_file): Use log_printf instead of stderr. + * g10/tdbdump.c (export_ownertrust): Use estream fucntions. + (import_ownertrust): Ditto. + * g10/tdbio.c (tdbio_dump_record): Ditto. Change arg to estream_t. + + gpg: Fix segv due to NULL value stored as opaque MPI. + * g10/build-packet.c (gpg_mpi_write): Check for NULL return from + gcry_mpi_get_opaque. + (gpg_mpi_write_nohdr, do_key): Ditto. + * g10/keyid.c (hash_public_key): Ditto. + +2015-02-12 Werner Koch + + scd: Fix regression in 2.1.2 (due to commit 2183683) + * scd/apdu.c (pcsc_vendor_specific_init): Replace use of + bufNN_to_uint by direct code. + +2015-02-12 Andre Heinecke + + dirmngr: Initialize cache from sysconfig dir. + * dirmngr/certcache.c (cert_cache_init): Load certificates + from sysconfig dir instead of the homeidr. + * dirmngr/dirmngr.c (main): Removed parsing of obsolete + homedir_data option. + * dirmngr/dirmngr.h (opt): Removed homedir_data. + * doc/dirmngr.texi: Update and clarify certs directory doc. + +2015-02-11 Werner Koch + + Release 2.1.2. + + dirmngr: Avoid warning about unused function. + * dirmngr/dirmngr.c (my_gnutls_log): Build only if gnutls is used. + + build: Update standard build-aux files. + + Use inline functions to convert buffer data to scalars. + * common/host2net.h (buf16_to_ulong, buf16_to_uint): New. + (buf16_to_ushort, buf16_to_u16): New. + (buf32_to_size_t, buf32_to_ulong, buf32_to_uint, buf32_to_u32): New. + +2015-02-09 Werner Koch + + gpg: Prevent an invalid memory read using a garbled keyring. + * g10/keyring.c (keyring_get_keyblock): Whitelist allowed packet + types. + * g10/keydb.c (parse_keyblock_image): Ditto. + + gpg: Fix a NULL-deref in export due to invalid packet lengths. + * g10/build-packet.c (write_fake_data): Take care of a NULL stored as + opaque MPI. + + gpg: Fix a NULL-deref due to empty ring trust packets. + * g10/parse-packet.c (parse_trust): Always allocate a packet. + +2015-02-04 Werner Koch + + gpg-agent: Use "pinentry-basic" as fallback. + * common/homedir.c (get_default_pinentry_name): New. + (gnupg_module_name): Use that for the default pinentry. + (gnupg_module_name_flush_some): New. + * agent/gpg-agent.c (agent_sighup_action): Flush some module names. + * agent/call-pinentry.c (start_pinentry): Do not modify + opt.pinentry_program. + + w32: Add manifest to gpg. + * g10/gpg.w32-manifest.in: New. + * g10/gpg-w32info.rc: Add manifest. + * g10/Makefile.am (EXTRA_DIST): Add manifest. + (gpg-w32info.o): Depend on manifest. + * configure.ac (BUILD_VERSION): New. + (AC_CONFIG_FILES): Add manifest. + +2015-02-03 Werner Koch + + Update copyright years. + * common/w32info-rc.h.in (W32INFO_COMPANYNAME): Change to "The GnuPG + Project". + +2015-02-02 Werner Koch + + w32: Change default Windows install dir and add bin to PATH. + * build-aux/speedo.mk (WITH_GUI): New macro. The Windows installer is + now build by default without any GUI stuff. + * build-aux/speedo/w32/inst.nsi: Change standard installation + directory. + (AddToPath, un.RemoveFromPath): New. + (gnupginst): Add bin directory to the PATH. + +2015-02-01 Werner Koch + + w32: Allow for Unicocde installation directory. + * common/homedir.c (w32_rootdir): Use Unicode fucntion not only for + WinCE. + +2015-01-30 Joshua Rogers + + kbx: Fix resource leak. + * kbx/keybox-update.c (blob_filecopy): Fix resource leak. On error + return, 'fp' and 'newfp' was never closed. + +2015-01-29 Werner Koch + + agent: Fix use of imported but unprotected openpgp keys. + * agent/agent.h (PRIVATE_KEY_OPENPGP_NONE): New. + * agent/command.c (do_one_keyinfo): Implement it. + * agent/findkey.c (agent_key_from_file): Ditto. + (agent_key_info_from_file): Ditto. + (agent_delete_key): Ditto. + * agent/protect.c (agent_private_key_type): Add detection for openpgp + "none" method. + +2015-01-29 NIIBE Yutaka + + po: Update Japanese Translation. + +2015-01-28 Werner Koch + + gpg: Limit the size of key packets to a sensible value. + * g10/parse-packet.c (MAX_KEY_PACKET_LENGTH): New. + (MAX_UID_PACKET_LENGTH): New. + (MAX_COMMENT_PACKET_LENGTH): New. + (MAX_ATTR_PACKET_LENGTH): New. + (parse_key): Limit the size of a key packet to 256k. + (parse_user_id): Use macro for the packet size limit. + (parse_attribute): Ditto. + (parse_comment): Ditto. + + gpg: Fix buffering problem in --list-config. + * g10/gpg.c (list_config): Replace print_sanitized_string2 by + es_write_sanitized. + + * common/stringhelp.c (print_sanitized_buffer2): Remove. + (print_sanitized_buffer, print_sanitized_utf8_buffer): Remove. + (print_sanitized_utf8_buffer, print_sanitized_utf8_string): Remove. + (print_sanitized_string): Remove. + + * sm/certdump.c (print_dn_part, print_dn_parts): Remove arg FP. + (pretty_print_sexp, gpgsm_print_name2, gpgsm_print_name): Remove. + + Add a hook to be called right after main. + * common/init.c (early_system_init): New stub function. + + gpg: Allow predefined names as answer to the keygen.algo prompt. + * g10/keygen.c (ask_algo): Add list of strings. + + agent: Add some extra robustness to extract_private_key. + * agent/cvt-openpgp.c (extract_private_key): Add arg "arraysize". + Make sure that R_FLAGS and R_CURVE are set to NULL. + +2015-01-28 NIIBE Yutaka + + scd: Fix varargs call for 64-bit arch on ECC keys. + * scd/app-openpgp.c (store_fpr): Remove CARD_VERSION from the + arguments. + (rsa_writekey): Follow the change. + (do_genkey): Likewise. + (ecc_writekey): Likewise. Cast to size_t. + +2015-01-27 Werner Koch + + gpg: Fix segv introduced to commit 4d7c9b0. + * g10/keygen.c (get_parameter_passphrase): Take care of R == NULL. + +2015-01-27 NIIBE Yutaka + + agent: Fix agent_public_key_from_file for ECC. + * agent/cvt-openpgp.c (extract_private_key): New. + (convert_to_openpgp): Use extract_private_key. + * agent/findkey.c (agent_public_key_from_file): Use + extract_private_key. + +2015-01-26 Werner Koch + + sm: Simplify fix ed8383c6. + * sm/minip12.c (p12_build): Release PWBUF only at the end. + +2015-01-25 Joshua Rogers + + ccid: Remove incorrect expression leading to errors. + * scd/ccid-driver.c (send_escape_cmd): Fix setting of 'rc'. + +2015-01-23 Werner Koch + + gpgconf: Fix validity check for UINT32 values. + * tools/gpgconf-comp.c (option_check_validity): Enable check for + UINT32. + +2015-01-22 Werner Koch + + gpg: Improve skipping of PGP-2 keys. + * g10/keydb.c (keydb_search_first, keydb_search_next): Skip legacy + keys. + * g10/keyring.c (keyring_get_keyblock): Handle GPG_ERR_LEGACY_KEY. + (prepare_search): Ditto. + (keyring_rebuild_cache): Skip legacy keys. + * g10/keyserver.c (keyidlist): Ditto. + * g10/trustdb.c (validate_key_list): Ditto. + + gpg: Add dedicated error code for PGP-2 keys. + * g10/parse-packet.c (parse_key): Return GPG_ERR_LEGACY_KEY for PGP2 + keys. + * g10/import.c (read_block): Simplify by checking GPG_ERR_LEGACY_KEY. + * g10/getkey.c (lookup): Silence error message for PGP-2 keys. + + * common/util.h (GPG_ERR_LEGACY_KEY): Add replacement for older + libgpg-error. + + gpg: Replace remaining old error code macros by GPG_ERR_. + * g10/gpg.h (g10_errstr): Remove macro and change all occurrences by + gpg_strerror. + (G10ERR_): Remove all macros and change all occurrences by their + GPG_ERR_ counterparts. + + gpg: Remove an unused variable. + * g10/getkey.c (getkey_ctx_s): Remove last_rc. + +2015-01-21 Werner Koch + + dirmngr: Fix TLS build problems. + * dirmngr/Makefile.am (AM_CFLAGS): Add flags for TLS libs. + + gpg: Support --passphrase with --quick-gen-key. + * g10/keygen.c: Include shareddefs.h. + (quick_generate_keypair): Support static passphrase. + (get_parameter_passphrase): New. + (do_generate_keypair): Use it. + + gpg: Re-enable the "Passphrase" parameter for batch key generation. + * agent/command.c (cmd_genkey): Add option --inq-passwd. + * agent/genkey.c (agent_genkey): Add new arg override_passphrase. + * g10/call-agent.c (inq_genkey_parms): Handle NEWPASSWD keyword. + (agent_genkey): Add arg optional arg "passphrase". + * g10/keygen.c (common_gen, gen_elg, gen_dsa, gen_ecc) + (gen_rsa, do_create): Add arg "passphrase" and pass it through. + (do_generate_keypair): Make use of pPASSPHRASE. + (release_parameter_list): Wipe out a passphrase parameter. + +2015-01-19 Werner Koch + + kbx: Minor cleanup for the previous fix. + * kbx/keybox-search.c (blob_get_keyid): Rename to + blob_get_first_keyid. Check number of keys and remove blob type check. + +2015-01-19 Damien Goutte-Gattat + + kbx: Call skipfnc callback to filter out keys. + * kbx/keybox-search.c (blob_get_keyid): New. + (keybox-search): Call skipfnc callback function. + +2015-01-13 Andreas Schwier + + scd: Allow for certificates > 1024 with PC/SC. + * scd/pcsc-wrapper.c (handle_transmit): Enlarge buffer to 4096 too + allow for larger certificates. + +2015-01-08 NIIBE Yutaka + + dirmngr: Fix error code path of map_host. + * dirmngr/ks-engine-hkp.c (map_host): Fix error return. + +2015-01-08 Joshua Rogers + + scd: fix get_public_key for OpenPGPcard v1.0. + * scd/app-openpgp.c (get_public_key): correctly close 'fp' upon use. + +2015-01-07 NIIBE Yutaka + + dirmngr: fix LDAP query PATTERNS limit check. + * dirmngr/ldap.c (start_cert_fetch_ldap): fix ARGC limitation. + + scd: fix merge failure. + * scd/apdu.c (pcsc_pinpad_verify): Remove wrong lines inserted by + merge. + +2015-01-05 Werner Koch + + sm,g13: Init local vars to avoid compiler warnings. + * sm/misc.c (transform_sigval): Init RSA_S_LEN. + * g13/mount.c (read_keyblob): Init HEADERLEN. + + gpg: Remove unused args from a function. + * g10/keyserver.c (parse_keyserver_uri): Remove args configname and + configlineno. Change all callers. + + gpg: Clear a possible rest of the KDF secret buffer. + * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Fix order of args. + + build: Require automake 1.14. + * configure.ac (AM_INIT_AUTOMAKE): Add serial-tests. + +2015-01-04 Werner Koch + + agent: Make --allow-loopback-pinentry gpgconf changeable. + +2014-12-22 Joshua Rogers + + tools: Free variable before return. + * tools/gpgconf-comp.c: Free 'dest_filename' before it is returned + upon error. + +2014-12-22 Daniel Kahn Gillmor + + sm: Avoid double-free on iconv failure. + * sm/minip12.c: (p12_build) if jnlib_iconv_open fails, avoid + double-free of pwbuf. + + scd: Avoid double-free on error condition in scd. + * scd/command.c (cmd_readkey): avoid double-free of cert + + avoid future chance of using uninitialized memory. + * common/iobuf.c: (iobuf_open): initialize len + + avoid double-close in unusual dotlock situations. + * common/dotlock.c: (dotlock_create_unix) avoid double-close() + in unusual situations. + + gpgkey2ssh: clean up varargs. + * tools/gpgkey2ssh.c (key_to_blob) : ensure that va_end is called. + +2014-12-22 Werner Koch + + doc: Fix memory leak in yat2m. + * doc/yat2m.c (write_th): Free NAME. + + dirmngr: Fix memory leak. + * dirmngr/server.c (cmd_ks_search, cmd_ks_get): Fix memory leak. + + * dirmngr/ks-engine-hkp.c (ks_hkp_mark_host): Remove double check. + + dirmngr: Remove un-needed check. + * dirmngr/crlfetch.c (crl_fetch): Check that URL is not NULL. + + dirmngr,gpgsm: Return NULL on fail. + * dirmngr/ldapserver.c (ldapserver_parse_one): Set SERVER to NULL. + * sm/gpgsm.c (parse_keyserver_line): Ditto. + +2014-12-22 NIIBE Yutaka + + scd: ECDH Support. + * agent/divert-scd.c (divert_pkdecrypt): Support ECDH. + * scd/app-openpgp.c (get_algo_byte, store_fpr): Support ECDH. + (send_key_attr): Support ECDH. Fix EdDSA algorithm value. + (retrieve_key_material): Initialize fields. + (get_public_key, ecc_writekey, do_writekey): Support ECDH. + (ecdh_writekey): Remove. + (do_decipher): Support ECDH. + (parse_algorithm_attribute): Support ECDH. Fix EdDSA. + +2014-12-19 Werner Koch + + agent: Make sure --max-cache-ttl is >= --default-cache-ttl. + * agent/gpg-agent.c (finalize_rereadable_options): New. + (main, reread_configuration): Call it. + + agent: Keep the session environment for restricted connections. + * agent/command-ssh.c (setup_ssh_env): Move code to ... + * agent/gpg-agent.c (agent_copy_startup_env): .. new function. Change + calllers. + * agent/command.c (start_command_handler): Call that fucntion for + restricted connections. + + agent: Fix string prepended to remotely initiated prompts. + * agent/command.c (cmd_setkeydesc): Use %0A and not \n. Make + translatable. + +2014-12-18 Werner Koch + + build: Remove option to build without agent. + * configure.ac (build-agent): Set to yes. + +2014-12-17 Werner Koch + + gpgconf: Exit with failure if --launch fails. + * tools/gpgconf-comp.c (gc_component_launch): Return an error code. + * tools/gpgconf.c (main): Exit if launch failed. + +2014-12-16 Werner Koch + + Release 2.1.1. + + po: Update the German translation. + +2014-12-16 Petr Pisar + + po: Update Czech translation. + +2014-12-16 Werner Koch + + gpg: Show private DO information in the card status. + * g10/call-agent.c (agent_release_card_info): Free private_do. + (learn_status_cb): Parse PRIVATE-DO-n stati. + +2014-12-16 Ineiev + + po: Update Russian translation. + +2014-12-16 Jedi + + po: Update zh_TW translation. + +2014-12-15 Werner Koch + + gpg: Add sub-command "factory-reset" to --card-edit. + * common/util.h (GPG_ERR_OBJ_TERM_STATE): New. + * scd/iso7816.c (map_sw): Add this error code. + * scd/app-openpgp.c (do_getattr): Return the life cycle indicator. + * scd/app.c (select_application): Allow a return value of + GPG_ERR_OBJ_TERM_STATE. + * scd/scdaemon.c (set_debug): Print the DBG_READER value. + * g10/call-agent.c (start_agent): Print a status line for the + termination state. + (agent_scd_learn): Make arg "info" optional. + (agent_scd_apdu): New. + * g10/card-util.c (send_apdu): New. + (factory_reset): New. + (card_edit): Add command factory-reset. + + gpg: Fix regression in notation data regression. + * g10/misc.c (pct_expando): Reorder conditions for clarity. + * g10/sign.c (write_signature_packets): Fix notation data creation. + + gpg: Avoid extra LF in notaion data listing. + * g10/keylist.c (show_notation): Use log_printf. + +2014-12-12 Werner Koch + + scd: Fix possibly inhibited checkpin of the admin pin. + * scd/app-openpgp.c (do_check_pin): Do not check a byte of a released + buffer. + + gpg: Let --card--status create a shadow key (card key stub). + * agent/command.c (cmd_learn): Add option --sendinfo. + * agent/learncard.c (agent_handle_learn): Add arg "send" andsend + certifciate only if that is set. + * g10/call-agent.c (agent_scd_learn): Use --sendinfo. Make INFO + optional. + (agent_learn): Remove. + * g10/keygen.c (gen_card_key): Replace agent_learn by agent_scd_learn. + + gpg: Fix possible read of unallocated memory. + * g10/parse-packet.c (can_handle_critical): Check content length + before calling can_handle_critical_notation. + +2014-12-11 Werner Koch + + build: Replace deprecated autconf macro. + * m4/intl.m4: s/AM_PROG_MKDIR_P/AC_PROG_MKDIR_P/ + * m4/po.m4: Ditto. + +2014-12-08 Werner Koch + + dirmngr: Improve dead host detection. + * dirmngr/ks-engine-hkp.c (handle_send_request_error): Mark host dead + also for 2 other error messages. + + http: Improve diagnostic messages. + * common/http.c (send_request): Print TLS alert info + (connect_server): Detect bogus DNS entry. + + gpg: Obsolete some keyserver helper options. + * g10/options.h (opt): Remove keyserver_options.other. + * g10/gpg.c (main): Obsolete option --honor-http-proxt. + * g10/keyserver.c (add_canonical_option): Replace by ... + (warn_kshelper_option): New. + (parse_keyserver_uri): Obsolete "x-broken-http". + + dirmngr: Return a proper error for all dead hosts. + * dirmngr/ks-engine-hkp.c (map_host): Change to return an gpg_error_t. + Return an error code for all dead hosts. + (make_host_part): Change to return an gpg_error_t. Change all + callers. + + gpg: Write a status line for a failed --send-keys. + * g10/keyserver.c (keyserver_put): Write an status error. + +2014-12-08 NIIBE Yutaka + + scd: Fix for EdDSA. + * scd/app-openpgp.c (get_algo_byte): It catches 22. + (store_fpr): It's MPI usually, but it's opaque bytes for EdDSA. + +2014-12-05 Andre Heinecke + + Document no-allow-mark-trusted option. + doc: Document no-allow-mark-trusted for gpg-agent + + * doc/gpg-agent.texi: Change allow-mark-trusted doc to + no-allow-mark-trusted. + + -- + Since rev. 78a56b14 allow-mark-trusted is the default option + and was replaced by no-allow-mark-trusted to disable the + interactive prompt. + +2014-12-05 NIIBE Yutaka + + scd: Fix for NIST P-256. + * g10/card-util.c (card_store_subkey): Error check. + * scd/app-opengpg.c (ecc_writekey): Support NIST P-256. + (do_writekey): Error check. + +2014-12-04 Werner Koch + + gpg: Allow import of large keys. + * g10/import.c (import): Skip too large keys. + * kbx/keybox-file.c (IMAGELEN_LIMIT): Change limit from 2MB to 5MB. + +2014-12-03 Werner Koch + + gpg: Remove option aliases --[no-]throw-keyid and --notation-data. + * g10/gpg.c (opts): Remove them. + * g10/options.h (opt): s/throw_keyid/throw_keyids/ and change users. + +2014-12-02 Werner Koch + + agent: Replace some sprintf. + * agent/call-scd.c (agent_card_pksign): Replace sprintf by bin2hex. + * agent/command-ssh.c (ssh_identity_register): Ditto. + * agent/pkdecrypt.c (agent_pkdecrypt): Replace sprintf by + put_membuf_printf. + +2014-12-01 Werner Koch + + tools: Improve watchgnupg portability. + * configure.ac (AC_CHECK_HEADERS): Check for sys.select.h + * tools/watchgnupg.c: Include it. + + gpg: Fix export bug using exact search with only one key in the keybox. + * g10/export.c (do_export_stream): Disable caching. + * g10/keyserver.c (keyidlist): Ditto. + + scd: Implement socket redirection. + * scd/scdaemon.c (ENAMETOOLONG): New. + (redir_socket_name): New. + (cleanup): Take care of a redirected socket. + (main): Pass redir_socket_name to create_server_socket. + (create_socket_name): Remove superfluous length check. + (create_server_socket): Add arg r_redir_name and implement + redirection. Replace assert for older Assuan by an error message. + + dirmngr: Implement socket redirection. + * dirmngr/dirmngr.c (ENAMETOOLONG): new. + (redir_socket_name): New. + (main): Add Assuan socket redirection. + (cleanup): Adjust cleanup for redirection. + +2014-11-28 Werner Koch + + agent: Implement socket redirection. + * agent/gpg-agent.c (ENAMETOOLONG): New. + (redir_socket_name, redir_socket_name_extra) + (redir_socket_name_ssh): New. + (remove_socket): Take care of the redir names. + (main): Pass the redir names to create_server_socket. + (create_socket_name): Remove length check - that is anyway done later. + (create_server_socket): Add arg r_redir_name and implement redirection + if Libassuan is at least 2.14. + + gpg: Change another BUG() call to a regular error message. + * g10/mainproc.c (proc_tree): Replace BUG by a proper error messages. + + Add option --no-autostart. + * g10/gpg.c: Add option --no-autostart. + * sm/gpgsm.c: Ditto. + * g10/options.h (opt): Add field autostart. + * sm/gpgsm.h (opt): Ditto. + * g10/call-agent.c (start_agent): Print note if agent was not + autostarted. + * sm/call-agent.c (start_agent): Ditto. + * g10/call-dirmngr.c (create_context): Likewise. + * sm/call-dirmngr.c (start_dirmngr_ext): Ditto. + +2014-11-27 Мирослав Николић + + gpg-agent: Add restricted connection feature. + * agent/agent.h (opt): Add field extra_socket. + (server_control_s): Add field restricted. + * agent/command.c: Check restricted flag on many commands. + * agent/gpg-agent.c (oExtraSocket): New. + (opts): Add option --extra-socket. + (socket_name_extra): New. + (cleanup): Cleanup that socket name. + (main): Implement oExtraSocket. + (create_socket_name): Add arg homedir and change all callers. + (create_server_socket): Rename arg is_ssh to primary and change + callers. + (start_connection_thread): Take ctrl as arg. + (start_connection_thread_std): New. + (start_connection_thread_extra): New. + (handle_connections): Add arg listen_fd_extra and replace the + connection starting code by parameterized loop. + * common/asshelp.c (start_new_gpg_agent): Detect the use of the + restricted mode and don't fail on sending the pinentry environment. + + * common/util.h (GPG_ERR_FORBIDDEN): New. + + agent: Make auditing of the option list easier. + * agent/gpg-agent.c (opts): Use ARGPARSE_ macros. + +2014-11-26 Kristian Fiskerstrand + + dirmngr: Only report hkps scheme when available. + * dirmngr/ks-engine-hkp.c (ks_hkp_help): Make use of TLS macros. + +2014-11-26 Werner Koch + + gpg: Change a bug() call to a regular error message. + * g10/decrypt-data.c (decrypt_data): Return an error code instead of + calling BUG(). + +2014-11-25 Werner Koch + + Fix buffer overflow in openpgp_oid_to_str. + * common/openpgp-oid.c (openpgp_oid_to_str): Fix unsigned underflow. + + * common/t-openpgp-oid.c (BADOID): New. + (test_openpgp_oid_to_str): Add test cases. + +2014-11-24 Werner Koch + + gpg: Fix use of uninit.value in listing sig subpkts. + * g10/parse-packet.c (dump_sig_subpkt): Print regex subpacket + sanitized. + + gpg: Fix off-by-one read in the attribute subpacket parser. + * g10/parse-packet.c (parse_attribute_subpkts): Check that the + attribute packet is large enough for the subpacket type. + + gpg: Fix batch generation of ECC keys. + * g10/keygen.c (get_parameter_algo): Map ECC algorithm strings + directly. + +2014-11-24 Daniel Kahn Gillmor + + Distinguish between ARGPARSE_AMBIGUOUS_{OPTION,COMMAND} + * common/argparse.c (initialize): Use correct value. + + gpg: Refer to --throw-keyids instead of --throw-keyid. + * g10/encrypt.c: adjust error message + +2014-11-21 Werner Koch + + gpg: Track number of skipped v3 keys on import. + * g10/import.c (stats_s): Add field v3keys. + (import): Update this field. + (import_print_stats): Print v3 key count. + (read_block): Skip v3 keys and return a count for them. + + gpg: Fix regression in parse_key. + * g10/parse-packet.c (parse): Better return just the gpg_err_code. + (parse_key): Return the error code. + + speedo: Add simple logos to the installer. + * build-aux/speedo/w32/README.txt: Include GnuPG Readme. + * build-aux/speedo/w32/gnupg-logo-150x57.bmp: New. + * build-aux/speedo/w32/gnupg-logo-164x314.bmp: New. + * build-aux/speedo/w32/inst.nsi: Add logos. + * build-aux/speedo.mk ($(bdir)/NEWS.tmp): Extract news items. + +2014-11-20 Werner Koch + + gpg: Fix hash detection for ECDSA. + * g10/sign.c (sign_file): Use DSA or ECDSA and not DSA|EdDSA. + + Fix linker problem on OS X. + * common/init.c (default_errsource): Move to the .data segmemt. + +2014-11-19 Werner Koch + + gpg-connect-agent: Add convenience option --uiserver. + + Add "gpgconf --kill dirmngr" and avoid useless launch before a kill. + * common/asshelp.c (start_new_gpg_agent): Add arg autostart. Change + all callers to use 1 for it. + (start_new_dirmngr): Ditto. + * tools/gpg-connect-agent.c: Add option --no-autostart. + (main): Default autostart to 1. + (start_agent): Implement no-autostart. + * tools/gpgconf-comp.c (gpg_agent_runtime_change): Use --no-autostart. + (scdaemon_runtime_change): Ditto. + (dirmngr_runtime_change): New. + + po: Copied missing translations from the 2.0 branch. + * po/LINGUAS: Add new translations. + +2014-11-17 Werner Koch + + gpg: Fix a NULL-deref for invalid input data. + * g10/mainproc.c (proc_encrypted): Take care of canceled passpharse + entry. + +2014-11-13 Werner Koch + + gpg: Make the use of "--verify FILE" for detached sigs harder. + * g10/openfile.c (open_sigfile): Factor some code out to ... + (get_matching_datafile): new function. + * g10/plaintext.c (hash_datafiles): Do not try to find matching file + in batch mode. + * g10/mainproc.c (check_sig_and_print): Print a warning if a possibly + matching data file is not used by a standard signatures. + + gpg: Fix a missing LF in debug output. + * g10/kbnode.c (dump_kbnode): Print a LF. + + gpg: Remove PGP-2 related cruft. + * g10/armor.c (parse_hash_header,carmor_filter): Ignore MD5 in hash + header. + (fake_packet): Remove pgp-2 workaround for white space stripping. + * g10/filter.h (armor_filter_context_t): Remove field pgp2mode. + * g10/options.h (opt): Remove field pgp2_workarounds. + * g10/gpg.c (main): Do not set this field. + * g10/gpgv.c (main): Ditto. + * g10/mainproc.c (proc_encrypted): Use SHA-1 as fallback s2k hash + algo. Using MD5 here is useless. + (proc_plaintext): Remove PGP-2 related woraround + (proc_tree): Remove another workaround but keep the one for PGP-5. + +2014-11-12 Werner Koch + + gpg: Improve perceived speed of secret key listings. + * g10/keylist.c (list_keyblock): Flush stdout for secret keys. + + gpg: Fix regression in --refresh-keys. + * g10/keyserver.c (keyserver_get): Factor all code out to ... + (keyserver_get_chunk): new. Extimate line length. + (keyserver_get): Split up requests into chunks. + + gpg: Add import options "keep-ownertrust". + * g10/options.h (IMPORT_KEEP_OWNERTTRUST): New. + * g10/import.c (parse_import_options): Add "keep-ownertrust". + (import_one): Act upon new option. + +2014-11-11 Werner Koch + + Remove use of gnulib (part 2) + * configure.ac (strpbrk): Add to AC_CHECK_FUNCS. + (gl_EARLY): Remove. + * common/stringhelp.c (strpbrk) [!HAVE_STRPBRK]: New. + * common/sysutils.c (gnupg_mkdtemp): New. Based on code from + glibc-2.6. + (gnupg_setenv): Rewrite. + (gnupg_unsetenv): Rewrite. + * g10/exec.c: Include sysutils.h and replace mkdtemp by gnupg_mkdtemp. + * g13/be-encfs.c: Ditto. + * g13/mount.c: Ditto. + * tools/symcryptrun.c (confucius_mktmpdir): Ditto. + + Remove use of gnulib (part 1) + * gl/: Remove entire tree. + * configure.ac: Remove gnulib tests and the gl/ Makefile. + (setenv): Add to AC_CHECK_FUNCS. + * autogen.rc (extra_aclocal_flags): Set to empty. + * Makefile.am (ACLOCAL_AMFLAGS): Remove -I gl/m4 + (SUBDIRS): Remove gl/. + * agent/Makefile.am (common_libs): Remove ../gl/gnulib.a + * common/Makefile.am (t_common_ldadd): Ditto. + * dirmngr/Makefile.am (dirmngr_LDADD): Ditto. + (dirmngr_ldap_LDADD, dirmngr_client_LDADD): Ditto. + * g10/Makefile.am (needed_libs): Ditto. + * g13/Makefile.am (g13_LDADD): Ditto. + * kbx/Makefile.am (kbxutil_LDADD): Ditto. + ($(PROGRAMS)): Ditto. + * scd/Makefile.am (scdaemon_LDADD): Ditto. + * sm/Makefile.am (common_libs): Ditto. + * tools/Makefile.am (common_libs, commonpth_libs): Ditto. + + * agent/gpg-agent.c: Remove "mkdtemp.h" + * g10/exec.c: Ditto. + * scd/scdaemon.c: Ditto. + * tools/symcryptrun.c: Ditto. + * common/sysutils.c: Remove "setenv.h" + + * common/t-timestuff.c: Use putenv if setenv is not available. + +2014-11-07 Werner Koch + + gpg: Remove warning message for non-implemented search modes. + * kbx/keybox-search.c (keybox_search): Silently ignore. + * doc/specify-user-id.texi: Docuement '@", '+', and '.' search + prefixes. + + w32: Fix http access module. + * common/http.c (write_server) [W32]: Rework to use send() instead of + write even when build with npth. + (cookie_read) [W32]: Rework to use recv() instead of read even when + build with npth. + + build: Add method to use a custom swdb.lst and use adns with Windows. + * build-aux/getswdb.sh: Add option --skip-verify. + * build-aux/speedo.mk: Add config var CUSTOM_SWDB. Tage adns version + from swdb and build for Windows with adns. + + build: Improve test for ADNS. + * configure.ac : Use adns_free as probe function for libadns. + (HAVE_ADNS_FREE): Remove bogus tests to set this and remove the macro. + (ADNSLIBS): Do not ac_subst - it is only used within configure. + +2014-11-05 Werner Koch + + speedo: Append the date to the Windows installer. + * build-aux/speedo.mk (BUILD_DATESTR): New. + (dist-source, installer): Use it. + + Release 2.1.0. + + Avoid sign extension when shifting the MSB. + * sm/fingerprint.c (gpgsm_get_short_fingerprint): Cast MSB before + shifting. + * g10/build-packet.c (delete_sig_subpkt): Ditto. + +2014-11-04 Werner Koch + + Remove all expired common CA certificates. + * doc/com-certs.pem: Remove certifciates. + +2014-11-02 Werner Koch + + gpg: Avoid extra pinentries for each subkey in --export-secret-keys. + * agent/command.c (cmd_export_key): Actually implement the cache_nonce + feature. + * g10/export.c (do_export_stream): Make use of a cache_nonce. + + gpg: Fix endless loop in keylisting with fingerprint. + * g10/getkey.c (getkey_next): Disable cache. + + gpg: Minor cleanup for key listing related code. + * g10/getkey.c (get_pubkey_next): Divert to getkey_next. + (get_pubkey_end): Move code to getkey_end. + * g10/keydb.c (keydb_search_reset): Add a debug statement. + (dump_search_desc): Add arg HD and print the handle. + + gpg: Do not show an useless passphrase prompt in batch mode. + * g10/keygen.c: Remove unused PASSPHRASE related code. + (proc_parameter_file): Remove useless asking for a passphrase in batch + mode. + +2014-10-31 Werner Koch + + gpg: Remove superfluous check for Libgcrypt >= 1.4.0. + * g10/gpg.c (main): Remove check. + + kbx: Let keydb_search skip unwanted blobs. + * kbx/keybox.h (keybox_blobtype_t): New. + * kbx/keybox-defs.h (BLOBTYPE_*): Replace by KEYBOX_BLOBTYPE_*. + * kbx/keybox-search.c (keybox_search): Add arg want_blobtype and skip + non-matching blobs. + * sm/keydb.c (keydb_search): Pass KEYBOX_BLOBTYPE_X509 to keybox_search. + * g10/keydb.c (keydb_search): Pass KEYBOX_BLOBTYPE_PGP to keybox_search. + + gpg: Fix --rebuild-keydb-caches. + * g10/parse-packet.c (parse_key): Store even unsupported packet + versions. + * g10/keyring.c (keyring_rebuild_cache): Do not copy keys with + versions less than 4. + + gpg: Fix testing for secret key availability. + * g10/getkey.c (have_secret_key_with_kid): Do not change the search + mode. + + build: Avoid distributing backup files etc. + * Makefile.am (EXTRA_DIST): Do not include directories. + +2014-10-30 Werner Koch + + tests: Speed up the genkey1024.test by using not so strong random. + * agent/gpg-agent.c (oDebugQuickRandom): New. + (opts): New option --debug-quick-random. + (main): Use new option. + * common/asshelp.c (start_new_gpg_agent): Add hack to pass an + additional argument for the agent name. + * tests/openpgp/defs.inc: Pass --debug-quick-random to the gpg-agent + starting parameters. + * tests/openpgp/version.test: Ditto. + +2014-10-29 Werner Koch + + common: Check option arguments for a valid range. + * common/argparse.h (ARGPARSE_INVALID_ARG): New. + * common/argparse.c: Include limits h and errno.h. + (initialize): Add error strings for new error constant. + (set_opt_arg): Add range checking. + + Fix stdint.h problem for Apple. + * gl/stdint_.h [__APPLE__]: Include hack. + +2014-10-27 Werner Koch + + speedo: Fixes for native build. + * build-aux/speedo.mk (TARGETOS): Init with empty string. + (speedo_pkg_gnupg_configure): Use --enable-gpg2-is-gpg only for w32. + (INST_VERSION, INST_PROD_VERSION): Create only for w32. + +2014-10-24 Werner Koch + + agent: Support pinentries with integrated repeat passphrase feature. + * agent/agent.h (struct pin_entry_info_s): Add fields repeat_okay and + with_repeat. + * agent/call-pinentry.c (close_button_status_cb): Rewrite and check + for PIN_REPEAT. Change users to check only the relevant bit. + (agent_askpin): Support repeat logic of new Pinentries. + + * agent/command-ssh.c (ssh_identity_register): Use the new repeat + feature. + * agent/genkey.c (agent_ask_new_passphrase): Ditto. + +2014-10-19 Werner Koch + + gpg: Silence "packet with obsolete versoin" warnings. + * g10/parse-packet.c (parse_key): Print warning only in very verbose + mode. + + gpg: Make card key generation work again. + * g10/call-agent.c (agent_scd_learn): Rename from agent_learn. + (agent_learn): New. + * g10/keygen.c (gen_card_key): Call new agent-learn. + +2014-10-17 Werner Koch + + dirmngr: Allow building without LDAP support. + * configure.ac: Add option --disable-ldap. + (USE_LDAP): New ac_define and am_conditional. + * dirmngr/Makefile.am: Take care of USE_LDAP. + * dirmngr/dirmngr.c (!USE_LDAP): Make all ldap options dummy options + and do not call any ldap function. + * dirmngr/server.c (!USE_LDAP): Do not call any ldap function. + * dirmngr/crlfetch.c (!USE_LDAP): Ditto. + + w32: Set SYSROOT to help finding config scripts. + * autogen.sh : Set SYSROOT. + + gpg: Remove all support for v3 keys and always create v4-signatures. + * g10/build-packet.c (do_key): Remove support for building v3 keys. + * g10/parse-packet.c (read_protected_v3_mpi): Remove. + (parse_key): Remove support for v3-keys. Add dedicated warnings for + v3-key packets. + * g10/keyid.c (hash_public_key): Remove v3-key support. + (keyid_from_pk): Ditto. + (fingerprint_from_pk): Ditto. + + * g10/options.h (opt): Remove fields force_v3_sigs and force_v4_certs. + * g10/gpg.c (cmd_and_opt_values): Remove oForceV3Sigs, oNoForceV3Sigs, + oForceV4Certs, oNoForceV4Certs. + (opts): Turn --force-v3-sigs, --no-force-v3-sigs, --force-v4-certs, + --no-force-v4-certs int dummy options. + (main): Remove setting of the force_v3_sigs force_v4_certs flags. + * g10/revoke.c (gen_revoke, create_revocation): Always create v4 certs. + * g10/sign.c (hash_uid): Remove support for v3-signatures + (hash_sigversion_to_magic): Ditto. + (only_old_style): Remove this v3-key function. + (write_signature_packets): Remove support for creating v3-signatures. + (sign_file): Ditto. + (sign_symencrypt_file): Ditto. + (clearsign_file): Ditto. Remove code to emit no Hash armor line if + only v3-keys are used. + (make_keysig_packet): Remove arg SIGVERSION and force using + v4-signatures. Change all callers to not pass a value for this arg. + Remove all v3-key related code. + (update_keysig_packet): Remove v3-signature support. + * g10/keyedit.c (sign_uids): Always create v4-signatures. + + * g10/textfilter.c (copy_clearsig_text): Remove arg pgp2mode and + change caller. + +2014-10-13 Werner Koch + + gpg: Remove extra RSA import status line. + * g10/import.c (stats_s): Remove field "imported_rsa". + (import_print_stats): Do not print separate value for RSA. + (import_one): Remove the RSA counter. + + gpg: Fix informative printing of user ids. + * g10/getkey.c (keyid_list): Add field "fpr". + (cache_user_id): Store fpr and check for dups only by fpr. + (get_pubkey_byfpr): New. + (get_user_id_string): Make static and use xasprintf. + (get_long_user_id_string): Use xasprintf. + (get_user_id_byfpr): New. + (get_user_id_byfpr_native): New. + * g10/keyid.c (fingerprint_from_pk): Make arg RET_LEN optional. + * g10/import.c (import_one): Use get_user_id_byfpr_native. + + gpg: Allow importing keys with duplicated long key ids. + * g10/keydb.c (keydb_handle): Add field no_caching. + (keyblock_cache): Repalce field kid by fpr. + (keydb_disable_caching): New. + (keydb_search): Use the fingerprint as cache index. + + * g10/import.c (import_one): Use the fingerprint and not the kid to + lookup the key. Call keydb_disable_caching beofre re-searching for + update. + + * tests/openpgp/import.test: Add a test case. + + tests: Speed up conventional encryption tests for gpg. + * tests/openpgp/conventional-mdc.test: Add an s2k-count option. + * tests/openpgp/conventional.test: Ditto. + +2014-10-12 Werner Koch + + gpg: Minor change for better readability. + * g10/build-packet.c (write_version): Remove. + (do_pubkey_enc, do_onepass_sig): Write version directly. + +2014-10-10 Werner Koch + + doc: Fix a man page rendering problem. + * doc/gpg-agent.texi (Agent Configuration): Fix rendering of the + sshcontrol example. + +2014-10-10 Daniel Kahn Gillmor + + gpg: Add build and runtime support for larger RSA keys. + * configure.ac: Added --enable-large-secmem option. + * g10/options.h: Add opt.flags.large_rsa. + * g10/gpg.c: Contingent on configure option: adjust secmem size, + add gpg --enable-large-rsa, bound to opt.flags.large_rsa. + * g10/keygen.c: Adjust max RSA size based on opt.flags.large_rsa + * doc/gpg.texi: Document --enable-large-rsa. + +2014-10-09 Werner Koch + + gpg: Skip overlong keys and a print a warning. + * kbx/keybox-search.c (keybox_search): Add arg r_skipped and skip too + long blobs. + * sm/keydb.c (keydb_search): Call keybox_search with a dummy param. + * g10/keydb.c (struct keydb_handle): Add field skipped_long_blobs. + (keydb_search_reset): Reset that field. + (keydb_search): Update that field. + (keydb_get_skipped_counter): New. + * g10/keylist.c (list_all): Print count of skipped keys. + + gpg: Sync keylist output and warning messages. + * g10/keylist.c (list_all): Flush stdout before logging. + * g10/misc.c (print_pubkey_algo_note): Ditto. + (print_cipher_algo_note): Ditto. + (print_digest_algo_note): Ditto. + (print_md5_rejected_note): Ditto. + + kbx: Fix handling of overlong keys. + * kbx/keybox-file.c (IMAGELEN_LIMIT): Change limit from 10^6 to 2MiB. + (_keybox_read_blob2): Skip too long record records. + (_keybox_write_blob): Do not accept too long record. + * kbx/keybox-dump.c (file_stats_s): Add field skipped_long_blobs. + (_keybox_dump_file): Print new counter. + (_keybox_dump_file): Skip too long records. + ---- + + To test this feature you may set the limit back to 1MiB and use key + F7F0E70F307D56ED which is in my local copy close to 2MiB. Without + this patch it was possible to import the key but access to that key + and all keys stored after it was not possible. + + gpg: Take care to use pubring.kbx if it has ever been used. + * kbx/keybox-defs.h (struct keybox_handle): Add field for_openpgp. + * kbx/keybox-file.c (_keybox_write_header_blob): Set openpgp header + flag. + * kbx/keybox-blob.c (_keybox_update_header_blob): Add arg for_openpgp + and set header flag. + * kbx/keybox-init.c (keybox_new): Rename to do_keybox_new, make static + and add arg for_openpgp. + (keybox_new_openpgp, keybox_new_x509): New. Use them instead of the + former keybox_new. + * kbx/keybox-update.c (blob_filecopy): Add arg for_openpgp and set the + openpgp header flags. + + * g10/keydb.c (rt_from_file): New. Factored out and extended from + keydb_add_resource. + (keydb_add_resource): Switch to the kbx file if it has the openpgp + flag set. + + * kbx/keybox-dump.c (dump_header_blob): Print header flags. + +2014-10-09 Daniel Kahn Gillmor + + Avoid unnecessary library linkage. + * dirmngr/Makefile.am: Avoid $(DNSLIBS) for dirmngr_ldap + * g10/Makefile.am: $(LIBREADLINE) is only for gpg2; gpgv2 does not + need $(LIBASSUAN_LIBS) + * sm/Makefile.am: gpgsm does not need $(ZLIBS) + * tools/Makefile.am: gpgconf does not need $(NPTH_LIBS) + +2014-10-08 Werner Koch + + gpg: Avoid error exit if keygrip computations fails in a key listing. + * g10/keyid.c (keygrip_from_pk): Use log_info and clear array on error. + +2014-10-03 Werner Koch + + Release 2.1.0-beta864. + + gpg: Allow creating a cert-only primary key. + * g10/keygen.c (ask_key_flags): Allow a 'c' in direct entry. + + build: Add configure options --disable-{ntb,gnu}tls. + * configure.ac: Add --disable-ntbtls and --disable-gnutls. + +2014-10-03 Andre Heinecke + + gpg: Check gpg-agent version before 2.1 migration. + * g10/call-agent.c, g10/call-agent.h (agent_get_version): New. + * g10/migrate.c (migrate_secring): Abort migration if + agent_get_version returns not at least 2.1.0 + +2014-10-03 Werner Koch + + po: Update German translation. + + Remove support for the GPG_AGENT_INFO envvar. + * agent/agent.h (opt): Remove field use_standard_socket. + * agent/command.c (cmd_killagent): Always allow killing. + * agent/gpg-agent.c (main): Turn --{no,}use-standard-socket and + --write-env-file into dummy options. Always return true for + --use-standard-socket-p. Do not print the GPG_AGENT_INFO envvar + setting or set that envvar. + (create_socket_name): Simplify by removing non standard socket + support. + (check_for_running_agent): Ditto. + * common/asshelp.c (start_new_gpg_agent): Remove GPG_AGENT_INFO use. + * common/simple-pwquery.c (agent_open): Ditto. + * configure.ac (GPG_AGENT_INFO_NAME): Remove. + * g10/server.c (gpg_server): Do not print the AgentInfo comment. + * g13/server.c (g13_server): Ditto. + * sm/server.c (gpgsm_server): Ditto. + * tools/gpgconf.c (main): Simplify by removing non standard socket + support. + +2014-10-02 Werner Koch + + gpg: Fix regression removing SHA256. + * g10/misc.c (map_md_openpgp_to_gcry): Always use SHA256. + + First changes for future use of NTBTLS. + * configure.ac (NEED_NTBTLS_ABI, NEED_NTBTLS_VERSION): New. + (HTTP_USE_NTBTLS): New. Prefer over GNUTLS. + * m4/ntbtls.m4: New. + * m4/Makefile.am (EXTRA_DIST): Add new file. + * common/http.c: Add conditionals to eventually use NTBTLS. + + build: Update m4 scripts. + * m4/gpg-error.m4: Update from Libgpg-error git master. + * m4/libgcrypt.m4: Update from Libgcrypt git master. + * configure.ac: Declare SYSROOT a precious variable. Add extra error + message for library configuration mismatches. + +2014-09-29 Werner Koch + + doc: Remove GnuPG-1 related parts from gpg.texi. + * doc/Makefile.am (YAT2M_OPTIONS): Add 2.1 to the source info. + * doc/gpg.texi: Remove gpg1 related texts. + +2014-09-27 Werner Koch + + gpg: Default to SHA-256 for all signature types on RSA keys. + * g10/main.h (DEFAULT_DIGEST_ALGO): Use SHA256 in --gnupg and SHA1 in + strict RFC or PGP modes. + * g10/sign.c (make_keysig_packet): Use DEFAULT_DIGEST_ALGO also for + RSA key signatures. + * configure.ac: Do not allow to disable sha256. + + gpg: Simplify command --gen-key and add --full-gen-key. + * g10/gpg.c (aFullKeygen): New. + (opts): Add command --full-key-gen. + (main): Implement it. + * g10/keygen.c (DEFAULT_STD_ALGO): Replace wrong GCRY_PK_RSA although + the value is identical. + (DEFAULT_STD_CURVE): New. + (DEFAULT_STD_SUBALGO): New. + (DEFAULT_STD_SUBKEYSIZE): New. + (DEFAULT_STD_SUBCURVE): New. + (quick_generate_keypair): Use new macros here. + (generate_keypair): Add arg "full" and fix call callers. Do not ask + for keysize in non-full node. + (ask_user_id): Add arg "full" and simplify for non-full mode. + +2014-09-26 Werner Koch + + gpg: Add shortcut for setting key capabilities. + * g10/keygen.c (ask_key_flags): Add shortcut '='. + * doc/help.txt (gpg.keygen.flags): New. + +2014-09-25 Werner Koch + + gpg: Do not always print dashes in obsolete_option. + * g10/gpg.c (main): Pass option names to obsolete_option without + double dash. + * g10/misc.c (obsolete_option, obsolete_scdaemon_option): Print double + dash only for command line options. + +2014-09-25 Daniel Kahn Gillmor + + gpg: Warn about (but don't fail) on scdaemon options in gpg.conf. + * g10/gpg.c: Add config options that should belong in scdaemon.conf + * g10/main.h, g10/misc.c (obsolete_scdaemon_option): New. + +2014-09-22 Werner Koch + + speedo: Check that wget and gpgv are installed. + * build-aux/getswdb.sh: Check for required tools. + + speedo: Autodetect sha1sum tools. + * build-aux/getswdb.sh: Add option --find-sha1sum. + * build-aux/speedo.mk (check-tools): New phony target. Not yet used. + (SHA1SUM): New var. Use it instead of sha1sum. + + gpg: Create default keyring with .kbx suffix. + * g10/keydb.c (maybe_create_keyring_or_box): Rename arg for clarity. + (keydb_add_resource): Fix order of args to maybe_create_keyring_or_box + and check and create .kbx. + +2014-09-20 Werner Koch + + gpg: --delete-secret-key - check that a secret key exists. + * g10/delkey.c (do_delete_key): Check availibility of a secret key. + + gpg: Make algorithm selection prompt for ECC more clear. + * g10/keygen.c (ask_algo): Change 9 to "ECC and ECC". + +2014-09-18 Werner Koch + + Release 2.1.0-beta834. + + speedo: Distribute needed files. + * Makefile.am (EXTRA_DIST): Add speedo stuff. + + build: Enable gpgtar by default. + + common: Do not build maintainer modules in non-maintainer mode. + * common/Makefile.am (module_maint_tests): Use only in maintainer + mode. + (t_common_cflags): New. + + common: Remove superfluous statements. + * common/exechelp-posix.c: Remove weak pragmas. + * common/sexputil.c (make_canon_sexp_from_rsa_pk): Remove double + const. + + g13: Avoid segv after pipe creation failure. + * g13/call-gpg.c (gpg_encrypt_blob): Init some vars in case of an + early error. + (gpg_decrypt_blob): Ditto. + + scd: Fix int/short mismatch in format string of app-p15.c. + * scd/app-p15.c (parse_certid): Use snprintf and cast value. + (send_certinfo): Ditto. + (send_keypairinfo): Ditto. + (do_getattr): Ditto. + + agent: Init a local variable in the error case. + * agent/pksign.c (do_encode_md): Init HASH on error. + + agent: Remove left over debug output. + * agent/command-ssh.c (ssh_signature_encoder_eddsa): Remove debug + output. + + agent: Silence compiler warning for a debug message. + * agent/call-pinentry.c (agent_query_dump_state): Use %p for + POPUP_TID. + + sm: Silence compiler warnings. + * sm/certreqgen-ui.c (gpgsm_gencertreq_tty): Remove unused var I. + * sm/certreqgen.c (proc_parameters): Init PUBLIC to avoid compiler + warning. + + gpg: Silence a compiler warning. + * g10/parse-packet.c (enum_sig_subpkt): Replace hack. + + gpg: Replace a hash algo test function. + * g10/gpg.c (print_mds): Replace openpgp_md_test_algo. + + speedo: Various fixes. + * build-aux/speedo.mk: Take zlib and bzip2 from ftp.gnupg.org. Minor + other fixes. + +2014-09-17 Werner Koch + + gpg: Print a warning if the subkey expiration may not be what you want. + * g10/keyedit.c (subkey_expire_warning): New. + (keyedit_menu): Call it when needed. + + gpg: Improve passphrase caching. + * agent/cache.c (last_stored_cache_key): New. + (agent_get_cache): Allow NULL for KEY. + (agent_store_cache_hit): New. + * agent/findkey.c (unprotect): Call new function and try to use the + last stored key. + + * g10/revoke.c (create_revocation): Add arg CACHE_NONCE and pass to + make_keysig_packet. + (gen_standard_revoke): Add arg CACHE_NONCE and pass to + create_revocation. + * g10/keygen.c (do_generate_keypair): Call gen_standard_revoke with + cache nonce. + +2014-09-12 Werner Koch + + gpg: Use algorithm id 22 for EdDSA. + * common/openpgpdefs.h (PUBKEY_ALGO_EDDSA): Change to 22. + * g10/keygen.c (ask_curve): Reword the Curve25519 warning note. + +2014-09-11 Werner Koch + + gpg: Stop early on bogus old style comment packets. + * g10/parse-packet.c (parse_key): Take care of too short packets for + old style commet packets. + +2014-09-10 Werner Koch + + dirmngr: Support https for KS_FETCH. + * dirmngr/ks-engine-hkp.c (cert_log_cb): Move to ... + * dirmngr/misc.c (cert_log_cb): here. + * dirmngr/ks-engine-http.c (ks_http_fetch): Support 307-redirection + and https. + + dirmngr: Fix the ks_fetch command for the http scheme. + * common/http.c (http_session_ref): Allow for NULL arg. + +2014-09-08 Werner Koch + + gpg: Fix memory leak in ECC encryption. + * g10/pkglue.c (pk_encrypt): Fix memory leak and streamline error + handling. + +2014-09-02 Werner Koch + + gpg: Fix export of NIST ECC keys. + * common/openpgp-oid.c (struct oidtable): New. + (openpgp_curve_to_oid): Rewrite and allow OID as input. + (openpgp_oid_to_curve): Make use of the new table. + + agent: Fix import of OpenPGP EdDSA keys. + * agent/cvt-openpgp.c (get_keygrip): Special case EdDSA. + (convert_secret_key): Ditto. + (convert_transfer_key): Ditto. + (apply_protection): Handle opaque MPIs. + + (do_unprotect): Check FLAG_OPAQUE instead of FLAG_USER1 before + unpacking an opaque mpi. + +2014-09-01 Kyle Butt + + gpg: Fix export of ecc secret keys by adjusting check ordering. + * g10/export.c (transfer_format_to_openpgp): Move the check against + PUBKEY_MAX_NSKEY to after the ECC code adjusts the number of + parameters. + +2014-09-01 Werner Koch + + agent: Allow key unprotection using AES-256. + * agent/protect.c (PROT_CIPHER): Rename to GCRY_CIPHER_AES128 for + clarity. + (do_decryption): Add args prot_cipher and prot_cipher_keylen. USe + them instead of the hardwired values. + (agent_unprotect): Change to use a table of protection algorithms. + Add AES-256 variant. + +2014-08-28 Werner Koch + + gpg: Do not show "MD5" and triplicated "RSA" in --version. + * g10/gpg.c (build_list_pk_test_algo): Ignore RSA aliases + (build_list_md_test_algo): Ignore MD5. + + gpg: Do not show "MD5" and triplicated "RSA" in --version. + * g10/gpg.c (build_list_pk_test_algo): Ignore RSA aliases + (build_list_md_test_algo): Ignore MD5. + +2014-08-26 Werner Koch + + gpg: Remove CAST5 from the default prefs and order SHA-1 last. + * g10/keygen.c (keygen_set_std_prefs): Update prefs. + + Switch to the libgpg-error provided estream. + * configure.ac (NEED_GPG_ERROR_VERSION): Reguire 1.14. + (GPGRT_ENABLE_ES_MACROS): Define. + (estream_INIT): Remove. + * m4/estream.m4: Remove. + * common/estream-printf.c, common/estream-printf.h: Remove. + * common/estream.c, common/estream.h: Remove. + * common/init.c (_init_common_subsystems): Call gpgrt initialization. + + gpg: Allow for positional parameters in the passphrase prompt. + * g10/passphrase.c (passphrase_get): Replace sprintf by xasprintf. + +2014-08-20 Werner Koch + + gpg: Fix "can't handle public key algorithm" warning. + * g10/parse-packet.c (unknown_pubkey_warning): Check for encr/sign + capabilities. + +2014-08-19 Werner Koch + + speedo: Get version numbers from online database. + * build-aux/getswdb.sh: New. + * build-aux/speedo.mk: Get release version numbers from swdb.lst. + + build: Create VERSION file via autoconf. + * Makefile.am (dist-hook): Remove creation of VERSION. + (EXTRA_DIST): Add VERSION. + * configure.ac: Let autoconf create VERSION. + +2014-08-18 Werner Koch + + gpg: Install the current release signing pubkey. + * g10/distsigkey.gpg: New. + + agent: Return NO_SECKEY instead of ENONET for PKSIGN and others. + * agent/pksign.c (agent_pksign_do): Replace ENONET by NO_SECKEY. + * agent/findkey.c (agent_key_from_file): No diagnostic for NO_SECKEY. + * agent/pkdecrypt.c (agent_pkdecrypt): Replace checking for ENOENT. + + kbx: Make user id and signature data optional for OpenPGP. + * kbx/keybox-blob.c (_keybox_create_openpgp_blob): Remove restriction. + + gpg: Change default cipher for --symmetric from CAST5 to AES-128. + * g10/main.h (DEFAULT_CIPHER_ALGO): Chhange to AES or CAST5 or 3DES + depending on configure option. + * g10/gpg.c (main): Set opt.s2k_cipher_algo to DEFAULT_CIPHER_ALGO. + + yat2m: Support @set and @value. + * doc/yat2m.c (variablelist): New. + (set_variable): New. + (macro_set_p): Also check the variables. + (proc_texi_cmd): Support the @value command. + (parse_file): Support the @set command. + (top_parse_file): Release variablelist. + + yat2m: Support the $* command for man page rendering. + +2014-08-17 Werner Koch + + estream: Change license from GPL to LPGL. + * common/estream-printf.c, common/estream-printf.h: Change license. + * common/estream.c, common/estream.h: Ditto. + +2014-08-14 Werner Koch + + Release 2.1.0-beta783. + + po: Update the German (de) translation. + + sm: Create homedir and lock empty keybox creation. + * sm/gpgsm.h (opt): Add field "no_homedir_creation". + * sm/gpgsm.c (main): Set it if --no-options is used. + * sm/keydb.c (try_make_homedir): New. Similar to the one from + g10/openfile.c. + (maybe_create_keybox): New. Similar to the one from g10/keydb.c. + (keydb_add_resource): Replace some code by maybe_create_keybox. + + gpg: Screen keyserver responses. + * g10/main.h (import_screener_t): New. + * g10/import.c (import): Add screener callbacks to param list. + (import_one): Ditto. + (import_secret_one): Ditto. + (import_keys_internal): Ditto. + (import_keys_stream): Ditto. + * g10/keyserver.c (struct ks_retrieval_screener_arg_s): New. + (keyserver_retrieval_screener): New. + (keyserver_get): Pass screener to import_keys_es_stream(). + + scd: Minor changes to app-sc-hsm. + * scd/app-sc-hsm.c: Re-indendet some parts and set some vars to NULL + after xfree for improbed robustness. + (read_ef_prkd): Replace serial operator by blocks for better + readability. + (apply_PKCS_padding): Rewrite for easier auditing. + (strip_PKCS15_padding): Ditto. Add stricter check on SRCLEN. + + gpg: Disable an MD5 workaround for pgp2 by default. + * g10/sig-check.c (do_check): Move some code to ... + * g10/misc.c (print_md5_rejected_note): new function. + * g10/mainproc.c (proc_tree, proc_plaintext): Enable MD5 workaround + only if option --allow-weak-digest-algos is used. + + gpg: Remove options --pgp2 and --rfc1991. + * g10/gpg.c (oRFC1991, oPGP2): Remove + (opts): Remove --pgp2 and --rfc1991. + * g10/options.h (CO_PGP2, CO_RFC1991): Remove. Remove all users. + (RFC2440, PGP2): Remove. Remove all code only enabled by these + conditions. + * tests/openpgp/clearsig.test: Remove --rfc1991 test. + + build: Fix autogen.sh base version hack. + * autogen.sh : Fix. + + gpg: Remove --compress-keys and --compress-sigs feature. + * g10/gpg.c (oCompressKeys, oCompressSigs): Remove. + (opts): Turn --compress-keys and --compress-signs in NOPs. + * g10/options.h (opt): Remove fields compress_keys and compress_sigs. + * g10/export.c (do_export): Remove compress_keys feature. + * g10/sign.c (sign_file): Remove compress_sigs feature. + +2014-08-13 Werner Koch + + gpg: Add list-option "show-usage". + * g10/gpg.c (parse_list_options): Add "show-usage". + * g10/options.h (LIST_SHOW_USAGE): New. + * g10/keyid.c (usagestr_from_pk): Add arg FILL. Change caller. + * g10/keylist.c (list_keyblock_print): Print usage info. + +2014-08-12 Werner Koch + + gpg: Make --with-colons work again for --search-keys. + * g10/keyserver.c (search_line_handler): Replace log_debug by + es_printf. + +2014-08-08 NIIBE Yutaka + + po: Update Japanese translation. + +2014-07-25 Werner Koch + + scd: Minor and editorial changes to app-sc-hsm.c. + * scd/app-sc-hsm.c (select_and_read_binary): Use SW_ macro. + (parse_certid): Remove useless test. + (send_certinfo, send_keypairinfo): Shrink malloc to the needed size. + (do_getattr): Ditto. + (verify_pin): Use SW_ macro. + (do_decipher): Replace OFS variable and extend comment. + + scd: Add a new status word code. + * scd/apdu.h (SW_REF_DATA_INV): New. + * scd/apdu.c (apdu_strerror): Add string. + +2014-07-25 Andreas Schwier + + scd: Support for SmartCard-HSM. + * scd/app-sc-hsm.c: New. + * scd/app.c (select_application, get_supported_applications): Register + new app. + +2014-07-25 Werner Koch + + gpg: Switch to an EdDSA format with prefix byte. + * g10/keygen.c (gen_ecc): USe "comp" for EdDSA. + +2014-07-23 Werner Koch + + agent: Show just one warning with all failed passphrase constraints. + * agent/genkey.c (check_passphrase_constraints): Build a final warning + after all checks. + + agent: Only one confirmation prompt for an empty passphrase. + * agent/genkey.c (check_passphrase_constraints): Moev empty passphrase + check to the front. + + gpg: Add command --quick-gen-key. + * g10/gpg.c (aQuickKeygen): New. + * g10/misc.c (is_valid_user_id): New stub. + * g10/keygen.c (quickgen_set_para): New. + (quick_generate_keypair): New. + + common: Add cpr_get_answer_is_yes_def() + * g10/cpr.c (cpr_get_answer_is_yes): Factor code out to .... + (cpr_get_answer_is_yes_def): ...new. + + gpg: Make --quick-sign-key promote local key signatures. + * g10/keyedit.c (sign_uids): Promote local sigs in quick mode. + +2014-07-22 Werner Koch + + scd: Do not use the pcsc-wrapper. + * scd/apdu.c (NEED_PCSC_WRAPPER): Do not define. + * scd/Makefile.am (libexec_PROGRAMS): Remove gnupg-pcsc-wrapper + (gnupg_pcsc_wrapper_SOURCES): Remove. + (gnupg_pcsc_wrapper_LDADD): Remove. + (gnupg_pcsc_wrapper_CFLAGS): Remove. + +2014-07-21 Werner Koch + + gpg: Improve --list-packets output for faulty packets. + * g10/parse-packet.c: Add list_mode output for certain failures. + + gpg: Cap size of attribute packets at 16MB. + * g10/parse-packet.c (parse_attribute): Avoid xmalloc failure and cap + size of packet. + +2014-07-03 Werner Koch + + Release 2.1.0-beta751. + + gpg: Make show-uid-validity the default. + + tests: Fix end-of-all-ticks test for Western locales. + * common/t-timestuff.c (test_timegm): Use timegm if available. + (main): Set TX to UTC if timegm is not available. + +2014-07-03 Kristian Fiskerstrand + + gpg: Spelling error. + +2014-06-30 Werner Koch + + gpg: Auto-create revocation certificates. + * configure.ac (GNUPG_OPENPGP_REVOC_DIR): New config define. + * g10/revoke.c (create_revocation): Add arg "leadin". + (gen_standard_revoke): New. + * g10/openfile.c (get_openpgp_revocdir): New. + (open_outfile): Add MODE value 3. + * g10/keyid.c (hexfingerprint): New. + * g10/keygen.c (do_generate_keypair): Call gen_standard_revoke. + + estream: Fix minor glitch in "%.*s" format. + * common/estream-printf.c (pr_string): Take care of non-nul terminated + strings. + + gpg: Rearrange code in gen_revoke. + * g10/revoke.c (gen_revoke): Factor some code out to ... + (create_revocation): new. + + gpg: Create exported secret files and revocs with mode 700. + * common/iobuf.c (direct_open): Add arg MODE700. + (iobuf_create): Ditto. + * g10/openfile.c (open_outfile): Add arg RESTRICTEDPERM. Change call + callers to pass 0 for it. + * g10/revoke.c (gen_desig_revoke, gen_revoke): Here pass true for new + arg. + * g10/export.c (do_export): Pass true for new arg if SECRET is true. + + common: Minor code cleanup for a legacy OS. + * common/iobuf.c (direct_open) [__riscos__]: Simply cpp conditionals. + +2014-06-27 Werner Koch + + speedo: Fix the w32 installer name. + + po: Update some strings of the French (fr) translation. + + po: Update the German (de) translation. + + agent: Adjust for changed npth_eselect under W32. + * agent/gpg-agent.c (handle_connections) [W32]: Make events_set an + unsigned int to match the changed prototype. + + dirmngr: Use the homedir based socket also under W32. + * common/homedir.c (dirmngr_user_socket_name): Use same code for all + platforms. + +2014-06-27 Yuri Chornoivan + + po: Update and enable Ukrainian (uk) translation. + + Fix typos in messages. + +2014-06-27 Werner Koch + + build: Remove unused options. + * configure.ac: Remove option --build-agent-only. + (FAKE_CURL, GPGKEYS_CURL): Remove check for cURL + (GPGKEYS_MAILTO): Remove ac_subst but keep the currently unused + SENDMAIL check. + (GPGKEYS_KDNS): Remove ac_subst. + * autogen.rc (final_info): Remove suggestion to use the removed option + --enable-mailto. + +2014-06-27 NIIBE Yutaka + + scd: Add pinpad support for REINER SCT cyberJack go. + * scd/ccid-driver.h (VENDOR_REINER, CYBERJACK_GO): New. + * scd/ccid-driver.c (ccid_transceive_secure): Handle the case for + VENDOR_REINER. Original work was by Alina Friedrichsen (tiny change). + +2014-06-27 Werner Koch + + scd: Support reader Gemalto IDBridge CT30. + * scd/ccid-driver.h (GEMPC_CT30): New product id. + * scd/ccid-driver.c (parse_ccid_descriptor): Add quirk for that + reader. + +2014-06-26 Werner Koch + + gpg: Limit keysize for unattended key generation to useful values. + * g10/keygen.c (gen_elg): Enforce keysize 1024 to 4096. + (gen_rsa): Enforce keysize 1024 to 4096. + (gen_dsa): Enforce keysize 768 to 3072. + + Enable DNS SRV records again. + * configure.ac (GPGKEYS_HKP, GPGKEYS_FINGER): Remove ac_subst. + (use_dns_srv): Make test work. + + agent: Fix export of RSA keys to OpenPGP. + * agent/cvt-openpgp.c (convert_transfer_key): Fix sexp build format + string. + + gpg,gpgsm: Simplify wrong_args function. + + speedo: "make clean-gnupg" may not remove the source. + * build-aux/speedo.mk (clean-$(1)): Take care of gnupg. + + gpgsm: Fix default config name. + +2014-06-25 Werner Koch + + doc: Improve the rendering of the manual. + + doc: Update for modern makeinfo. + * doc/texi.css: Remove. + * doc/Makefile.am (AM_MAKEINFOFLAGS): Use --css-ref. + + gpg: Allow key-to-card upload for cert-only keys. + * g10/card-util.c (card_store_subkey): Allo CERT usage for key 0. + +2014-06-24 Werner Koch + + doc: Add conditionals for GnuPG-1. + +2014-06-20 Werner Koch + + gpg: Make export of ECC keys work again. + * agent/cvt-openpgp.c (convert_to_openpgp): Use the curve name instead + of the curve parameters. + * g10/export.c (canon_pubkey_algo): Rename to ... + (canon_pk_algo): this. Support ECC. + (transfer_format_to_openpgp): Expect curve name. + + gpg: Avoid infinite loop in uncompressing garbled packets. + * g10/compress.c (do_uncompress): Limit the number of extra FF bytes. + +2014-06-17 Kristian Fiskerstrand + + gpg: Fix a couple of spelling errors. + +2014-06-17 Werner Koch + + speedo: Support building from dist-source generated tarball. + +2014-06-13 Werner Koch + + http: Print human readable GNUTLS status. + * common/http.c (send_gnutls_bye): Take care of EAGAIN et al. + (http_verify_server_credentials): Print a human readable status. + +2014-06-12 Werner Koch + + gpg: Improve the output of --list-packets. + * g10/parse-packet.c (parse): Print packet meta info in list mode. + +2014-06-11 Werner Koch + + speedo: Improve building of the w32 installer. + * build-aux/speedo.mk: Change name of build directory to PLAY. + Improve the dist-source target. + * build-aux/speedo/w32/gdk-pixbuf-loaders.cache: Add a blank + line (plus comment). + * build-aux/speedo/w32/inst.nsi: Change name of file to gnupg-w32-*. + Install more tools. + +2014-06-10 Werner Koch + + speedo: Revamped speedo and include a w32 installer. + * build-aux/speedo/: New. + * build-aux/speedo/w32/: New. + + build: Add more options to autogen.sh. + * autogen.sh: Add options --print-host and --print-build. + + w32: Fix build problem with dirmngr. + * dirmngr/ks-engine-hkp.c (EAI_SYSTEM) [W32]: Add replacement + constant. + + gpg: Use more specific reason codes for INV_RECP. + * g10/pkclist.c (find_and_check_key, build_pk_list): Use more specific + reasons codes for INV_RECP. + +2014-06-06 Werner Koch + + Improve the beta number generation. + * autogen.sh: Add option --find-version + * configure.ac: Rework the setting of the mym4_ variables. + +2014-06-05 Werner Koch + + Remove keyserver helper code. + * configure.ac: Remove keyserver helper related stuff. + * Makefile.am (SUBDIRS): Remove keyserver. + * keyserver/Makefile.am: Remove. + + gpg: Require confirmation for --gen-key with experimental curves. + * g10/keygen.c (ask_curve): Add arg both. Require confirmation for + Curve25519. + + gpg: Auto-migrate existing secring.gpg. + * g10/migrate.c: New. + * g10/import.c (import_old_secring): New. + (import_one): Add arg silent. + (transfer_secret_keys): Add arg batch. + (import_secret_one): Add args batch and for_migration. + * g10/gpg.c (main): Call migration function. + +2014-06-04 Werner Koch + + gpgsm: Fix commit be07ed65. + * sm/server.c (option_handler): Use "with-secret". + +2014-06-03 Werner Koch + + Add new option --with-secret. + * g10/gpg.c: Add option --with-secret. + * g10/options.h (struct opt): Add field with_secret. + * g10/keylist.c (public_key_list): Pass opt.with_secret to list_all + and list_one. + (list_all, list_one): Add arg mark_secret. + (list_keyblock_colon): Add arg has_secret. + * sm/gpgsm.c: Add option --with-secret. + * sm/server.c (option_handler): Add option "with-secret". + * sm/gpgsm.h (server_control_s): Add field with_secret. + * sm/keylist.c (list_cert_colon): Take care of with_secret. Also move + the token string from the wrong field 14 to 15. + + gpgsm: New commands --export-secret-key-{p8,raw} + * sm/gpgsm.c: Add new commands. + * sm/minip12.c (build_key_sequence): Add arg mode. + (p12_raw_build): New. + * sm/export.c (export_p12): Add arg rawmode. Call p12_raw_build. + (gpgsm_p12_export): Ditto. + (print_short_info): Print the keygrip. + +2014-06-02 Werner Koch + + gpg: Avoid NULL-deref in default key listing. + * g10/keyid.c (hash_public_key): Take care of NULL keys. + * g10/misc.c (pubkey_nbits): Ditto. + + gpg: Simplify default key listing. + * g10/mainproc.c (list_node): Rework. + + gpg: Graceful skip reading of corrupt MPIs. + * g10/parse-packet.c (mpi_read): Change error message on overflow. + + gpgsm: Handle re-issued CA certificates in a better way. + * sm/certchain.c (find_up_search_by_keyid): Consider all matching + certificates. + (find_up): Add some debug messages. + + gpgsm: Add a way to save a found state. + * kbx/keybox-defs.h (keybox_found_s): New. + (keybox_handle): Factor FOUND out to above. Add saved_found. + * kbx/keybox-init.c (keybox_release): Release saved_found. + (keybox_push_found_state, keybox_pop_found_state): New. + + * sm/keydb.c (keydb_handle): Add field saved_found. + (keydb_new): Init it. + (keydb_push_found_state, keydb_pop_found_state): New. + + gpg: Fix bug parsing a zero length user id. + * g10/getkey.c (get_user_id): Do not call xmalloc with 0. + + * common/xmalloc.c (xmalloc, xcalloc): Take extra precaution not to + pass 0 to the arguments. + +2014-05-19 Werner Koch + + dirmngr: Print certificates on failed TLS verification. + * dirmngr/ks-engine-hkp.c (cert_log_cb): New. + (send_request): Set callback. + + http: Add callback to help logging of server certificates. + * common/http.c (http_session_s): Add field cert_log_cb. + (http_session_set_log_cb): New. + (http_verify_server_credentials): Call callback. + +2014-05-16 Werner Koch + + keyserver: Improve support for hkps pools. + * dirmngr/ks-engine-hkp.c (hostinfo_s): Add fields cname, v4addr, and + v6addr. + (create_new_hostinfo): Clear them. + (my_getnameinfo): Add args numeric and r_isnumeric. + (is_ip_address): New. + (map_host): Add arg r_host. Rewrite the code to handle pools in a + special way. + (ks_hkp_print_hosttable): Change format of help info output. + (make_host_part): Add arg optional r_httphost. + (send_request): Add arg httphost. + (ks_hkp_search, ks_hkp_get, ks_hkp_put): Get httphost and pass it to + send_request. + + http: Allow overriding of the Host header. + * common/http.c (http_open): Add arg httphost. + (http_open_document): Pass NULL for httphost. + (send_request): Add arg httphost. If given, use HTTPHOST instead of + SERVER. Use https with a proxy if requested. + (http_verify_server_credentials): Do not stop at the first error + message. + * dirmngr/ocsp.c (do_ocsp_request): Adjust call to http_open. + * keyserver/curl-shim.c (curl_easy_perform): Ditto. + * dirmngr/ks-engine-http.c (ks_http_fetch): Ditto. + * dirmngr/ks-engine-hkp.c (ks_hkp_help): Ditto. + +2014-05-14 Werner Koch + + gpg: Fix uninitialized access to search descindex with gpg keyboxes. + * kbx/keybox-search.c (keybox_search): Add arg R_DESCINDEX. Chnage + both callers. + * g10/keydb.c (keydb_search): Always set DESCINDEX. + + w32: Make make_absfilename work with drive letters. + * common/stringhelp.c (do_make_filename) [HAVE_DRIVE_LETTERS]: Fix. + + gpg: Remove useless diagnostic in MDC verification. + * g10/decrypt-data.c (decrypt_data): Do not distinguish between a bad + MDC packer header and a bad MDC. + + gpg: Fix glitch entering a full expiration time. + * g10/keygen.c (ask_expire_interval): Get the current time after the + prompt. + +2014-05-08 Werner Koch + + agent: Fix import of non-protected gpg keys. + * agent/cvt-openpgp.c (do_unprotect): Return an s-exp also for + non-protected keys. + (convert_from_openpgp_main): Do not call agent_askpin for a + non-protected key. + + Make more use of *_NAME macros. + * configure.ac (GPG_DISP_NAME, GPGSM_DISP_NAME): New. + (GPG_AGENT_DISP_NAME, SCDAEMON_DISP_NAME): New. + (DIRMNGR_DISP_NAME, G13_DISP_NAME): New. + (GPGCONF_DISP_NAME): New. + (SCDAEMON_SOCK_NAME): New. + * common/argparse.c (show_help): Map description string. + +2014-05-08 NIIBE Yutaka + + agent: Fix auth key comment handling. + * agent/command-ssh.c (ssh_send_key_public): Handle the case with no + comment. + +2014-05-07 Werner Koch + + Make -jN work again. + * common/Makefile.am ($(PROGRAMS)): New rule + (t_http_LDADD): Use libcommontls.a without directory prefix. + * dirmngr/Makefile.am ($(PROGRAMS)): New rule. + + gpg: Print the key algorithm/curve with signature info. + * g10/mainproc.c (check_sig_and_print): Print the name and curve. + + gpg: Fix memleak in signature verification of bogus keys. + * g10/mainproc.c (check_sig_and_print): Factor common code out to ... + (print_good_bad_signature): here. + + gpg: Mark experimental algorithms in the key listing. + * g10/keylist.c (list_keyblock_print): Remove duplicate curve name. + Print a note for experimental algorithms. + * g10/misc.c (print_pubkey_algo_note): Fix warning message. + + gpg: Finish experimental support for Ed25519. + * agent/cvt-openpgp.c (try_do_unprotect_arg_s): Add field "curve". + (get_keygrip): Add and use arg CURVE. + (convert_secret_key): Ditto. + (convert_transfer_key): Ditto. + (get_npkey_nskey): New. + (prepare_unprotect): Replace gcrypt functions by + get_npkey_nskey. Allow opaque MPIs. + (do_unprotect): Use CURVE instead of parameters. + (convert_from_openpgp_main): Ditto. + (convert_to_openpgp): Simplify. + * g10/import.c (one_mpi_from_pkey): Remove. + (transfer_secret_keys): Rewrite to use the curve instead of the + parameters. + * g10/parse-packet.c (parse_key): Mark protected MPIs with USER1 flag. + + * common/openpgp-oid.c (openpgp_curve_to_oid): Allow the use of + "NIST P-256" et al. + * g10/keygen.c (ask_curve): Add arg ALGO. + (generate_keypair): Rewrite the ECC key logic. + + * tests/openpgp/ecc.test: Provide the "ecc" passphrase. + + kbx: Add experimental support for EDDSA. + * kbx/keybox-openpgp.c (parse_key): Use algo constants and add + experimental support for EdDSA. + + agent: Remove greeting message. + * agent/gpg-agent.c (main): Remove greeting. Make --no-greeting a + dummy. + +2014-05-06 Werner Koch + + Use "samethread" mode keyword for some es_fopenmem. + * dirmngr/ks-engine-hkp.c (armor_data): Add mode keyword. + * g10/call-dirmngr.c (ks_put_inq_cb): Ditto. + * scd/atr.c (atr_dump): Ditto. + +2014-05-05 Werner Koch + + dirmngr: Add support for hkps keyservers. + * dirmngr/dirmngr.c: Include gnutls.h. + (opts): Add --gnutls-debug and --hkp-cacert. + (opt_gnutls_debug, my_gnutls_log): New. + (set_debug): Set gnutls log level. + (parse_rereadable_options): Register a CA file. + (main): Init GNUTLS. + * dirmngr/ks-engine-hkp.c (ks_hkp_help): Support hkps. + (send_request): Ditto. + + http: Add reference counting to the session object. + * common/http.c (http_session_t): Add field "refcount". + (_my_socket_new, _my_socket_ref, _my_socket_unref): Add debug code. + (send_request, my_npth_read, my_npth_write): Use SOCK object for the + transport ptr. + (http_session_release): Factor all code out to ... + (session_unref): here. Deref SOCK. + (http_session_new): Init refcount and transport ptr. + (http_session_ref): New. Ref and unref all assignments. + +2014-05-02 Werner Koch + + http: Add HTTP_FLAG_FORCE_TLS and http_get_tls_info. + * common/http.c (http_parse_uri): Factor code out to ... + (parse_uri): here. Add arg FORCE_TLS. + (do_parse_uri): Ditto. Implement flag. + (http_get_tls_info): New. + (http_register_tls_ca): Allow clearing of the list. + (send_request): Use a default verification function. + * common/http.h (HTTP_FLAG_FORCE_TLS): New. + * common/t-http.c (main): Add several command line options. + + common: Fix test for openpgp_oid_is_ed25519. + * common/t-openpgp-oid.c (test_openpgp_oid_is_ed25519): Add correct + value. + + http: Revamp TLS API. + * configure.ac (NEED_GNUTLS_VERSION): New. + (HTTP_USE_GNUTLS, LIBGNUTLS_CFLAGS, LIBGNUTLS_LIBS): New ac_subst. + + * common/http.h (http_session_t): New. + * common/http.c: Remove compatibility for gnutls < 3.0. + (http_session_s): New. + (cookie_s): Replace gnutls_session_t by http_session_t. + (tls_callback, tls_ca_certlist): New variables. + (my_socket_unref): Add preclose args. + (my_npth_read, my_npth_write): New. + (make_header_line): Fix bug using int* instead of char*. + (http_register_tls_callback): New. + (http_register_tls_ca): New. + (http_session_new): New. + (http_session_release): New. + (http_get_header_names): New. + (escape_data): Add hack to escape in forms mode. + (send_request) [HTTP_USE_GNUTLS]: Support SNI. + (send_request) [HTTP_USE_GNUTLS]: Fix use of make_header_line. + (send_gnutls_bye): New. + (cookie_close): Make use of preclose feature. + (http_verify_server_credentials): New. + (main) [TEST]: Remove test code. + * common/t-http.c: New. + * common/tls-ca.pem: New. + * common/Makefile.am (tls_sources): New. Move http code to here. + (libcommontls_a_SOURCES): New. + (libcommontlsnpth_a_SOURCES): New. + (EXTRA_DIST): Add tls-ca.pem + (module_maint_tests): Add t-http. + (t_http_SOURCES, t_http_CFLAGS, t_http_LDADD): New. + + * dirmngr/Makefile.am (dirmngr_LDADD): Add libcommontlsnpth. + + common: Cleanup the use of USE_NPTH and HAVE_NPTH macros. + * configure.ac (HAVE_NPTH): New ac_define. + * common/estream.c: Use USE_NPTH instead of HAVE_NPTH. + * common/http.c: Ditto. Replace remaining calls to pth by npth calls. + (connect_server): Remove useless _(). + * common/exechelp-posix.c, common/exechelp-w32.c + * common/exechelp-w32ce.c: Use HAVE_PTH to include npth.h. + * common/init.c (_init_common_subsystems): Remove call to pth_init. + * common/sysutils.c (gnupg_sleep): Use npth_sleep. + * scd/ccid-driver.c (my_sleep): Ditto. + +2014-04-30 Werner Koch + + estream: Implement "samethread" mode keyword. + * src/estream.c (estream_internal): Add field SAMETHREAD. + (init_stream_lock, lock_stream, trylock_stream, unlock_stream): Use it. + (parse_mode): Add arg SAMETHREAD and parse that keyword. + (es_initialize): Rename to ... + (init_stream_obj): this. Add arg SAMETHREAD. + (es_create): Add arg SAMETHREAD. Call init_stream_lock after + init_stream_obj. + (doreadline): Call es_create with samethread flag. + (es_fopen, es_mopen, es_fopenmem, es_fopencookie, do_fdopen) + (do_fpopen, do_w32open): Implement "samethread" keyword. + (es_freopen): Take samthread flag from old stream. + (es_tmpfile): Call es)_create w/o samethread. + + estream: Fix deadlock in es_fileno. + * src/estream.c (es_fileno_unlocked): Call the unlocked functions. + + estream: Add debug code to the lock functions. + * common/estream.c (dbg_lock_0, dbg_lock_1, dbg_lock_1): New. + + estream: Replace locking macros by functions. + * common/estream.c: Replace most macros. + +2014-04-28 NIIBE Yutaka + + ECC Fixes. + * agent/cvt-openpgp.c (get_keygrip, convert_secret_key) + (convert_transfer_key): Follow newer (>= 1.6) libgcrypt API, which + does not distinguish the detail. + (do_unprotect, convert_from_openpgp_main): Don't call + map_pk_openpgp_to_gcry, as it's the value of libgcrypt API already and + not the value defined by OpenPGP. + (convert_to_openpgp): It's "ecc". + * agent/gpg-agent.c (map_pk_openpgp_to_gcry): Remove. + * g10/call-agent.c (agent_pkdecrypt): Fix off-by-one error. + * g10/pubkey-enc.c (get_it): Fix swapping the fields error. + +2014-04-22 Werner Koch + + gpg: Pass --homedir to gpg-agent. + * agent/gpg-agent.c (main): Make sure homedir is absolute. + * common/asshelp.c (lock_spawning): Create lock file with an absolute + name. + (start_new_gpg_agent): Use an absolute name for the socket and pass + option --homedir to the agent. + (start_new_dirmngr): Use an absolute name for the --homedir. + + common: Add functions make_absfilename and make_absfilename_try. + * common/stringhelp.c (do_make_filename): Add modes 2 and 3. + (make_absfilename): New. + (make_absfilename_try): New. + + common: Add function gnupg_getcwd. + * tools/gpg-connect-agent.c (gnu_getcwd): Move to ... + * common/sysutils.c (gnupg_getcwd): .. here. + * tools/gpg-connect-agent.c (get_var_ext): Use gnupg_getcwd. + + gpg: Print a warning if GKR has hijacked gpg-agent. + * g10/call-agent.c (check_hijacking): New. + (start_agent): Call it. + (membuf_data_cb, default_inq_cb): Move more to the top. + +2014-04-17 Werner Koch + + gpg: New %U expando for the photo viewer. + * g10/photoid.c (show_photos): Set namehash. + * g10/misc.c (pct_expando): Add "%U" expando. + + common: Add z-base-32 encoder. + * common/zb32.c: New. + * common/t-zb32.c: New. + * common/Makefile.am (common_sources): Add zb82.c + (module_tests): Add t-zb32. + +2014-04-16 Werner Koch + + Two minor code cleanups and one NULL deref on error fix. + * common/estream.c (es_freopen): Remove useless check for STREAM. + * kbx/keybox-blob.c (_keybox_create_x509_blob): Remove useless check + for BLOB. + * tools/sockprox.c (run_proxy): Do not fclose(NULL). + +2014-04-15 Werner Koch + + gpg: Re-enable secret key deletion. + * g10/call-agent.c (agent_delete_key): New. + * g10/keydb.h (FORMAT_KEYDESC_DELKEY): New. + * g10/passphrase.c (gpg_format_keydesc): Support new format. + * g10/delkey.c (do_delete_key): Add secret key deletion. + + gpg: Re-indent a file. + * g10/delkey.c: Re-indent. + (do_delete_key, delete_keys): Change return type top gpg_error_t. + + gpg: Fix regression in secret key export. + * agent/cvt-openpgp.c (convert_to_openpgp): Fix use + gcry_sexp_extract_param. + * g10/export.c (do_export_stream): Provide a proper prompt to the + agent. + + gpg: Change pinentry prompt to talk about "secret key". + * g10/passphrase.c (gpg_format_keydesc): Add mode 2. Change strings. + * g10/keydb.h (FORMAT_KEYDESC_NORMAL, FORMAT_KEYDESC_IMPORT) + (FORMAT_KEYDESC_EXPORT): New. Use them for clarity. + + agent: Add command DELETE_KEY. + * agent/command.c (cmd_delete_key): New. + * agent/findkey.c (modify_description): Add '%C' feature. + (remove_key_file): New. + (agent_delete_key): New. + * agent/command-ssh.c (search_control_file): Make arg R_DISABLE + optional. + + * configure.ac: Require libgpg-error 1.13. + +2014-04-09 NIIBE Yutaka + + scd: EdDSA support. + * scd/app-openpgp.c (KEY_TYPE_EDDSA, CURVE_ED25519): New. + (struct app_local_s): Add eddsa. + (get_algo_byte, store_fpr): Support KEY_TYPE_EDDSA. + (get_ecc_key_parameters, get_curve_name): Support CURVE_ED25519. + (send_key_attr, get_public_key): Support KEY_TYPE_EDDSA. + (build_ecc_privkey_template): Rename as it supports both of + ECDSA and EdDSA. + (ecc_writekey): Rename. Support CURVE_ED25519, too. + (do_writekey): Follow the change of ecc_writekey. + (do_auth): Support KEY_TYPE_EDDSA. + (parse_ecc_curve): Support CURVE_ED25519. Bug fix for other curves. + (parse_algorithm_attribute): Bug fix for ECDH. Support EdDSA. + +2014-04-08 Werner Koch + + dirmngr: Fix compiler warning. + * common/mischelp.h (JNLIB_GCC_HAVE_PUSH_PRAGMA): New. + * dirmngr/dirmngr.c (handle_tick): Factor time check out to ... + (time_for_housekeeping_p): new. + + gpgconf: Add command --launch. + * tools/gpgconf.c: Add command --launch. + * tools/gpgconf-comp.c (gc_component_launch): New. + + scd: Silent compiler warnings about unused variables. + * scd/app-openpgp.c (build_ecdsa_privkey_template): Mark unused arg. + (ecdh_writekey): Mark unused args. + +2014-04-08 NIIBE Yutaka + + agent: Support EdDSA. + * agent/pksign.c (agent_pksign_do): Handle EdDSA signature. + + g10: EdDSA support. + * g10/keyid.c (keygrip_from_pk): Compute keygrip of EdDSA key. + * g10/keygen.c (generate_subkeypair): Ed25519 is for EdDSA. + * common/openpgp-oid.c (oid_ed25519): Update. + +2014-04-04 NIIBE Yutaka + + agent: EdDSA support for SSH. + * agent/command-ssh.c (ssh_signature_encoder_eddsa): Signature is + two 32-byte opaque data which should not be interpreted as number. + +2014-03-27 Werner Koch + + gpg: Add commands --quick-sign-key and --quick-lsign-key. + * g10/gpg.c (main): Add commands --quick-sign-key and + --quick-lsign-key. + * g10/keyedit.c (sign_uids): Add args FP and QUICK. + (keyedit_quick_sign): New. + (show_key_with_all_names): Add arg NOWARN. + + Change some keyedit functions to allow printing to arbitrary streams. + * common/ttyio.c (tty_print_string): Add optional arg FP. Change all + callers. + (tty_print_utf8_string2): Ditto. + * g10/keyedit.c (show_prefs): Ditto. + (show_key_with_all_names_colon): Ditto. + (show_names): Ditto. + * g10/keylist.c (print_revokers): Ditto. + (print_fingerprint): Ditto. + +2014-03-23 Werner Koch + + agent: Replace es_mopen by es_fopenmem for ssh. + * agent/command-ssh.c (ssh_read_key_public_from_blob): Use + es_fopenmem. + (ssh_handler_request_identities): Ditto. + (ssh_request_process): Ditto. + +2014-03-22 Werner Koch + + agent: Put ssh key type as comment into sshcontrol. + * agent/command-ssh.c (ssh_key_type_spec): Add field name. + (ssh_key_types): Add human readable names. + (add_control_entry): Add arg SPEC and print key type as comment. + (ssh_identity_register): Add arg SPEC. + (ssh_handler_add_identity): Add var SPEC and pass ssh_receive_key. + + agent: Support the Ed25519 signature algorithm for ssh. + * agent/command-ssh.c (SPEC_FLAG_IS_EdDSA): New. + (ssh_key_types): Add entry for ssh-ed25519. + (ssh_identifier_from_curve_name): Move to the top. + (stream_read_skip): New. + (stream_read_blob): New. + (ssh_signature_encoder_rsa): Replace MPIS array by an s-exp and move + the s-exp parsing to here. + (ssh_signature_encoder_dsa): Ditto. + (ssh_signature_encoder_ecdsa): Ditto. + (ssh_signature_encoder_eddsa): New. + (sexp_key_construct): Rewrite. + (ssh_key_extract): Rename to ... + (ssh_key_to_blob): .. this and rewrite most of it. + (ssh_receive_key): Add case for EdDSA. + (ssh_convert_key_to_blob, key_secret_to_public): Remove. + (ssh_send_key_public): Rewrite. + (ssh_handler_request_identities): Simplify. + (data_sign): Add rename args. Add new args HASH and HASHLEN. Make + use of es_fopenmen and es_fclose_snatch. Remove parsing into MPIs + which is now doe in the sgnature encoder functions. + (ssh_handler_sign_request): Take care of Ed25519. + (ssh_key_extract_comment): Rewrite using gcry_sexp_nth_string. + + agent: Cleanups to prepare implementation of Ed25519. + * agent/cvt-openpgp.c: Remove. + (convert_to_openpgp): Use gcry_sexp_extract_param. + * agent/findkey.c (is_eddsa): New. + (agent_is_dsa_key, agent_is_eddsa_key): Check whether ecc means EdDSA. + * agent/pksign.c (agent_pksign_do): Add args OVERRIDEDATA and + OVERRIDEDATALEN. + + * common/ssh-utils.c (is_eddsa): New. + (get_fingerprint): Take care or EdDSA. + +2014-03-18 Werner Koch + + tools: Fix NULL deref in gpg-connect-agent. + * tools/gpg-connect-agent.c (handle_inquire): Do not pass NULL to + strlen. + + dirmngr: Resurrect hosts in the HKP hosttable. + * dirmngr/dirmngr.c (HOUSEKEEPING_INTERVAL): New. + (housekeeping_thread): New. + (handle_tick): Call new function. + * dirmngr/ks-engine-hkp.c (RESURRECT_INTERVAL): New. + (struct hostinfo_s): Add field died_at and set it along with the dead + flag. + (ks_hkp_print_hosttable): Print that info. + (ks_hkp_housekeeping): New. + + common: New function elapsed_time_string. + * common/gettime.c (elapsed_time_string): New. + +2014-03-17 Werner Koch + + gpg: Reject signatures made with MD5. + * g10/gpg.c: Add option --allow-weak-digest-algos. + (main): Set option also in PGP2 mode. + * g10/options.h (struct opt): Add flags.allow_weak_digest_algos. + * g10/sig-check.c (do_check): Reject MD5 signatures. + * tests/openpgp/defs.inc: Add allow_weak_digest_algos to gpg.conf. + + gpg: Make --auto-key-locate work again with keyservers. + * dirmngr/ks-engine-hkp.c (ks_hkp_get): Allow exact search mode. + * g10/keyserver.c (keyserver_import_name): Implement. + (keyserver_get): Use exact mode for name based import. + (keyserver_get): Add args R_FPR and R_FPRLEN. Change all callers. + + gpg: New mechanism "clear" for --auto-key-locate. + * g10/getkey.c (parse_auto_key_locate): Implement "clear". + +2014-03-14 Werner Koch + + gpg-connect-agent: Make it easier to connect to the dirmngr. + * tools/gpg-connect-agent.c: Add options --dirmngr and + --dirmngr-program. + + dirmngr: Make use of IPv4 and IPV6 more explicit. + * common/http.c (connect_server): Handle the new flags. + * common/http.h (HTTP_FLAG_IGNORE_IPv4, HTTP_FLAG_IGNORE_IPv4): New. + * dirmngr/ks-engine-hkp.c (map_host): Add arg r_httpflags. + (make_host_part): Ditto. + (send_request): Add arg httpflags. + (ks_hkp_search, ks_hkp_get, ks_hkp_put): Handle httpflags. + + dirmngr: Do not use brackets around legacy IP addresses. + * dirmngr/ks-engine-hkp.c (my_getnameinfo): Change args to take a + complete addrinfo. Bracket only v6 addresses. Change caller. + + gpg: Print the actual used keyserver address. + * dirmngr/ks-engine-hkp.c (ks_hkp_search, ks_hkp_get): Print SOURCE + status lines. + * g10/call-dirmngr.c (ks_status_parm_s): New. + (ks_search_parm_s): Add field stparm. + (ks_status_cb): New. + (ks_search_data_cb): Send source to the data callback. + (gpg_dirmngr_ks_search): Change callback prototope to include the + SPECIAL arg. Adjust all users. Use ks_status_cb. + (gpg_dirmngr_ks_get): Add arg r_source and use ks_status_cb. + * g10/keyserver.c (search_line_handler): Adjust callback and print + "data source" disgnostic. + (keyserver_get): Print data source diagnostic. + + dirmngr: Default to a user socket name and enable autostart. + * common/homedir.c (dirmngr_socket_name): Rename to + dirmngr_sys_socket_name. + (dirmngr_user_socket_name): New. + * common/asshelp.c (start_new_dirmngr): Handle sys and user dirmngr + socket. + * dirmngr/dirmngr.c (main): Ditto. + * dirmngr/server.c (cmd_getinfo): Ditto. + * sm/server.c (gpgsm_server): Ditto. + * dirmngr/dirmngr-client.c (start_dirmngr): Likewise. + * tools/gpgconf.c (main): Print "dirmngr-sys-socket" with --list-dirs. + + * configure.ac (USE_DIRMNGR_AUTO_START): Set by default. + +2014-03-12 Werner Koch + + gpg: Add option --dirmngr-program. + * g10/gpg.c: Add option --dirmngr-program. + * g10/options.h (struct opt): Add field dirmngr_program. + * g10/call-dirmngr.c (create_context): Use new var. + + * dirmngr/dirmngr.c: Include gc-opt-flags.h. + (main): Remove GC_OPT_FLAG_*. + * tools/gpgconf-comp.c (GC_OPT_FLAG_NO_CHANGE): Move macro to ... + * common/gc-opt-flags.h: here. + + dirmngr: Detect dead keyservers and try another one. + * dirmngr/ks-action.c (ks_action_resolve): Rename var for clarity. + (ks_action_search, ks_action_put): Ditto. + (ks_action_get): Consult only the first server which retruned some + data. + + * dirmngr/ks-engine-hkp.c (SEND_REQUEST_RETRIES): New. + (map_host): Add arg CTRL and call dirmngr_tick. + (make_host_part): Add arg CTRL. + (mark_host_dead): Allow the use of an URL. + (handle_send_request_error): New. + (ks_hkp_search, ks_hkp_get, ks_hkp_put): Mark host dead and retry on + error. + + http: Add a flag to the URL parser indicating a literal v6 address. + * common/http.h (struct parsed_uri_t): Add field v6lit. + * common/http.c (do_parse_uri): Set v6lit. + +2014-03-12 NIIBE Yutaka + + scd: writekey support of ECC. + * scd/app-openpgp.c (CURVE_SEC_P256K1, get_algo_byte): New. + (store_fpr): Support ECC keys with varargs. + (get_ecc_key_parameters, get_curve_name): Support secp256k1. + (parse_ecc_curve): Likewise. + (build_ecdsa_privkey_template, rsa_writekey, ecdsa_writekey): New. + (ecdh_writekey): New. Not implemented yet. + (do_writekey): Call rsa_writekey, ecdsa_writekey, or ecdh_writekey. + (do_genkey): Follow the change of store_fpr. + +2014-03-11 Werner Koch + + dirmngr: Put brackets around IP addresses in the hosttable. + * dirmngr/ks-engine-hkp.c (EAI_OVERFLOW): Provide a substitute. + (my_getnameinfo): New. + (map_host): Use it. + + dirmngr: Add command option to mark hosts as dead or alive. + * dirmngr/server.c (cmd_killdirmngr): Factor some code out to ... + (check_owner_permission): here. + (cmd_keyserver): Add options --dead and --alive. + * dirmngr/ks-engine-hkp.c (host_in_pool_p): New. + (ks_hkp_mark_host): New. + + dirmngr: Make Assuan output of keyblocks easier readable. + * dirmngr/server.c (data_line_cookie_write): Print shorter data lines + in verbose mode. + + dirmngr: Fix HKP host selection code. + * dirmngr/server.c (cmd_keyserver): Add option --resolve and change + --print-hosttable to --hosttable. + * dirmngr/ks-action.c (ks_printf_help): New. + (ks_action_resolve): New. + * dirmngr/ks-engine-hkp.c (select_random_host): Fix selection. + (ks_hkp_print_hosttable): Print to assuan stream. + (map_host): Remove debug code. Add arg FORCE_SELECT. Return numeric + IP addr if it can't be resolved. + (make_host_part): Add arg FORCE_SELECT; change callers to pass false. + (ks_hkp_resolve): New. + + List readline support in configure summary. + * m4/readline.m4: Set gnupg_cv_have_readline. + * configure.ac: Add readline support to summary output. + +2014-03-11 NIIBE Yutaka + + agent: API change of agent_key_from_file. + * agent/findkey.c (agent_key_from_file): Always return S-expression. + * agent/command.c (cmd_passwd): Distinguish by SHADOW_INFO. + (cmd_export_key): Likewise. Free SHADOW_INFO. + (cmd_keytocard): Likewise. Release S_SKEY. + * agent/pkdecrypt.c (agent_pkdecrypt): Likewise. + * agent/pksign.c (agent_pksign_do): Likewise. Use the S-expression to + know the key type. + +2014-03-10 Werner Koch + + Backport useful code from fixes for bug 1447. + * configure.ac: Cehck for inet_ntop. + * m4/libcurl.m4: Provide a #define for the version of the curl + library. + + scd: acquire lock in new_reader_slot. + * scd/apdu.c (new_reader_slot): Acquire lock. + (open_ct_reader, open_pcsc_reader_direct, open_pcsc_reader_wrapped) + (open_ccid_reader, open_rapdu_reader): Release lock. + (lock_slot, trylock_slot, unlock_slot): Move more to the top. + + Do not require libiconv for Android. + * configure.ac (require_iconv): New. Set to false for android. + (AM_ICONV): Run only if required. + +2014-03-07 Werner Koch + + dirmmgr: Use a portability wrapper for struct timeval. + * dirmngr/dirmngr_ldap.c [W32]: Include winber.h. + (my_ldap_timeval_t): New. + + Silence more warnings about unused vars and args. + * dirmngr/cdblib.c (cdb_init) [W32]: Remove unused var. + * dirmngr/dirmngr-client.c (start_dirmngr): s/int/assuan_fd_t/. + * dirmngr/dirmngr.c (w32_service_control): Mark unused args. + (call_real_main): New. + (main) [W32]: Use new function to match prototype. + (real_main) [W32]: Mark unused vars. + (handle_signal) [W32]: Do not build the function at all. + (handle_connections) [W32]: Do not define signo. + * dirmngr/ldap-wrapper-ce.c (outstream_reader_cb): Remove used vars. + * g10/tdbio.c (ftruncate) [DOSISH]: Define only if not yet defined. + + dirmngr: Simplify strtok macro. + * dirmngr/ldap-url.c (ldap_utf8_strtok): Remove unused r3d arg. + (ldap_str2charray): Remove lasts. + + Use attribute __gnu_printf__ also in estream header files. + * common/estream-printf.h: Use attribute gnu_printf. + * common/estream.h: Ditto. + + Use attribute __gnu_printf__ with our estream-printf functions. + * common/mischelp.h (JNLIB_GCC_A_PRINTF): Use __gnu_printf__ + (JNLIB_GCC_A_NR_PRINTF): Ditto. + + w32: Silence warnings about unused vars. + * agent/gpg-agent.c (main) [W32]: Mark unused vars. + * sm/gpgsm.c (run_protect_tool) [W32]: Ditto. + * g10/trustdb.c (check_regexp) [DISABLE_REGEX]: Ditto. + * scd/scdaemon.c (main) [W32]: Ditto. + (handle_connections) [W32]: Ditto. + (handle_signal) [W32]: Do not build the function at all. + * scd/apdu.c (pcsc_send_apdu_direct): Ditto. + (connect_pcsc_card): s/long/pcsc_dword_t/. + (open_pcsc_reader_direct): Remove var listlen. + + w32: Fix a potential problem in gpgconf's gettext. + * tools/gpgconf-comp.c (my_dgettext) [USE_SIMPLE_GETTEXT]: Make sure + to return something even DOMAIN is not given. + + Silence several warnings when building under Windows. + * agent/call-scd.c (start_scd): Replace int by assuan_fd_t. + (start_pinentry): Ditto. + * common/asshelp.c (start_new_gpg_agent): Replace int by assuan_fd_t. + * common/dotlock.c (GNUPG_MAJOR_VERSION): Include stringhelp.h for + prototypes on Windows and some other platforms. + * common/logging.c (fun_writer): Declare addrbuf only if needed. + * g10/decrypt.c (decrypt_message_fd) [W32]: Return not_implemented. + * g10/encrypt.c (encrypt_crypt) [W32]: Return error if used in server + mode. + * g10/dearmor.c (dearmor_file, enarmor_file): Replace GNUPG_INVALID_FD + by -1 as temporary hack for Windows. + * g10/export.c (do_export): Ditto. + * g10/revoke.c (gen_desig_revoke, gen_revoke): Ditto. + * g10/sign.c (sign_file, clearsign_file, sign_symencrypt_file): Ditto. + * g10/server.c (cmd_verify, gpg_server) [W32]: Return an error. + + w32: Include winsock2.h to silence warnings. + + gl: Avoid warning about shadowing an arg. + * gl/setenv.c (KNOWN_VALUE): s/value/_v/. + + common: Fix build problem with Sun Studio compiler. + * common/estream.c (ESTREAM_MUTEX_UNLOCK): Use int dummy dummy + functions. + (ESTREAM_MUTEX_INITIALIZE): Ditto. + + gpg: Do not require a trustdb with --always-trust. + * g10/tdbio.c (tdbio_set_dbname): Add arg R_NOFILE. + * g10/trustdb.c (trustdb_args): Add field no_trustdb. + (init_trustdb): Set that field. + (revalidation_mark): Take care of a nonexistent trustdb file. + (read_trust_options): Ditto. + (tdb_get_ownertrust): Ditto. + (tdb_get_min_ownertrust): Ditto. + (tdb_update_ownertrust): Ditto. + (update_min_ownertrust): Ditto. + (tdb_clear_ownertrusts): Ditto. + (tdb_cache_disabled_value): Ditto. + (tdb_check_trustdb_stale): Ditto. + (tdb_get_validity_core): Ditto. + * g10/gpg.c (main): Do not create a trustdb with most commands for + trust-model always. + + gpg: Print a "not found" message for an unknown key in --key-edit. + * g10/keyedit.c (keyedit_menu): Print message. + + gpg: Protect against rogue keyservers sending secret keys. + * g10/options.h (IMPORT_NO_SECKEY): New. + * g10/keyserver.c (keyserver_spawn, keyserver_import_cert): Set new + flag. + * g10/import.c (import_secret_one): Deny import if flag is set. + + agent: Fix UPDATESTARTUPTTY for ssh. + * agent/command-ssh.c (setup_ssh_env): Fix env setting. + + gpgv: Init Libgcrypt to avoid syslog warning. + * g10/gpgv.c (main): Check libgcrypt version and disable secure + memory. + + Improve libcurl detection. + * m4/libcurl.m4: Do not use AC_PATH_PROG if --with-libcurl as been + given. Suggested by John Marshall. + + gpg: Remove legacy keyserver examples from the template conf file. + * g10/options.skel: Update. + + (cherry picked from commit f3c5cc8bcd37e38b5d65db6a50466e22d03d1f0c) + + w32: Define WINVER only if needed. + * common/sysutils.c (WINVER): Define only if less that 5.0. + + w32: Remove unused code. + * jnlib/w32-reg.c (write_w32_registry_string): Remove. + + agent: Make --allow-mark-trusted the default. + * agent/gpg-agent.c (opts, main): Add option --no-allow-mark-trusted. + Put this option into the gpgconf-list. + (main): Enable opt.allow_mark_trusted by default. + * tools/gpgconf-comp.c (gc_options_gpg_agent): Replace + allow-mark-trusted by no-allow-mark-trusted. + + * agent/trustlist.c (agent_marktrusted): Always set the "relax" flag. + + ssh: Add support for Putty. + * agent/gpg-agent.c [W32]: Include Several Windows header. + (opts): Change help text for enable-ssh-support. + (opts, main): Add option --enable-putty-support + (putty_support, PUTTY_IPC_MAGIC, PUTTY_IPC_MAXLEN): New for W32. + (agent_init_default_ctrl): Add and asssert call. + (putty_message_proc, putty_message_thread): New. + (handle_connections) [W32]: Start putty message thread. + * common/sysutils.c (w32_get_user_sid): New for W32 only + * tools/gpgconf-comp.c (gc_options_gpg_agent): Add + --enable-ssh-support and --enable-putty-support. Make the + configuration group visible at basic level. + * agent/command-ssh.c (serve_mmapped_ssh_request): New for W32 only. + + agent: Fix binary vs. text mode problem in ssh. + * agent/command-ssh.c (file_to_buffer) + (ssh_handler_request_identities): Open streams in binary mode. + (start_command_handler_ssh): Factor some code out to .. + (setup_ssh_env): new function. + + Fix syntax error for building on APPLE. + * scd/pcsc-wrapper.c [__APPLE__]: Fix syntax error. + + Ignore obsolete option --disable-keypad. + * scd/scdaemon.c (opts): Ignore --disable-keypad. + + Allow marking options as ignored. + * jnlib/argparse.h (ARGPARSE_OPT_IGNORE): New. + (ARGPARSE_TYPE_MASK): New, for internal use. + (ARGPARSE_ignore): New. + * jnlib/argparse.c (optfile_parse, arg_parse): Replace remaining + constants by macros. + (optfile_parse): Implement ARGPARSE_OPT_IGNORE. + (arg_parse): Exclide ignore options from --dump-options. + +2014-03-06 Werner Koch + + common: Fix recent commit 55656208. + * common/membuf.c (get_membuf_shrink): Fix use of LEN. + +2014-03-06 NIIBE Yutaka + + Fix g10/trust.c. + * g10/trust.c (register_trusted_keyid, register_trusted_key) + (update_ownertrust): Call functions with tdb_. + +2014-02-26 Werner Koch + + common: Replace all macros in strusage() returned strings. + * common/argparse.c (writechar): Remove. + (writestrings): Simplify. + (strusage): Use map_static_macro_string. + + common: New function map_static_macro_string. + * common/mapstrings.c: New. + * common/t-mapstrings.c: New. + * common/t-support.h (DIM, DIMof): Define if not defined. + * common/Makefile.am: Add new files. + + common: New function get_membuf_shrink. + * common/membuf.c (get_membuf_shrink): New. + + agent: Fixed unresolved symbol under Windows. + * agent/gpg-agent.c (main): s/ttyname/gnupg_ttyname/. + + common: Require an installed libiconv. + * common/utf8conv.c: Remove dynload.h. + (load_libiconv): Remove. Remove all calls to it. + +2014-02-10 Werner Koch + + gpg: Silent more compiler warnings due to some configure options. + * g10/keygen.c (generate_keypair, gen_card_key) + (gen_card_key_with_backup) [!ENABLE_CARD_SUPPORT]: Mark unused args. + + tests: Avoid segv if dns cert lookup is not configured. + * common/dns-cert.c (get_dns_cert) [!USE_DNS_CERT]: Reset return args. + + gpg: Cleanup compiler warnings due to some configure options. + * g10/photoid.c (show_photos) [DISABLE_PHOTO_VIEWER]: Mark args as + unused. + * tools/gpgconf-comp.c (my_dgettext): Mark DOMAIN as unused if NLS is + not configured. + + gpg: Allow building without any trust model support. + * configure.ac: Add option --disable-trust-models + (NO_TRUST_MODELS): New ac_define and am_conditional. + * g10/Makefile.am (trust_source): New. + (gpg2_SOURCES): Factor some files out to above. Add trust.c. + * g10/gpg.c [NO_TRUST_MODELS]: Disable options --export-ownertrust, + --import-ownertrust, --update-trustdb, --check-trustdb, --fix-trustdb, + --list-trustdb, --trustdb-name, --auto-check-trustdb, + --no-auto-check-trustdb, and --force-ownertrust. + (parse_trust_model) [NO_TRUST_MODELS]: Do not build. + (main) [NO_TRUST_MODELS]: Set trust_model to always and exclude all + trustdb related option code. + * g10/keyedit.c (cmds) [NO_TRUST_MODELS]: Remove menu items "trust", + "enable", and "disable". + * g10/keylist.c (public_key_list) [NO_TRUST_MODELS]: Do not print + "tru" record. + + * g10/trust.c: New. + * g10/trustdb.c (struct key_item): Move to trustdb.h. + (register_trusted_keyid): Rename to tdb_register_trusted_keyid. + (register_trusted_key): Rename to tdb_register_trusted_key. + (trust_letter, uid_trust_string_fixed, trust_value_to_string) + (string_to_trust_value, get_ownertrust_with_min, get_ownertrust_info) + (get_ownertrust_string, get_validity_info, get_validity_string) + (clean_sigs_from_uid, clean_uid_from_key, clean_key): Move to trust.c. + (mark_usable_uid_certs): Move to trust.c and make global. + (is_in_klist): Move as inline to trustdb.h. + (trustdb_check_or_update): Rename to tdb_check_or_update + (revalidation_mark): Rename to tdb_revalidation_mark. + (get_ownertrust): Rename to tdb_get_ownertrust. + (get_min_ownertrust): Rename to tdb_get_min_ownertrust. + (update_ownertrust): Rename to tdb_update_ownertrust. + (clear_ownertrusts): Rename to tdb_clear_ownertrusts. + (cache_disabled_value): Rename to tdb_cache_disabled_value. + (check_trustdb_stale): Rename to tdb_check_trustdb_stale. + (get_validity): Rename to tdb_get_validity_core, add arg MAIN_PK and + factor some code out to ... + * trust.c (get_validity): ...new. + (check_or_update_trustdb): New wrapper. + (revalidation_mark): New wrapper. + (get_ownertrust): New wrapper. + (get_ownertrust_with_min): New wrapper. + (update_ownertrust): New wrapper. + (clear_ownertrusts): New wrapper. + (cache_disabled_value): New wrapper. + (check_trustdb_stale): New wrapper. + + * tests/openpgp/defs.inc (opt_always): New. Use in all tests instead + of --always-trust. + + tests: Handle disabled algorithms. + * tests/openpgp/mds.test: Skip disabled algorithms. + * tests/openpgp/signencrypt-dsa.test: Ditto. + * tests/openpgp/sigs-dsa.test: Ditto. + +2014-02-07 Werner Koch + + Silence annoying ABI change warning. + * configure.ac [GCC]: Pass -Wno-psabi for gcc >= 4.6. Avoid some gcc + option tests for gcc >= 4.6 + + Allow disabling of card support. + * configure.ac: Add option --disable-card-support. Also add + am_conditional and do not build scd if card support is enabled. + + gpg: List only available algos in --gen-key. + * g10/keygen.c (ask_algo, ask_curve): Take care of GPG_USE_. + + gpg: Change --print-mds to output enabled OpenPGP algos. + * g10/gpg.c (print_mds): Use opengpg_md_test_algo. Test also for MD5 + availibility. + + gpg: Avoid compiler warnings for disabled algos. + * g10/misc.c (map_cipher_openpgp_to_gcry): Add case for disabled algo. + (openpgp_pk_test_algo2): Ditto. + (map_md_openpgp_to_gcry): Ditto. + +2014-02-05 Werner Koch + + gpg: Change format for the key size in --list-key and --edit-key. + * g10/gpg.c (oLegacyListMode, opts, main): Add --legacy-list-mode. + * g10/options.h (struct opt): Add field legacy_list_mode. + * g10/keydb.h (PUBKEY_STRING_SIZE): New. + * g10/keyid.c (pubkey_string): New. + * g10/import.c (import_one, import_secret_one): Use pubkey_string. + * g10/keylist.c (print_seckey_info): Ditto. + (print_pubkey_info, print_card_key_info): Ditto. + (list_keyblock_print): Ditto. + * g10/mainproc.c (list_node): Ditto. + * g10/pkclist.c (do_edit_ownertrust, build_pk_list): Ditto. + * g10/keyedit.c (show_key_with_all_names): Ditto. Also change the + format. + (show_basic_key_info): Ditto. + * common/openpgp-oid.c (openpgp_curve_to_oid): Also allow "ed25519". + (openpgp_oid_to_curve): Downcase "ed25519" + +2014-01-31 Werner Koch + + gpg: Add configure options to disable algorithms. + * acinclude.m4 (GNUPG_GPG_DISABLE_ALGO): New. + * configure.ac: Add --enable-gpg-* options to disable non MUS + algorithms. + * g10/misc.c (map_cipher_openpgp_to_gcry): Implement these options. + (openpgp_pk_test_algo2): Ditto. + (map_md_openpgp_to_gcry): Ditto. + (openpgp_cipher_test_algo, openpgp_md_test_algo): Simplify. + + gpg: Improve --version algo info output. + * g10/misc.c (openpgp_pk_algo_name): Return a different string for + each ECC algorithm. + * g10/gpg.c (build_list_pk_test_algo): New wrapper to cope with the + different algo type enums. + (build_list_pk_algo_name): Ditto. + (build_list_cipher_test_algo): Ditto. + (build_list_cipher_algo_name): Ditto. + (build_list_md_test_algo): Ditto. + (build_list_md_algo_name): Ditto. + (my_strusage): Use them. + (list_config): Ditto. Add "pubkeyname". + (build_list): Add letter==1 hack. + + gpg: Start using OpenPGP digest algo ids. + * g10/misc.c (print_pubkey_algo_note): Use enum typedef for the arg. + (print_cipher_algo_note): Ditto. + (print_digest_algo_note): Ditto. + (map_md_openpgp_to_gcry): New. + (openpgp_md_test_algo): Rewrite. + (openpgp_md_algo_name): Rewrite to do without Libgcrypt. + * g10/cpr.c (write_status_begin_signing): Remove hardwired list of + algo ranges. + + gpg: Use only OpenPGP cipher algo ids. + * g10/misc.c (map_cipher_openpgp_to_gcry): Use explicit mapping and + use enums for the arg and return value. + (map_cipher_gcry_to_openpgp): Ditto. + (openpgp_cipher_blocklen): Use constant macros. + (openpgp_cipher_test_algo): Use mapping function and prepare to + disable algorithms. + (openpgp_cipher_algo_name): Do not use Libgcrypt. + + * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Replace + CGRY_CIPHER_* by CIPHER_ALGO_*. + + * common/openpgpdefs.h (cipher_algo_t): Remove unused + CIPHER_ALGO_DUMMY. + +2014-01-30 Werner Koch + + gpg: Use only OpenPGP public key algo ids and add the EdDSA algo id. + * common/sexputil.c (get_pk_algo_from_canon_sexp): Change to return a + string. + * g10/keygen.c (check_keygrip): Adjust for change. + * sm/certreqgen-ui.c (check_keygrip): Likewise. + + * agent/pksign.c (do_encode_dsa): Remove bogus map_pk_openpgp_to_gcry. + + * g10/misc.c (map_pk_openpgp_to_gcry): Remove. + (openpgp_pk_test_algo): Change to a wrapper for openpgp_pk_test_algo2. + (openpgp_pk_test_algo2): Rewrite. + (openpgp_pk_algo_usage, pubkey_nbits): Add support for EdDSA. + (openpgp_pk_algo_name): Rewrite to remove need for gcry calls. + (pubkey_get_npkey, pubkey_get_nskey): Ditto. + (pubkey_get_nsig, pubkey_get_nenc): Ditto. + * g10/keygen.c(do_create_from_keygrip): Support EdDSA. + (common_gen, gen_ecc, ask_keysize, generate_keypair): Ditto. + * g10/build-packet.c (do_key): Ditto. + * g10/export.c (transfer_format_to_openpgp): Ditto. + * g10/getkey.c (cache_public_key): Ditto. + * g10/import.c (transfer_secret_keys): Ditto. + * g10/keylist.c (list_keyblock_print, list_keyblock_colon): Ditto. + * g10/mainproc.c (proc_pubkey_enc): Ditto. + * g10/parse-packet.c (parse_key): Ditto, + * g10/sign.c (hash_for, sign_file, make_keysig_packet): Ditto. + * g10/keyserver.c (print_keyrec): Use openpgp_pk_algo_name. + * g10/pkglue.c (pk_verify, pk_encrypt, pk_check_secret_key): Use only + OpenPGP algo ids and support EdDSA. + * g10/pubkey-enc.c (get_it): Use only OpenPGP algo ids. + * g10/seskey.c (encode_md_value): Ditto. + +2014-01-29 Werner Koch + + gpg: Remove cipher.h and put algo ids into a common file. + * common/openpgpdefs.h (cipher_algo_t, pubkey_algo_t, digest_algo_t) + (compress_algo_t): New. + * agent/gpg-agent.c: Remove ../g10/cipher.h. Add openpgpdefs.h. + * g10/cipher.h (DEK): Move to ... + * g10/dek.h: new file. + * g10/cipher.h (is_RSA, is_ELGAMAL, is_DSA) + (PUBKEY_MAX_NPKEY, PUBKEY_MAX_NSKEY, PUBKEY_MAX_NSIG, PUBKEY_MAX_NENC) + (PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC, PUBKEY_USAGE_CERT) + (PUBKEY_USAGE_AUTH, PUBKEY_USAGE_NONE): Move to + * g10/packet.h: here. + * g10/cipher.h: Remove. Remove from all files. + * g10/filter.h, g10/packet.h: Include dek.h. + * g10/Makefile.am (common_source): Remove cipher.h. Add dek.h. + + include: Remove this directory. + * include/cipher.h: Move to ... + * g10/cipher.h: here. + * agent/gpg-agent.c: Adjust header file name. + + * include/host2net.h: Move to ... + * common/host2net.h: here. Change license to LGPLv3/GPLv2. Adjust + notices to reflect that only me worked on that file. + + * include/types.h: Remove. + * common/types.h: Include inttypes.h. Add byte typedef and comments + for __riscos__. + * common/iobuf.h: Adjust header file name. + + * include/_regex.h: Remove this unused file. + + * include/Makefile.am: Remove. + * Makefile.am (SUBDIRS): Remove "include". + * configure.ac (AC_CONFIG_FILES): Remove include/Makefile. + * include/ChangeLog-2011: Move to ... + * common/ChangeLog-2011.include: here. + * common/Makefile.am (EXTRA_DIST): Add file. + + * include/zlib-riscos.h: Move this repo only file to ... + * g10/zlib-riscos.h: here. + + * include/: Remove. + +2014-01-17 Werner Koch + + Remove unused u64 type definitions. + * configure.ac: Remove check for uint64 and UINT64_C. + * include/types.h: Remove u64 stuff. + * common/types.h: Ditto. + +2014-01-16 NIIBE Yutaka + + agent: Not remove SSH socket when already running. + * agent/gpg-agent.c (main): Defer setting of socket_name_ssh to avoid + removal of the socket when it will die in create_server_socket for + socket_name. + + agent: Fix agent_is_eddsa_key. + * agent/findkey.c (agent_is_eddsa_key): Implemented. + +2014-01-15 NIIBE Yutaka + + Add secp256k1. + * common/openpgp-oid.c (openpgp_curve_to_oid): Add secp256k1. + (openpgp_oid_to_curve): Likewise. + + * g10/keygen.c (ask_curve): Add secp256k1. + +2014-01-10 Werner Koch + + autogen.sh: Add rule for 64 bit windows. + + Turn autogen.sh into a generic script. + * autogen.sh: Revamp. + * autogen.rc: New. + * Makefile.am (EXTRA_DIST): Add autogen.rc. + + Rename scripts/ to build-aux/ + * scripts/: Rename to build-aux/ + * Makefile.am: Adjust accordingly. + * configure.ac (AC_CONFIG_AUX_DIR): Change to build-aux. + +2014-01-09 Werner Koch + + Improve the speedo make script. + * scripts/gpg-w32-dev/README: Remove + * scripts/gpg-w32-dev/GNUmakefile, scripts/gpg-w32-dev/speedo.mk: + Merge into .. + * scripts/speedo.mk: this. + + gpgsplit: Allow building without zlib support. + * tools/gpgsplit.c [!HAVE_ZLIB]: Do not include zlib.h. + (handle_zlib): Build only if HAVE_ZLIB is defined. + (write_part): Support zlib and zip only if HAVE_ZLIB is defined. + + w32: Fix backslash quoting in registry name. + * configure.ac (GNUPG_REGISTRY_DIR): Double backslashes. + + Fix test for zlib. + * configure.ac (HAVE_ZLIB): Define only if found. + + Add --enable-silent-rules stuff. + * configure.ac: Add AM_SILENT_RULES. + +2014-01-08 Werner Koch + + w32: Add macro for the registry key. + * configure.ac (GNUPG_REGISTRY_DIR) [W32]: New ac-define. + * common/homedir.c (default_homedir): Use it. + * common/logging.c (do_logv): Use it. + +2013-12-11 Werner Koch + + gpg: Change --show-session-key to print the session key earlier. + * g10/cpr.c (write_status_strings): New. + (write_status_text): Replace code by a call to write_status_strings. + * g10/mainproc.c (proc_encrypted): Remove show_session_key code. + * g10/decrypt-data.c (decrypt_data): Add new show_session_key code. + +2013-12-05 Werner Koch + + gpg: Change OID of Ed25519 and add Brainpool oids. + * common/openpgp-oid.c (openpgp_curve_to_oid): Change OID for + Ed25519. Add brainpool OIDs. + (openpgp_oid_to_curve): Ditto. + +2013-11-29 Werner Koch + + common: Add put_membuf_printf. + * common/membuf.c (put_membuf_printf): New. + +2013-11-27 Werner Koch + + gpg: Change armor Version header to emit only the major version. + * g10/options.h (opt): Rename field no_version to emit_version. + * g10/gpg.c (main): Init opt.emit_vesion to 1. Change --emit-version + to bump up opt.emit_version. + * g10/armor.c (armor_filter): Implement different --emit-version + values. + +2013-11-18 Werner Koch + + Make use of the *_NAME etc macros. + Replace hardwired strings at many places with new macros from config.h + and use the new strusage macro replacement feature. + + * common/asshelp.c (lock_spawning) [W32]: Change the names of the spawn + sentinels. + * agent/command.c (cmd_import_key): Use asprintf to create the prompt. + + Add strusage macro replacement feature. + * common/argparse.c (writechar): New. + (writestrings): Add macro replacement feature. + (show_help): Remove specialized @EMAIL@ replacement. + * configure.ac (GNUPG_NAME, GPG_NAME, GPGSM_NAME): Define. + (GPG_AGENT_NAME, DIRMNGR_NAME, G13_NAME, GPGCONF_NAME): Define. + (GPGTAR_NAME, GPG_AGENT_INFO_NAME, GPG_AGENT_SOCK_NAME): Define. + (GPG_AGENT_SSH_SOCK_NAME, DIRMNGR_INFO_NAME): Define. + (DIRMNGR_SOCK_NAME): Define. + +2013-11-15 Werner Koch + + kbx: Implement update operation for OpenPGP keyblocks. + * kbx/keybox-update.c (keybox_update_keyblock): Implement. + * kbx/keybox-search.c (get_blob_flags): Move to ... + * kbx/keybox-defs.h (blob_get_type): here. + * kbx/keybox-file.c (_keybox_read_blob2): Fix calling without R_BLOB. + * g10/keydb.c (build_keyblock_image): Allow calling without + R_SIGSTATUS. + (keydb_update_keyblock): Implement for keybox. + + * kbx/keybox-dump.c (_keybox_dump_blob): Fix printing of the unhashed + size. Print "does not expire" also on 64 bit platforms. + + gpg: Rework ECC support and add experimental support for Ed25519. + * agent/findkey.c (key_parms_from_sexp): Add algo name "ecc". + (agent_is_dsa_key): Ditto. + (agent_is_eddsa_key): New. Not finished, though. + * agent/pksign.c (do_encode_eddsa): New. + (agent_pksign_do): Use gcry_log_debug functions. + * agent/protect.c (agent_protect): Parse a flags parameter. + * g10/keygen.c (gpg_curve_to_oid): Move to ... + * common/openpgp-oid.c (openpgp_curve_to_oid): here and rename. + (oid_ed25519): New. + (openpgp_oid_is_ed25519): New. + (openpgp_oid_to_curve): New. + * common/t-openpgp-oid.c (test_openpgp_oid_is_ed25519): New. + * g10/build-packet.c (gpg_mpi_write): Write the length header also for + opaque MPIs. + (gpg_mpi_write_nohdr): New. + (do_key): Use gpg_mpi_write_nohdr depending on algorithm. + (do_pubkey_enc): Ditto. + * g10/ecdh.c (pk_ecdh_encrypt_with_shared_point): Use + gpg_mpi_write_nohdr. + * g10/export.c (transfer_format_to_openpgp): + * g10/keygen.c (ecckey_from_sexp): Return the error. + (gen_ecc): Repalce arg NBITS by CURVE. + (read_parameter_file): Add keywords "Key-Curve" and "Subkey-Curve". + (ask_curve): New. + (generate_keypair, generate_subkeypair): Use ask_curve. + (do_generate_keypair): Also pass curve name. + * g10/keylist.c (list_keyblock_print, list_keyblock_colon): Print + curve name. + * g10/parse-packet.c (mpi_read): Remove workaround for + Libcgrypt < 1.5. + (parse_key): Fix ECC case. Print the curve name. + * g10/pkglue.c (mpi_from_sexp): Rename to get_mpi_from_sexp. + (pk_verify, pk_check_secret_key): Add special case for Ed25519. + * g10/seskey.c (encode_md_value): Ditto. + * g10/sign.c (do_sign, hash_for, sign_file): Ditto. + + kbx: Fix possible segv in kbxdump. + * kbx/keybox-dump.c (_keybox_dump_blob): Check length before get32. + + Require Libgcrypt 1.6. + * agent/pksign.c (do_encode_dsa): Remove Libgcrypt version check + +2013-11-13 NIIBE Yutaka + + scd: more pinpad input fix for PC/SC. + * scd/apdu.c (check_pcsc_pinpad): Set default values here. + (pcsc_pinpad_verify, pcsc_pinpad_modify): Remove setting default + values, as it's too late. + +2013-11-11 NIIBE Yutaka + + scd: more pinpad fix. + * scd/apdu.c (check_pcsc_pinpad): Set ->minlen and ->maxlen only when + those are specified. + (pcsc_pinpad_modify): Remove old check code. + +2013-10-29 NIIBE Yutaka + + scd: pinpad fix for PC/SC on Windows. + * scd/apdu.c (SCARD_CTL_CODE): Fix for Windows. + +2013-10-28 Werner Koch + + doc: Change yat2m to allow arbitrary condition names. + * doc/yat2m.c (MAX_CONDITION_NESTING): New. + (gpgone_defined): Remove. + (condition_s, condition_stack, condition_stack_idx): New. + (cond_is_active, cond_in_verbatim): New. + (add_predefined_macro, set_macro, macro_set_p): New. + (evaluate_conditions, push_condition, pop_condition): New. + (parse_file): Rewrite to use the condition stack. + (top_parse_file): Set prefined macros. + (main): Change -D to define arbitrary macros. + +2013-10-25 NIIBE Yutaka + + scd: fix pinpad input on Windows. + * scd/apdu.c (open_pcsc_reader_direct): Don't call + pcsc_vendor_specific_init here, but... + (connect_pcsc_card): Call it here. + +2013-10-23 NIIBE Yutaka + + po: Update Japanese translation. + +2013-10-16 NIIBE Yutaka + + scd: add pinpad readers information for PC/SC service. + * scd/apdu.c (pcsc_vendor_specific_init): Add information for Cherry + ST-2xxx, Reiner cyberJack, Vasco DIGIPASS, FSIJ Gnuk Token, and KAAN + Advance. + +2013-10-15 NIIBE Yutaka + + scd: remove pin length check. + * scd/apdu.c (pcsc_pinpad_verify): Remove old check code for pin + length. + +2013-10-04 Werner Koch + + gpg: Limit the nesting level of I/O filters. + * common/iobuf.c (MAX_NESTING_FILTER): New. + (iobuf_push_filter2): Limit the nesting level. + + * g10/mainproc.c (mainproc_context): New field ANY. Change HAVE_DATA + and ANY_SIG_SIGN to bit fields of ANY. Add bit field + UNCOMPRESS_FAILED. + (proc_compressed): Avoid printing multiple Bad Data messages. + (check_nesting): Return GPG_ERR_BAD_DATA instead of UNEXPECTED_DATA. + +2013-10-02 Werner Koch + + gpg: Fix bug with deeply nested compressed packets. + * g10/mainproc.c (MAX_NESTING_DEPTH): New. + (proc_compressed): Return an error code. + (check_nesting): New. + (do_proc_packets): Check packet nesting depth. Handle errors from + check_compressed. + +2013-09-08 Werner Koch + + Switch to deterministic DSA. + * agent/pksign.c (rfc6979_hash_algo_string): New. + (do_encode_dsa) [Libgcrypt >= 1.6]: Make use of RFC-6979. + +2013-08-30 Werner Koch + + scd: Suppress gcc warning about possible uninitialized use. + * scd/app-nks.c (parse_pwidstr): Always init r_pwid. + + gpg: Use 2048 as the default keysize in batch mode. + * g10/keygen.c (gen_elg, gen_dsa, gen_rsa): Set default keysize to + 2048. + + gpgtar: Fix building for systems with a separate libintl. + * tools/Makefile.am (gpgtar_LDADD): Add LIBINTL and LIBICONV. + + scd: Use vendor and product id macros also in apdu.c. + * scd/ccid-driver.c: Move vendor and product ids to ... + * scd/ccid-driver.h: here. + * scd/apdu.c (CCID_DRIVER_INCLUDE_USB_IDS): Define to include ids. + (pcsc_vendor_specific_init): Use vendor and product id macros. + +2013-08-30 NIIBE Yutaka + + scd: PC/SC pinpad input improvement. + * scd/apdu.c (struct reader_table_s): Add members: PINMIN, PINMAX, and + PINPAD_VERLEN_SUPPORTED. + (CM_IOCTL_VENDOR_IFD_EXCHANGE, FEATURE_GET_TLV_PROPERTIES, + PCSCv2_PART10_PROPERTY_*): New. + (new_reader_slot): Initialize pinpad_varlen_supported, pinmin, pinmax. + (pcsc_vendor_specific_init): New. + (open_pcsc_reader_direct, open_pcsc_reader_wrapped): Call + pcsc_vendor_specific_init. + (check_pcsc_pinpad): Not detect here but use the result of + pcsc_vendor_specific_init. + (pcsc_pinpad_verify, pcsc_pinpad_modify): Specify bNumberMessage. + +2013-08-29 Jonas Borgström + + scd: add support for RSA_CRT and RSA_CRT_N key import. + * scd/app-openpgp.c (do_writekey): Added RSA_CRT and RSA_CRT_N support. + +2013-08-29 Werner Koch + + kbx: Add a few macros for easier readability. + * kbx/keybox-update.c (FILECOPY_INSERT) + (FILECOPY_DELETE, FILECOPY_UPDATE): New macros. Replace numbers by + them. + +2013-08-28 Werner Koch + + Fix commit 04e2c83f. + * agent/command-ssh.c (stream_read_string): Do not assign to a NULL + ptr. + + gpg: Make decryption with the OpenPGP card work. + * scd/app-common.h (APP_DECIPHER_INFO_NOPAD): New. + * scd/app-openpgp.c (do_decipher): Add arg R_INFO. + * scd/app-nks.c (do_decipher): Add arg R_INFO as a dummy. + * scd/app.c (app_decipher): Add arg R_INFO. + * scd/command.c (cmd_pkdecrypt): Print status line "PADDING". + * agent/call-scd.c (padding_info_cb): New. + (agent_card_pkdecrypt): Add arg R_PADDING. + * agent/divert-scd.c (divert_pkdecrypt): Ditto. + * agent/pkdecrypt.c (agent_pkdecrypt): Ditto. + * agent/command.c (cmd_pkdecrypt): Print status line "PADDING". + * g10/call-agent.c (padding_info_cb): New. + (agent_pkdecrypt): Add arg R_PADDING. + * g10/pubkey-enc.c (get_it): Use padding info. + + agent: Fix two compiler warnings. + * agent/command.c (cmd_preset_passphrase, pinentry_loopback): Use %zu + in format string. + * scd/ccid-driver.c (ccid_get_atr): Ditto. + * agent/command-ssh.c (stream_read_string): Init arg STRING_SIZE to + avoid maybe_unitialized warning. + +2013-08-27 NIIBE Yutaka + + scd: fix parsing login-data DO. + * scd/app-openpgp.c (parse_login_data): Release RELPTR. Fix parsing. + + scd: fix Vega for Alpha reader. + * scd/ccid-driver.c (ccid_vendor_specific_init): Fix error handling + and size of command. + +2013-08-21 Werner Koch + + scd: Make SPRx32 pinpad work with PC/SC on Windows. + * scd/apdu.c (CM_IOCTL_GET_FEATURE_REQUEST): Use SCARD_CTL_CODE. + (SCARD_CTL_CODE): Define if not defined. + (reader_table_s): Add is_spr532. + (new_reader_slot): Clear it. + (check_pcsc_pinpad): Set it. + (pcsc_pinpad_verify, pcsc_pinpad_modify): Add fix for SPR532. + + scd: Improve --enable-pinpad-varlen. + * tools/gpgconf-comp.c (gc_options_scdaemon): Add + enable-pinpad-varlen. + * scd/apdu.c (check_pcsc_pinpad): Detect SPRx32 reader. + +2013-08-08 Werner Koch + + agent: Extend cmd KEYINFO to return data from sshcontrol. + * agent/command-ssh.c (struct control_file_s): Rename to + ssh_control_file_s. + (ssh_open_control_file, ssh_close_control_file) + (ssh_read_control_file, ssh_search_control_file): New. + (control_file_t): Rename and move to ... + * agent/agent.h (ssh_control_file_t): here. + * agent/command.c (do_one_keyinfo): Add args is_ssh, ttl, disabled, + and confirm. Rename unknown keytype indicator from '-' to 'X'. Extend + output. + (cmd_keyinfo): Add options --ssh-list and --with-ssh. + +2013-08-02 Werner Koch + + gpg: No need to create a trustdb when encrypting with --always-trust. + * g10/gpg.c (main): Special case setup_trustdb for --encrypt. + +2013-08-01 Werner Koch + + w32: Fix recent patch 9ff72e4. + * common/homedir.c (check_portable_app): Fix the name of the control + file. + + agent: Include missing prototype. + * agent/protect.c: Include cvt-openpgp.h. + + w32: Add code to support a portable use of GnuPG. + * common/homedir.c (w32_bin_is_bin, w32_portable_app) [W32]: New. + (check_portable_app) [W32]: New. + (standard_homedir, default_homedir) [W32]: Support the portable flag. + (w32_rootdir, w32_commondir) [W32]: Ditto. + (gnupg_bindir, gnupg_cachedir, dirmngr_socket_name) [W32]: Ditto. + * common/logging.h (JNLIB_LOG_NO_REGISTRY): New. + * common/logging.c (no_registry): New variable. + (log_set_prefix, log_get_prefix): Set/get that variable. + (do_logv): Do not check the registry if that variable is set. + + Silence compiler warning about deprecated Libgcrypt symbols. + * configure.ac (AH_BOTTOM): Define GCRYPT_NO_DEPRECATED. + + dirmngr: Define missing LDAP constant. + * dirmngr/ldap-url.c (LDAP_SCOPE_DEFAULT): Define if missing. + + scd: Fix a syntax error for Apple and Windows. + * scd/apdu.c (pcsc_dword_t) [W32]: Fix syntax error. + + common: Fix a build error when using adns. + * common/dns-cert.c (get_dns_cert) [USE_ADNS]: Fix synatx error. + +2013-07-31 Werner Koch + + common: Comment out unused code. + * common/w32-reg.c (write_w32_registry_string): Comment out. + + dirmngr: Remove unused file. + * dirmngr/get-path.c: Remove. + +2013-06-27 Werner Koch + + sm: Remove cruft from source files. + * sm/keydb.c, sm/keydb.h: Remove disabled code parts. + + Prepare for newer automake versions. + * configure.ac (AM_INIT_AUTOMAKE): Replace 2 argument form by the + option form. Add options from the top Makefile. + (AM_CONFIG_HEADER): Rename to AC_CONFIG_HEADER. + * Makefile.am (AUTOMAKE_OPTIONS): Remove. + + * kbx/Makefile.am: Remove INCLUDES. Include cmacros.am. FActor some + AM_CPPFLAGS options to AM_CFLAGS. + +2013-06-26 Werner Koch + + Fix Makefile regression. + * agent/Makefile.am (gpg_agent_DEPENDENCIES): Remove cruft from wrong + resolve conflict 2013-04-25. + (gpg_agent_DEPENDENCIES): Remove obsolete gpg_agent_res_deps + (gpg_agent_LDFLAGS): Remove obsolete gpg_agent_res_ldflags. + +2013-05-22 Werner Koch + + Implement unattended OpenPGP secret key import. + * agent/command.c (cmd_import_key): Add option --unattended. + * agent/cvt-openpgp.c (convert_transfer_key): New. + (do_unprotect): Factor some code out to ... + (prepare_unprotect): new function. + (convert_from_openpgp): Factor all code out to ... + (convert_from_openpgp_main): this. Add arg 'passphrase'. Implement + openpgp-native protection modes. + (convert_from_openpgp_native): New. + * agent/t-protect.c (convert_from_openpgp_native): New dummy fucntion + * agent/protect-tool.c (convert_from_openpgp_native): Ditto. + * agent/protect.c (agent_unprotect): Add arg CTRL. Adjust all + callers. Support openpgp-native protection. + * g10/call-agent.c (agent_import_key): Add arg 'unattended'. + * g10/import.c (transfer_secret_keys): Use unattended in batch mode. + + New debug functions log_printcanon and log_printsexp. + * common/sexputil.c (sexp_to_string, canon_sexp_to_string): New. + (log_printcanon, log_printsexp): New. + + agent: Fix length detection of canonical formatted openpgp keys. + * agent/command.c (cmd_import_key): Pass 0 instead of KEYLEN to + gcry_sexp_canon_len. + + agent: New option --disable-check-own-socket. + * agent/gpg-agent.c (oDisableCheckOwnSocket): New. + (disable_check_own_socket): New. + (parse_rereadable_options): Set new option. + (check_own_socket): Implement new option. + +2013-05-07 Werner Koch + + w32: Add icons and version information. + * common/gnupg.ico: New. Take from artwork/gnupg-favicon-1.ico. + * agent/gpg-agent-w32info.rc: New. + * g10/gpg-w32info.rc: New. + * scd/scdaemon-w32info.rc: New. + * sm/gpgsm-w32info.rc: New. + * tools/gpg-connect-agent-w32info.rc: New. + * common/w32info-rc.h.in: New. + * configure.ac (BUILD_REVISION, BUILD_FILEVERSION, BUILD_TIMESTAMP) + (BUILD_HOSTNAME): New. + (AC_CONFIG_FILES): Add w32info-rc.h. + * am/cmacros.am (.rc.o): New rule. + * agent/Makefile.am, common/Makefile.am, g10/Makefile.am + * scd/Makefile.am, sm/Makefile.am, tools/Makefile.am: Add stuff to + build resource files. + +2013-05-07 Ian Abbott + + doc: fix some Texinfo warnings. + * doc/gpg.texi: Fix syntax and add missing menu entries. + * doc/gpgsm.texi: Fix subsectioning. + +2013-04-22 Werner Koch + + Fix potential heap corruption in "gpg -v --version". + * g10/gpg.c (build_list): Rewrite to cope with buffer overflow in + certain locales. + +2013-04-19 Werner Koch + + gpgsm: Remove non-implemented commands from --help. + * sm/gpgsm.c (opts): Removed commands --clearsign, --symmetric, + --send-keys, and --recv-keys. + +2013-04-19 Daiki Ueno + + Make sure to call fflush if estream_t is backed with stdio. + * common/estream.c (es_func_fp_write): Call fflush after fwrite. + +2013-04-19 Werner Koch + + doc: Formatting fixes. + * doc/Makefile.am (.fig.jpg): Correct to use -L jpeg. + * doc/gpg.texi: Fix cross reference for --options. + * doc/gpgsm.texi: Likewise. + * doc/gpl.texi: Fix enumerate and re-indent examples. + +2013-04-01 NIIBE Yutaka + + scd: move SCDaemon to libexecdir. + * common/homedir.c (gnupg_module_name): It's now libexecdir. + * scd/Makefile.am (libexec_PROGRAMS): Add scdaemon + (bin_PROGRAMS): Remove scdaemon. + +2013-03-29 Werner Koch + + copyright assignments are not anymore required. + +2013-03-26 NIIBE Yutaka + + scd: PC/SC status fix. + * scd/apdu.c (pcsc_get_status_direct): Check PCSC_STATE_MUTE only when + PCSC_STATE_PRESENT. + + * scd/pcsc-wrapper.c (handle_status): Ditto. + + scd: PC/SC cleanup (more). + * scd/apdu.c (control_pcsc_direct, control_pcsc_wrapped, control_pcsc) + (check_pcsc_pinpad, pcsc_pinpad_verify, pcsc_pinpad_modify): Use + pcsc_dword_t. + + scd: call update_card_removed only when detecting removal. + * scd/command.c (update_reader_status_file): Add condition + vr->status == 0. + +2013-03-22 NIIBE Yutaka + + scd: PC/SC cleanup. + * scd/apdu.c (pcsc_dword_t): New. It was named as DWORD (double-word) + when a word was 16-bit. + (struct reader_table_s): Fixes for types. + (struct pcsc_readerstate_s) [__APPLE__]: Enable #pragma pack(1). + Throughout: Fixes for types. + + * scd/pcsc-wrapper.c: Likewise. + +2013-03-21 NIIBE Yutaka + + po: Enable ja.po. + * po/LINGUAS: Enable ja.po. + + scd: change default value of pinpad maxlen. + * scd/apdu.c (pcsc_pinpad_verify, pcsc_pinpad_modify): Default value + of maxlen for pinpad input is now 15 (was: 25). + + * scd/ccid-driver.c (ccid_transceive_secure): Likewise. + +2013-03-20 Werner Koch + + Add code to allow for late memory cleanup. + * common/init.c (mem_cleanup_item_t): New. + (run_mem_cleanup): New. + (_init_common_subsystems): Add an atexit for it. + (register_mem_cleanup_func): New. + + * g10/kbnode.c (cleanup_registered): New. + (release_unused_nodes): New. + (alloc_node): Call register_mem_cleanup_func. + + kbx: Remove unused macro. + * kbx/keybox.h (KEYBOX_WITH_OPENPGP): Remove unused macro. + +2013-03-19 Werner Koch + + gpg: Print indicator for unknown key capability. + * g10/keylist.c (print_capabilities): Print '?' for unknown usage. + +2013-03-19 Daniel Kahn Gillmor + + gpg: Allow setting of all zero key flags. + * g10/keygen.c (do_add_key_flags): Do not check for empty key flags. + +2013-03-19 Werner Koch + + gpg: Distinguish between missing and cleared key flags. + * include/cipher.h (PUBKEY_USAGE_NONE): New. + * g10/getkey.c (parse_key_usage): Set new flag. + +2013-03-15 NIIBE Yutaka + + scd: ccid-driver supporting larger APDU. + * scd/ccid-driver.c (ccid_transceive_apdu_level): Support larger + APDU. + + scd: fix missing close paren. + * scd/app-openpgp.c (du_auth): Fix. + +2013-03-09 NIIBE Yutaka + + scd: support ECDSA signing. + * scd/app-openpgp.c (do_sign): Only prepend message digest block + for RSA or do_auth. + (do_auth): Remove message digest block for ECDSA. + +2013-03-08 NIIBE Yutaka + + scd: support ECDSA public key. + * scd/app-openpgp.c (key_type_t): New. + (CURVE_NIST_P256, CURVE_NIST_P384, CURVE_NIST_P521): New. + (struct app_local_s): Change keyattr to have key_type and union. + (get_ecc_key_parameters, get_curve_name): New. + (send_key_attr, get_public_key): Support ECDSA. + (build_privkey_template, do_writekey, do_genkey): Follow the change + of the member KEY_ATTR. + (parse_historical): New. + (parse_algorithm_attribute): Support ECDSA. + +2013-03-05 Werner Koch + + Require libgpg-error 1.11. + * configure.ac: Require libgpg-error 1.11. + * common/util.h (GPG_ERR_NO_KEYSERVER, GPG_ERR_INV_CURVE) + (GPG_ERR_UNKNOWN_CURVE): Remove fallback definitions. + +2013-02-28 NIIBE Yutaka + + agent: pksign result conversion to sexp to upper layer. + * agent/agent.h (divert_pksign): Add R_SIGLEN argument. + * agent/divert-scd.c (divert_pksign): Return length at R_SIGLEN. + * agent/call-scd.c (agent_card_pksign): Move composition of + S-expression to... + * agent/pksign.c (agent_pksign_do): ... here. + +2013-02-22 Werner Koch + + Use has_leading_keyword in the assuan callbacks. + * agent/call-pinentry.c (inq_quality): Use has_leading_keyword. + * agent/call-scd.c (inq_needpin, inq_writekey_parms): Ditto. + * g10/call-agent.c (inq_writecert_parms, keyinfo_status_cb): Ditto. + (inq_genkey_parms, inq_ciphertext_cb, inq_import_key_parms): Ditto. + * g10/call-dirmngr.c (ks_put_inq_cb): Ditto. + * sm/call-agent.c (default_inq_cb, inq_ciphertext_cb): Ditto. + (inq_genkey_parms, istrusted_status_cb, learn_status_cb): Ditto. + (keyinfo_status_cb, inq_import_key_parms): Ditto. + * sm/call-dirmngr.c (inq_certificate, isvalid_status_cb): Ditto. + (lookup_status_cb, run_command_inq_cb, run_command_status_cb): Ditto. + + Remove some unused variables. + * tools/gpgconf-comp.c (gc_process_gpgconf_conf): Remove unused + used_components. + * agent/command-ssh.c (ssh_signature_encoder_ecdsa): Mark unused arg. + * g13/g13.c (main): Comment variable of yet unimplemented options. + + gpg: Fix a memory leak in batch key generation. + * g10/keygen.c (append_to_parameter): New. + (proc_parameter_file): Use new func to extend the parameter list. + + * g10/passphrase.c (passphrase_to_dek_ext): Print a diagnostic of + gcry_kdf_derive failed. + * g10/keygen.c (proc_parameter_file): Print a diagnostic if + passphrase_to_dek failed. + + gpg: Handle the agent's NEW_PASSPHRASE inquiry. + * g10/call-agent.c (default_inq_cb): Take care of NEW_PASSPHRASE. + + common: Add func has_leading_keyword. + * common/stringhelp.c (has_leading_keyword): New. + + Remove build hacks for FreeBSD. + * configure.ac [freebsd]: Do not add /usr/local to CPPFLAGS and + LDFLAGS. + +2013-02-22 NIIBE Yutaka + + agent: fix two bugs. + * agent/command.c (cmd_keytocard): Decrement KEYDATALEN. + * agent/findkey.c (agent_public_key_from_file): Increment for ELEMS. + + gpg: fix keytocard and support ECC card for key attribute. + * g10/call-agent.c (agent_keytocard): Supply PARM arg. + * g10/card-util.c (card_status): Support ECC. + (card_store_subkey): Don't assume RSA. + +2013-02-21 Werner Koch + + gpg: Fix a memory leak in batch key generation. + * g10/keygen.c (append_to_parameter): New. + (proc_parameter_file): Use new func to extend the parameter list. + + * g10/passphrase.c (passphrase_to_dek_ext): Print a diagnostic of + gcry_kdf_derive failed. + * g10/keygen.c (proc_parameter_file): Print a diagnostic if + passphrase_to_dek failed. + + gpg: Handle the agent's NEW_PASSPHRASE inquiry. + * g10/call-agent.c (default_inq_cb): Take care of NEW_PASSPHRASE. + + common: Add func has_leading_keyword. + * common/stringhelp.c (has_leading_keyword): New. + +2013-02-20 Werner Koch + + Remove build hacks for FreeBSD. + * configure.ac [freebsd]: Do not add /usr/local to CPPFLAGS and + LDFLAGS. + +2013-02-12 NIIBE Yutaka + + gpg: Implement card_store_subkey again. + * g10/call-agent.h (agent_keytocard): New. + * g10/call-agent.c (agent_keytocard): New. + * g10/card-util.c (replace_existing_key_p): Returns 1 when replace. + (card_generate_subkey): Check return value of replace_existing_key_p. + (card_store_subkey): Implement again using agent_keytocard. + + agent: Add KEYTOCARD command. + * agent/agent.h (divert_writekey, agent_card_writekey): New. + * agent/call-scd.c (inq_writekey_parms, agent_card_writekey): New. + * agent/command.c (cmd_keytocard, hlp_keytocard): New. + (register_commands): Add cmd_keytocard. + * agent/divert-scd.c (divert_writekey): New. + + Japanese: update po and doc. + * doc/help.ja.txt, po/ja.po: Updated. + +2013-02-08 NIIBE Yutaka + + scd: Rename 'keypad' to 'pinpad'. + * NEWS: Mention scd changes. + + * agent/divert-scd.c (getpin_cb): Change message. + + * agent/call-scd.c (inq_needpin): Change the protocol to + POPUPPINPADPROMPT and DISMISSPINPADPROMPT. + * scd/command.c (pin_cb): Likewise. + + * scd/apdu.c (struct reader_table_s): Rename member functions. + (check_pcsc_pinpad, pcsc_pinpad_verify, pcsc_pinpad_modify, + check_ccid_pinpad, ccid_pinpad_operation, apdu_check_pinpad + apdu_pinpad_verify, apdu_pinpad_modify): Rename. + + * scd/apdu.h (SW_HOST_NO_PINPAD, apdu_check_pinpad) + (apdu_pinpad_verify, apdu_pinpad_modify): Rename. + + * scd/iso7816.h (iso7816_check_pinpad): Rename. + + * scd/iso7816.c (map_sw): Use SW_HOST_NO_PINPAD. + (iso7816_check_pinpad): Rename. + (iso7816_verify_kp, iso7816_change_reference_data_kp): Follow + the change. + + * scd/ccid-driver.h (CCID_DRIVER_ERR_NO_PINPAD): Rename. + * scd/ccid-driver.c (ccid_transceive_secure): Use it. + + * scd/app-dinsig.c (verify_pin): Follow the change. + * scd/app-nks.c (verify_pin): Follow the change. + + * scd/app-openpgp.c (check_pinpad_request): Rename. + (parse_login_data, verify_a_chv, verify_chv3, do_change_pin): Follow + the change. + + * scd/scdaemon.c (oDisablePinpad, oEnablePinpadVarlen): Rename. + + * scd/scdaemon.h (opt): Rename to disable_pinpad, + enable_pinpad_varlen. + + * tools/gpgconf-comp.c (gc_options_scdaemon): Rename to + disable-pinpad. + +2013-02-07 Werner Koch + + gpg: Add pinentry-mode feature. + * g10/gpg.c: Include shareddefs.h. + (main): Add option --pinentry-mode. + * g10/options.h (struct opt): Add field pinentry_mode. + * g10/passphrase.c: Include shareddefs.h. + (have_static_passphrase): Take care of loopback pinentry_mode. + (read_passphrase_from_fd): Ditto. + (get_static_passphrase): New. + (passphrase_to_dek_ext): Factor some code out to ... + (emit_status_need_passphrase): new. + * g10/call-agent.c (start_agent): Send the pinentry mode. + (default_inq_cb): Take care of the PASSPHRASE inquiry. Return a + proper error code. + (agent_pksign): Add args keyid, mainkeyid and pubkey_algo. + (agent_pkdecrypt): Ditto. + * g10/pubkey-enc.c (get_it): Pass new args. + * g10/sign.c (do_sign): Pass new args. + + * g10/call-agent.c (struct default_inq_parm_s): New. Change all + similar structs to reference this one. Change all users and inquire + callback to use this struct, instead of NULL or some undefined but not + used structs. This change will help to eventually get rid of global + variables. + +2013-02-06 Werner Koch + + agent: Move a typedef to common and provide parse_pinentry_mode. + * common/agent-opt.c: New. + * common/shareddefs.h: New. + * common/Makefile.am: Add new files. + * agent/agent.h: Include shareddefs.h. + (pinentry_mode_t): Factor out to shareddefs.h. + * agent/command.c (option_handler): Use parse_pinentry_mode. + + agent: Return a better error code if no passphrase was given. + * agent/protect.c (hash_passphrase): Handle an empty passphrase. + +2013-02-05 NIIBE Yutaka + + scd: Fix check_keypad_request. + * scd/app-openpgp.c (check_keypad_request): 0 means not to use pinpad. + + SCD: Add vendor specific initalization. + * scd/ccid-driver.c (ccid_vendor_specific_init): New. + (ccid_open_reader): Call ccid_vendor_specific_init. + + SCD: Support P=N format for login data. + * scd/app-openpgp.c (parse_login_data): Support P=N format. + + SCD: Better interoperability. + * scd/apdu.c: Fill bTeoPrologue[2] field. + + SCD: Defaults to use pinpad if the reader has the capability. + * scd/app-openpgp.c (struct app_local_s): Remove VARLEN. + (parse_login_data): "P=0" means to disable pinpad. + (check_keypad_request): Default is to use pinpad if available. + + SCD: handle keypad request on the card. + * scd/app-openpgp.c: Add 2013. + (struct app_local_s): Add keypad structure. + (parse_login_data): Add parsing keypad request on the card. + (check_keypad_request): New. + (verify_a_chv, verify_chv3, do_change_pin): Call check_keypad_request + to determine use of keypad. + + SCD: Minor fix of ccid-driver. + * scd/ccid-driver.c (VENDOR_VEGA): Fix typo. + + SCD: Add support of Covadis VEGA_ALPHA reader. + * scd/ccid-driver.c: Add 2013. + (VENDER_VEGA, VEGA_ALPHA):New. + (ccid_transceive_secure): VEGA_ALPHA is same firmware as GEMPC_PINPAD. + Change bNumberMessage to 0x01, as it works better (was: 0xff). + + SCD: Support fixed length PIN input for keypad (PC/SC). + * scd/apdu.c (pcsc_keypad_verify): SUpport fixed length PIN input for + keypad. + (pcsc_keypad_modify): Likewise. + * scd/ccid-driver.c (ccid_transceive_secure): Clean up. + + SCD: Support fixed length PIN input for keypad. + * scd/iso7816.h (struct pininfo_s): Remove MODE and add FIXEDLEN. + * scd/app-dinsig.c (verify_pin): Initialize FIXEDLEN to unknown. + * scd/app-nks.c (verify_pin): Likewise. + * scd/app-openpgp.c (verify_a_chv, verify_chv3, do_change_pin): + Likewise. + * scd/apdu.c (check_pcsc_keypad): Add comment. + (pcsc_keypad_verify, pcsc_keypad_modify): PC/SC driver only support + readers with the feature of variable length input (yet). + (apdu_check_keypad): Set FIXEDLEN. + * scd/ccid-driver.c (ccid_transceive_secure): Add GEMPC_PINPAD + specific settings. + Support fixed length PIN input for keypad. + + SCD: API cleanup for keypad handling. + * scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s. + Change meaning of MODE. + (pininfo_t): Rename from iso7816_pininfo_t. + * scd/sc-copykeys.c: Include "iso7816.h". + * scd/scdaemon.c, scd/command.c: Likewise. + * scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h". + (ccid_transceive_secure): Follow the change of PININFO_T. + * scd/app.c: Include "apdu.h" after "iso7816.h". + * scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp) + (iso7816_change_reference_data_kp): Follow the change of API. + * scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD, + KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T. + (check_pcsc_keypad, check_ccid_keypad): Likewise. + (apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise. + (pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu) + (pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu) + (send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu) + (send_le): Follow the change of API. + * scd/apdu.h (apdu_check_keypad, apdu_keypad_verify) + (apdu_keypad_modify): Change the API. + * scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the + change. + + SCD: Clean up. Remove PADLEN for keypad input. + * scd/apdu.c (struct pininfo_s): Use iso7816_pininfo_s. + (struct reader_table_s): Remove last arg from check_keypad method. + (check_pcsc_keypad, check_pcsc_keypad): Remove PIN_PADLEN. + (pcsc_keypad_verify, pcsc_keypad_modify): Don't check PIN_PADLEN. + (send_apdu_ccid, ccid_keypad_operation): Remove PIN_PADLEN. + (apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): + Likewise. + + * scd/apdu.h (apdu_check_keypad, apdu_keypad_verify) + (apdu_keypad_modify): Remove PIN_PADLEN. + + * scd/ccid-driver.c (ccid_transceive_secure): Remove PIN_PADLEN. + + * scd/ccid-driver.h (ccid_transceive_secure): Remove PIN_PADLEN. + + * scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp) + (iso7816_change_reference_data_kp): Remove PADLEN. + + * scd/iso7816.h (struct iso7816_pininfo_s): Remove PADLEN, PADCHAR. + + SCD: Add option enable-keypad-varlen and support for GEMPC_PINPAD. + * scd/scdaemon.h (opt): Add enable_keypad_varlen. + * scd/scdaemon.c (cmd_and_opt_values): Add oEnableKeypadVarlen. + (opts, main): Add oEnableKeypadVarlen. + * scd/ccid-driver.c (GEMPC_PINPAD): New. + (ccid_transceive_secure): Add enable_varlen handling. + Enable GEMPC_PINPAD. + +2013-01-30 Werner Koch + + Remove unused status codes. + * common/status.h (STATUS_BEGIN_STREAM, STATUS_END_STREAM) + (STATUS_SIEXPIRED): Remove unused codes. + + gpg: Add status line PINENTRY_LAUNCHED. + * common/status.h (STATUS_PINENTRY_LAUNCHED): New. + * g10/server.c (server_local_s): Add field allow_pinentry_notify. + (option_handler): Add option "allow-pinentry-notify". + (gpg_proxy_pinentry_notify): New. + * g10/call-agent.c (default_inq_cb): Factor code out to the new + function. + +2013-01-25 NIIBE Yutaka + + agent: Fix a bug of handling return code from npth_join. + * agent/call-pinentry.c (agent_popup_message_stop): Fix npth_join + return code. + +2013-01-11 Christian Aistleitner + + gpg: Fix honoring --cert-digest-algo when recreating a cert. + * g10/sign.c (update_keysig_packet): Override original signature's + digest algo in hashed data and for hash computation. + +2013-01-11 Werner Koch + + Fix spurious cruft from configure summary output. + * configure.ac (build_scdaemon_extra): Remove $tmp cruft. + +2013-01-11 NIIBE Yutaka + + SCD: Hold lock for pinpad input. + * scd/apdu.c (apdu_check_keypad, apdu_keypad_verify) + (apdu_keypad_modify): Hold lock to serialize communication. + +2013-01-08 Werner Koch + + kbx: Switch from MD5 to SHA-1 for the checksum. + * kbx/keybox-blob.c (put_membuf): Use a NULL buf to store zero bytes. + (create_blob_finish): Write just the needed space. + (create_blob_finish): Switch to SHA-1. + * kbx/keybox-dump.c (print_checksum): New. + (_keybox_dump_blob): Print the checksum and the verification status. + + gpg: Cache keybox searches. + * common/iobuf.c (iobuf_seek): Fix for temp streams. + * g10/pubkey-enc.c (get_session_key, get_it): Add some log_clock calls. + * g10/keydb.c (dump_search_desc): New. + (enum_keyblock_states, struct keyblock_cache): New. + (keyblock_cache_clear): New. + (keydb_get_keyblock, keydb_search): Implement a keyblock cache. + (keydb_update_keyblock, keydb_insert_keyblock, keydb_delete_keyblock) + (keydb_rebuild_caches, keydb_search_reset): Clear the cache. + + Make log_clock easier to read. + * common/logging.c (log_clock): Print in microseconds. + + gpg: Remove a function wrapper. + * g10/keydb.h (keydb_search): Remove macro. + * g10/keydb.c (keydb_search2): Rename to keydb_search. Change all + callers. + +2013-01-08 NIIBE Yutaka + + SCD: Support not-so-smart card readers. + * scd/ccid-driver.c (struct ccid_driver_s): Add auto_voltage, + auto_param, and auto_pps. + (parse_ccid_descriptor): Set auto_voltage, auto_param, and auto_pps. + Support non-autoconf readers. + (update_param_by_atr): New. + (ccid_get_atr): Use 5V for PowerOn when auto_voltage is not supported. + Use 0x10 when nonnull_nad for SetParameters. + Call update_param_by_atr for parsing ATR, and use param for + SetParameters. + Send PPS if reader requires it and card is negotiable. + When bNadValue in the return values of SetParameters == 0, + clear handle->nonnull_nad flag. + +2013-01-07 Werner Koch + + gpg: Set the node flags while retrieving a keyblock. + * g10/keydb.c (parse_keyblock_image): Add args PK_NO and UID_NO and + set the note flags accordingly. + (keydb_get_keyblock): Transfer PK_NO and UID_NO to parse_keyblock_image. + * kbx/keybox-search.c (blob_cmp_fpr, blob_cmp_fpr_part) + (blob_cmp_name, blob_cmp_mail): Return the key/user number. + (keybox_search): Set the key and user number into the found struct. + (keybox_get_keyblock): Add args R_PK_NO and R_UID_NO and set them from + the found struct. + + New function log_clock. + * common/logging.c (log_clock): New. + * g10/gpg.c (set_debug): Print clock debug flag. + * g10/options.h (DBG_CLOCK_VALUE, DBG_CLOCK): New. + + gpg: Allow searching for user ids in a keybox. + * kbx/keybox-search.c (blob_cmp_name): Add arg X509 and adjust for PGP + use. Change callers. + (blob_cmp_mail): Add arg X509 and find the mailbox offset for PGP. + Chnage callers. + (has_subject_or_alt): Rename to has_username. + (has_username): Allow blobtype PGP. + (has_mail): Ditto. + + gpg: Allow generation of more than 4096 keys in one run. + * g10/getkey.c (cache_public_key): Make room in the cache if needed. + +2013-01-07 NIIBE Yutaka + + Update Japanese Translation. + * po/ja.po: Fix wrong translations for designated revocation. + Reported by Hideki Saito. + + Conflicts: + po/ja.po + +2013-01-05 NIIBE Yutaka + + Update Japanese Translation. + * po/ja.po: Fix fuzzy translations. + +2013-01-03 NIIBE Yutaka + + Update Japanese Translation. + * po/ja.po: Update with POT. + + Update Japanese Translation. + * po/ja.po: Start from the new one of 2.0. + +2012-12-28 Werner Koch + + gpg: Add signature cache support to the keybox. + * g10/keydb.c (parse_keyblock_image): Add arg SIGSTATUS. + (keydb_get_keyblock): Handle it. + (build_keyblock_image): Add arg SIGSTATUS. + (keydb_insert_keyblock): Handle it. + * kbx/keybox-blob.c (pgp_create_sig_part): Add arg SIGSTATUS. + (_keybox_create_openpgp_blob): Ditto. + * kbx/kbxutil.c (import_openpgp): Adjust for above change. + * kbx/keybox.h (KEYBOX_FLAG_SIG_INFO): New. + * kbx/keybox-search.c (_keybox_get_flag_location): Handle new flag. + (keybox_get_keyblock): Add arg R_SIGSTATUS. + * kbx/keybox-update.c (keybox_insert_keyblock): Add arg SIGSTATUS. + + kbxutil: Improve format of the Sig-Expire lines. + * kbx/keybox-dump.c (_keybox_dump_blob): Print the expirate timestamp. + + gpg: First working support for keyboxes. + * g10/getkey.c (get_pubkey_fast): Improve the assertion. + * kbx/keybox.h: Include iobuf.h. + * kbx/keybox-blob.c (keyboxblob_uid): Add field OFF. + (KEYBOX_WITH_OPENPGP): Remove use of this macro. + (pgp_create_key_part_single): New. + (pgp_temp_store_kid): Change to use the keybox-openpgp parser. + (pgp_create_key_part): Ditto. + (pgp_create_uid_part): Ditto. + (pgp_create_sig_part): Ditto. + (pgp_create_blob_keyblock): Ditto. + (_keybox_create_openpgp_blob): Ditto. + * kbx/keybox-search.c (keybox_get_keyblock): New. + * kbx/keybox-update.c (keybox_insert_keyblock): New. + * g10/keydb.c (parse_keyblock_image): + (keydb_get_keyblock): Support keybox. + (build_keyblock_image): New. + (keydb_insert_keyblock): Support keybox. + + * kbx/kbxutil.c (import_openpgp, main): Add option --dry-run and print + a kbx file to stdout. + + * kbx/keybox-file.c (_keybox_read_blob2): Allow keyblocks up to 10^6 + bytes. + + kbxutil: Print algo number and fold similar lines. + * kbx/keybox-defs.h (_keybox_openpgp_key_info): Add field ALGO. + * kbx/keybox-openpgp.c (parse_key): Store algo. + * kbx/kbxutil.c (dump_openpgp_key): Print algo number. + * kbx/keybox-dump.c (_keybox_dump_blob): Print identical Sig-Expire + value lines with a range of indices. + +2012-12-27 Werner Koch + + gpg: First patches to support a keybox storage backend. + * kbx/keybox-defs.h (_keybox_write_header_blob): Move prototype to .. + * kbx/keybox.h: here. + * kbx/keybox-init.c (keybox_lock): Add dummy function + * g10/keydb.c: Include keybox.h. + (KeydbResourceType): Add KEYDB_RESOURCE_TYPE_KEYBOX. + (struct resource_item): Add field kb. + (maybe_create_keyring_or_box): Add error descriptions to diagnostics. + Add arg IS_BOX. Write a header for a new keybox file. + (keydb_add_resource): No more need for the force flag. Rename the + local variable "force" to "create". Add URL scheme "gnupg-kbx". Add + magic test to detect a keybox file. Add basic support for keybox. + (keydb_new, keydb_get_resource_name, keydb_delete_keyblock) + (keydb_locate_writable, keydb_search_reset, keydb_search2): Add + support for keybox. + (lock_all, unlock_all): Ditto. + * g10/Makefile.am (needed_libs): Add libkeybox.a. + (gpg2_LDADD, gpgv2_LDADD): Add KSBA_LIBS as a workaround. + + * g10/keydb.h (KEYDB_RESOURCE_FLAG_PRIMARY) + KEYDB_RESOURCE_FLAG_DEFAULT, KEYDB_RESOURCE_FLAG_READONLY): New. + * g10/gpg.c, g10/gpgv.c (main): Use new constants. + +2012-12-20 Werner Koch + + gpg: Import only packets which are allowed in a keyblock. + * g10/import.c (valid_keyblock_packet): New. + (read_block): Store only valid packets. + +2012-12-19 Werner Koch + + gpg: Make commit 2b3cb2ee actually work. + * g10/sign.c (update_keysig_packet): Use digest_algo. + + (cherry-picked from commit d23ec86095714d388acac14b515445fe69f019e9) + + gpg: Suppress "public key already present" in quiet mode. + * g10/pkclist.c (find_and_check_key, build_pk_list): Print a + diagnostic only in non-quiet mode. + +2012-12-18 Werner Koch + + common: Add meta option ignore-invalid-option. + * common/argparse.c (iio_item_def_s, IIO_ITEM_DEF): New. + (initialize): Init field IIO_LIST. + (ignore_invalid_option_p): New. + (ignore_invalid_option_add): New. + (ignore_invalid_option_clear): New. + (optfile_parse): Implement meta option. + +2012-12-13 Werner Koch + Hans of Guardian + + utf8conv.c: Add hacks for Android. + * common/utf8conv.c [HAVE_ANDROID_SYSTEM]: Do not include iconv.h. + (iconv_open, iconv_close, load_libiconv) [HAVE_ANDROID_SYSTEM]: New + dummy functions. + (set_native_charset) [HAVE_ANDROID_SYSTEM]: Force use of "utf-8". + (jnlib_iconv_open) [HAVE_ANDROID_SYSTEM]: Act the same as under W32. + (jnlib_iconv) [HAVE_ANDROID_SYSTEM]: Ditto. + (jnlib_iconv_close) [HAVE_ANDROID_SYSTEM]: Ditto. + +2012-12-13 NIIBE Yutaka + + SCD: Fix the process of writing key or generating key. + * scd/app-openpgp.c (store_fpr): Flush KEY-FPR and KEY-TIME. + +2012-12-12 Werner Koch + + ssh: Support ECDSA keys. + * agent/command-ssh.c (SPEC_FLAG_IS_ECDSA): New. + (struct ssh_key_type_spec): Add fields CURVE_NAME and HASH_ALGO. + (ssh_key_types): Add types ecdsa-sha2-nistp{256,384,521}. + (ssh_signature_encoder_t): Add arg spec and adjust all callers. + (ssh_signature_encoder_ecdsa): New. + (sexp_key_construct, sexp_key_extract, ssh_receive_key) + (ssh_convert_key_to_blob): Support ecdsa. + (ssh_identifier_from_curve_name): New. + (ssh_send_key_public): Retrieve and pass the curve_name. + (key_secret_to_public): Ditto. + (data_sign): Add arg SPEC and change callers to pass it. + (ssh_handler_sign_request): Get the hash algo from SPEC. + * common/ssh-utils.c (get_fingerprint): Support ecdsa. + + * agent/protect.c (protect_info): Add flag ECC_HACK. + (agent_protect): Allow the use of the "curve" parameter. + * agent/t-protect.c (test_agent_protect): Add a test case for ecdsa. + + * agent/command-ssh.c (ssh_key_grip): Print a better error code. + +2012-12-11 Werner Koch + + ssh: Rewrite a function for better maintainability. + * agent/command-ssh.c (ssh_signature_encoder_dsa): Rewrite. + +2012-12-10 Werner Koch + + ssh: Improve key lookup for many keys. + * agent/command-ssh.c: Remove dirent.h. + (control_file_s): Add struct item. + (rewind_control_file): New. + (search_control_file): Factor code out to ... + (read_control_file_item): New. + (ssh_handler_request_identities): Change to iterate over entries in + sshcontrol. + + ssh: Cleanup sshcontrol file access code. + * agent/command-ssh.c (SSH_CONTROL_FILE_NAME): New macro to replace + the direct use of the string. + (struct control_file_s, control_file_t): New. + (open_control_file, close_control_file): New. Use them instead of + using fopen/fclose directly. + + agent: Add envvar "gnupg_SSH_AUTH_SOCK_by" + * agent/gpg-agent.c (main): Pass new envar gnupg_SSH_AUTH_SOCK_by to + an invoked process. + + config: Update npth.m4. + * m4/npth.m4: Take from current npth master. + +2012-12-04 NIIBE Yutaka + + Revert SCD changes of 2010-05-03. + * scd/apdu.c (pcsc_no_service): Remove. + (open_pcsc_reader_direct, open_pcsc_reader_wrapped): Remove + pcsc_no_service support. + (apdu_open_reader): Remove R_NO_SERVICE. + * scd/apdu.h (apdu_open_reader): Remove R_NO_SERVICE. + * scd/command.c (reader_disabled): Remove. + (get_current_reader): Follow the change of R_NO_SERVICE. + (open_card, cmd_serialno, scd_command_handler): Remove reader_disabled + support. + * scd/sc-copykeys.c (main): Follow the change of R_NO_SERVICE. + + Don't keep opening unavailable card reader. + * scd/command.c (update_reader_status_file): Don't call + get_current_reader. + +2012-11-30 David Shaw + + Refresh sample keys. + + Adjust awk to not add trailing whitespace. + * mksamplekeys: Tweak awk script to not add trailing whitespace to + blank lines (makes git pre-commit hook unhappy) + +2012-11-29 David Shaw + + The keyserver search menu should honor --keyid-format. + * keyserver.c (print_keyrec): Honor --keyid-format when getting back + full fingerprints from the keyserver (the comment in the code was + correct, the code was not). + +2012-11-27 Werner Koch + + Fix printing of ECC algo names in hkp keyserver listings. + * g10/keyserver.c (print_keyrec): Map OpenPGP algorithm ids. + +2012-11-26 Ben Kibbey + + Check for inet_addr() in -lnsl. + * configure.ac: Check for inet_addr() in libnsl. + +2012-11-20 Werner Koch + + Do not use a broken ttyname. + * configure.ac (HAVE_BROKEN_TTYNAME): New ac_define set for Android + systems. + * common/util.h (gnupg_ttyname): New macro. Change all callers of + ttyname to use this macro instead. + (ttyname) [W32]: Rename to _gnupg_ttyname and use also if + HAVE_BROKEN_TTYNAME is defined. + * common/simple-pwquery.c (agent_send_all_options): Keep on using + ttyname unless HAVE_BROKEN_TTYNAME is set. This is because this file + may be used standalone. + +2012-11-16 Werner Koch + + Fix non-portable use of chmod in autogen.sh. + * autogen.sh: Remove option -c from chmod. + + Improve parsing of the GIT revision number. + * configure.ac (mmm4_revision): Use git rev-parse. + + Add an OpenPGP card vendor. + * g10/card-util.c (get_manufacturer): Add Yubico. + +2012-11-06 Werner Koch + + agent: Use wipememory instead of memset in one place. + * agent/command.c (clear_outbuf): Use wipememory. Suggested by Ben + Kibbey. + + Allow decryption with card keys > 3072 bits. + * scd/command.c (MAXLEN_SETDATA): New. + (cmd_setdata): Add option --append. + * agent/call-scd.c (agent_card_pkdecrypt): Use new option for long + data. + + * scd/app-openpgp.c (struct app_local_s): Add field manufacturer. + (app_select_openpgp): Store manufacturer. + (do_decipher): Print a note for broken cards. + +2012-11-02 NIIBE Yutaka + + agent: Fix wrong use of gcry_sexp_build_array. + * findkey.c (agent_public_key_from_file): Fix use of + gcry_sexp_build_array. + +2012-10-31 NIIBE Yutaka + + SCD: Upon error, open_pcsc_reader_wrapped does same as _direct. + * scd/apdu.c (PCSC_E_NO_SERVICE): New. + (open_pcsc_reader_direct): Use PCSC_E_NO_SERVICE. + (open_pcsc_reader_wrapped): Set pcsc_no_service. + +2012-08-24 Werner Koch + + Update and enable French translation. + * po/fr.po: Update. + * po/LINGUAS: Enable fr. + +2012-08-24 David Prévot + + Fix typos spotted during translations. + * agent/genkey.c: s/to to/to/ + * sm/*.c: s/failed to allocated/failed to allocate/ + * sm/certlist.c, ./dirmngr/validate.c: s/should have not/should not have/ + * g10/seskey.c: missing closing parenthesis + * dirmngr/crlcache.c: s/may has/may have/ + + Consistency fix: + + * g10/gpg.c, kbx/kbxutil.c, sm/gpgsm.c: uppercase after Syntax + * dirmngr/dirmngr_ldap: no period in Syntax + * dirmngr/dirmngr-client.c: infinitive for option description: + s/certificates are expected/expect certificates/ + + Keep previous msgids of translated messages. + * po/Makefile.in.in: Use --previous with msgmerge. + +2012-08-24 Hans-Christoph Steiner + + Fix build system for Android by disabling tests since its x-compiled. + * configure.ac (HAVE_ANDROID_SYSTEM, RUN_TESTS): New. + (AH_BOTTOM) [__ANDROID__]: Do not re-define ttyname. + * Makefile.am: Depend tests on new RUN_TESTS conditional. + +2012-08-24 Werner Koch + + Fix left over use of jnlib on some platforms. + * tools/watchgnupg.c: Take mischelp.h from common/ and not jnlib/. + +2012-06-25 NIIBE Yutaka + + scd: handle reader/token removal. + * scd/apdu.c (pcsc_error_to_sw): PCSC_E_UNKNOWN_READER means + SW_HOST_NO_READER. + +2012-06-05 Werner Koch + + Change all quotes in strings and comments to the new GNU standard. + The asymmetric quotes used by GNU in the past (`...') don't render + nicely on modern systems. We now use two \x27 characters ('...'). + + The proper solution would be to use the correct Unicode symmetric + quotes here. However this has the disadvantage that the system + requires Unicode support. We don't want that today. If Unicode is + available a generated po file can be used to output proper quotes. A + simple sed script like the one used for en@quote is sufficient to + change them. + + The changes have been done by applying + + sed -i "s/\`\([^'\`]*\)'/'\1'/g" + + to most files and fixing obvious problems by hand. The msgid strings in + the po files were fixed with a similar command. + +2012-05-24 Werner Koch + + Print the hash algorithm in colon mode key listing. + * g10/keylist.c (list_keyblock_colon): Print digest_algo. + + Fix type conflict warning. + * g10/keylist.c: Change min_cert_level to a byte. + +2012-05-11 Werner Koch + + Switch to the new automagic beta numbering scheme. + * configure.ac: Add all the require m4 magic. + +2012-05-08 Werner Koch + + Add tweaks for the not anymore patented IDEA algorithm. + * g10/keygen.c (keygen_set_std_prefs): Include IDEA only in PGP2 + compatibility mode. + * g10/misc.c (idea_cipher_warn): Remove. Also remove all callers. + * common/status.h (STATUS_RSA_OR_IDEA): Remove. Do not emit this + status anymore. + + po: Update de.po. + * po/de.po: Update. + + common: Remove generated files only during maintainer-clean. + * common/Makefile.am (CLEANFILES): Rename to MAINTAINERCLEANFILES. + +2012-04-30 Werner Koch + + agent: Fix deadlock in trustlist due to the switch to npth. + * agent/trustlist.c (clear_trusttable): New. + (agent_reload_trustlist): Use new function. + (read_trustfiles): Require to be called with lock held. + (agent_istrusted): Factor all code out to ... + (istrusted_internal): new. Add ALREADY_LOCKED arg. Make sure the + table islocked. Do not print TRUSTLISTFLAG stati if called internally. + (agent_marktrusted): Replace calls to agent_reload_trustlist by + explicit code. + +2012-04-26 NIIBE Yutaka + + make DNS and URI fields work in gpgsm --gen-key. + * sm/certreqgen-ui.c (gpgsm_gencertreq_tty): Actually set mb_uri and + mb_dns.avoid buffer strncpy-induced buffer overrun + +2012-04-26 Jim Meyering + + avoid buffer strncpy-induced buffer overrun. + * dirmngr/crlcache.c (open_dir): Ensure that both this_update + and next_update member strings are NUL-terminated. + + remove doubled words in a comment. + +2012-04-20 Werner Koch + + Change license for some files in common to LGPLv3+/GPLv2+. + Having the LGPL on the common GnuPG code helps to share code + between GnuPG and related projects (like GPGME and Libassuan). This + is good for interoperability and to reduces bugs. + + * common/asshelp.c, common/asshelp.h, common/asshelp2.c, common/b64dec.c + * common/b64enc.c, common/convert.c, common/dns-cert.c + * common/dns-cert.h common/exechelp-posix.c, common/exechelp-w32.c + * common/exechelp-w32ce.c, common/exechelp.h, common/get-passphrase.c + * common/get-passphrase.h, common/gettime.c, common/gpgrlhelp.c + * common/helpfile.c, common/homedir.c, common/http.c, common/http.h + * common/i18n.c, common/init.c, common/init.h, common/iobuf.c + * common/iobuf.h, common/localename.c, common/membuf.c, common/membuf.h + * common/miscellaneous.c, common/openpgp-oid.c, common/openpgpdefs.h + * common/percent.c, common/pka.c, common/pka.h, common/session-env.c + * common/session-env.h, common/sexp-parse.h, common/sexputil.c + * common/signal.c, common/srv.c, common/srv.h, common/ssh-utils.c + * common/ssh-utils.h, common/sysutils.c, common/sysutils.h + * common/tlv.c, common/tlv.h, common/ttyio.c, common/ttyio.h + * common/userids.c, common/userids.h, common/xasprintf.c: Change + license to LGPLv3+/GPLv2+/ + +2012-04-10 Ben Kibbey + + Fix killing PID -1. + When the KILLSCD command had been sent a race condition would occur + causing PID -1 getting killed, which on Linux seems to terminate all + applications for the current user. + +2012-04-05 Werner Koch + + Do not mix test result with progress lines. + This makes parsing of the results easier. Fixes bug#1400. + + * tests/openpgp/defs.inc (progress_cancel, progress_end) + (progress_new): New. + * tests/openpgp/conventional-mdc.test: Use progress functions + * tests/openpgp/conventional.test: Ditto. + * tests/openpgp/encrypt-dsa.test: Ditto. + * tests/openpgp/encrypt.test: Ditto. + * tests/openpgp/sigs.test: Ditto. + +2012-04-04 Ben Kibbey + + Mention status messages in the documentation. + Note INQUIRE_MAXLEN. + + Document PASSWD --preset. + + Document GENKEY options. + + Document PRESET_PASSPHRASE. + + Document CLEAR_PASSPHRASE. + And describe the --mode=normal option. + +2012-03-27 Werner Koch + + Fix timegm regression test. + * common/t-timestuff.c (test_timegm): Change test to use January and + not February or December+1. Bug spotted by Daniel Kahn Gillmor. + + Print warning for arguments not considered an option. + GnuPG requires that options are given before other arguments. This + can sometimes be confusing. We now print a warning if we found an + argument looking alike a long option without being preceded by the + stop option. This is bug#1343. + + * common/argparse.h (ARGPARSE_FLAG_STOP_SEEN): New. + * common/argparse.c (arg_parse): Set new flag. + * g10/gpg.c (main): Print the warning. + * agent/gpg-agent.c (main): Ditto. + * dirmngr/dirmngr.c (main): Ditto. + * g13/g13.c (main): Ditto. + * scd/scdaemon.c (main): Ditto. + * sm/gpgsm.c (main): Ditto. + * tools/gpg-connect-agent.c (main): Ditto. + * tools/gpgconf.c (main): Ditto. + +2012-03-26 Werner Koch + + Allow compress algorithm 0. + * g10/mainproc.c (proc_compressed): Remove superfluous check for + compress algorithm 0. Reported by pfandrade. This is bug#1326. + + Add mksamplekeys script. + * doc/mksamplekeys: New. + +2012-02-28 Marcus Brinkmann + + Replace npth_yield in busy wait by npth_usleep. + * dirmngr/ldap-wrapper.c (ldap_wrapper_wait_connections): Call + npth_usleep instead of npth_yield. + +2012-02-16 Marcus Brinkmann + + Check for lber and link dirmngr_ldap to it. + * configure.ac (LBER_LIBS, HAVE_LBER): New variables, check for lber. + * dirmngr/Makefile.am (dirmngr_lda_LDADD): Add $(LBER_LIBS). + +2012-02-07 Werner Koch + + agent: Add pin length field to the shadowed private key format. + This is not yet fully implemented. It will eventually allow to + support pinpad equipped readers which do not support variable length + pin lengths. + * agent/protect.c (parse_shadow_info): Add optional arg R_PINLEN and + parse pinlen info. Change all callers to pass NULL for it. + + Use new status printing functions. + * agent/command.c (cmd_geteventcounter): Get rid of static buffers. + * scd/command.c (cmd_serialno, cmd_learn): Simplify by using + print_assuan_status. + + agent: New function agent_print_status. + * common/asshelp2.c (vprint_assuan_status): New. + (print_assuan_status): Re-implement using above func. + * agent/command.c (agent_print_status): New. + + po: Add Ukrainian translation. + * po/uk.po: New. + + common: Replace macro based function calls by using DEFAULT_ERRSOURCE. + * common/dns-cert.h (get_dns_cert): Remove macro. + * common/dns-cert.c (_get_dns_cert): Rename to get_dns_cert. Replace + arg ERRSOURCE by global DEFAULT_ERRSOURCE. + * common/http.h (http_parse_uri, http_raw_connect, http_open) + (http_open_document, http_wait_response): Remove macros. + * common/http.c (_http_parse_uri, _http_raw_connect, _http_open) + (_http_open_document, _http_wait_response): Remove underscore from + symbols. Replace args ERRSOURCE by global DEFAULT_ERRSOURCE. + * common/ssh-utils.h (ssh_get_fingerprint) + (ssh_get_fingerprint_string): Remove macros. + * common/ssh-utils.h (_ssh_get_fingerprint) + (_ssh_get_fingerprint_string): Remove underscore from symbols. + Replace args ERRSOURCE by global DEFAULT_ERRSOURCE. + * common/tlv.h (parse_ber_header, parse_sexp): Remove macros. + * common/tlv.c: Include util.h. + (_parse_ber_header, _parse_sexp): Remove underscore from symbols. + Replace args ERRSOURCE by global DEFAULT_ERRSOURCE. + +2012-02-06 Werner Koch + + Add replacement hack for Android's broken ttyname. + * configure.ac (HAVE_TTYNAME) [__ANDROID__]: Add hack. + + agent: Simplify printing of INQUIRE_MAXLEN. + * agent/command.c: Include asshelp.h. + (cmd_pkdecrypt, cmd_genkey, cmd_preset_passphrase) + (pinentry_loopback): Use print_assuan_status for INQUIRE_MAXLEN. + + common: Add function print_assuan_status. + * common/asshelp2.c: New. + (print_assuan_status): New function. + * common/Makefile.am (common_sources): Add asshelp2.c. + + common: Add a global variable to for the default error source. + For the shared code parts it is cumbersome to pass an error sourse + variable to each function. Its value is always a constant for a given + binary and thus a global variable makes things a lot easier than the + former macro stuff. + * common/init.c (default_errsource): New global var. + (init_common_subsystems): Rename to _init_common_subsystems. Set + DEFAULT_ERRSOURCE. + * common/init.h: Assert value of GPG_ERR_SOURCE_DEFAULT. + (init_common_subsystems): New macro. + * common/util.h (default_errsource): Add declaration. + * kbx/keybox-defs.h: Add some GPG_ERR_SOURCE_DEFAULT trickery. + +2012-02-03 Ben Kibbey + + Also let GENKEY and PKDECRYPT send the INQUIRE_MAXLEN status message. + * agent/command.c (cmd_pkdecrypt): Send the INQUIRE_MAXLEN status + message before doing the inquire. + (cmd_genkey): Ditto. + +2012-02-02 Ben Kibbey + + Inform the client of the preset passphrase length. + * agent/command.c (cmd_preset_passphrase): Send the INQUIRE_MAXLEN + status message before inquiring the passphrase. + +2012-02-01 David Shaw + + Honor --cert-digest-algo when recreating a cert. + * g10/sign.c (update_keysig_packet): Honor --cert-digest-algo when + recreating a cert. + + This is used by various things in --edit-key like setpref, primary, + etc. Suggested by Christian Aistleitner. + +2012-01-27 Werner Koch + + gl: Add support for Android to stdint.h replacement. + * gl/stdint_.h: When included from Bionic , just include + the system's . + + gpg-connect-tool: Take the string "true" as a true condition. + * tools/gpg-connect-agent.c (main): Handle strings "true" and "yes" in + conditions as expected. + +2012-01-26 Ben Kibbey + + Return GPG_ERR_CARD_NOT_PRESENT when pinentry-mode=loopback. + Since there isn't a way to prompt the user to insert the smartcard when + pinentry-mode=loopback, return GPG_ERR_CARD_NOT_PRESENT instead of + GPG_ERR_NO_PIN_ENTRY. + + * agent/divert-scd.c (ask_for_card): Return GPG_ERR_CARD_NOT_PRESENT + when pinentry-mode=loopback. + + Also check for GPG_ERR_ASS_CANCELED during an inquire. + Fix pinentry-mode=loopback when cancelling an inquire from scdaemon. + This is similar to commit 4f21f8d but for both protocol command + cancellation and pinentry cancellation. + + * agent/call-scd.c (agent_card_pkdecrypt): Check for + GPG_ERR_ASS_CANCELED. + (agent_card_pksign): Ditto. + +2012-01-25 Werner Koch + + nPth is now a hard requirement for GnuPG. + * configure.ac: Remove cruft to allow building without npth. + + Require libassuan 2.1.0. + * configure.ac (NEED_LIBASSUAN_VERSION): Set to 2.1.0. This is due to + the npth changes. + + Fix strerror vs. gpg_strerror usage. + This bug was introduced by the migration to npth. + * agent/gpg-agent.c (handle_connections): Use strerror. + + Add missing variable. + * agent/gpg-agent.c (handle_connections) [!W32]: Add missing variable. + +2012-01-25 Marcus Brinkmann + + Port LDAP wrapper to NPTH. + * agent/gpg-agent.c (handle_connections): Handle error. + * dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c: Port to NPTH. + + Port Windows code to NPTH. + * agent/gpg-agent.c (get_agent_ssh_socket_name): Use + INVALID_HANDLE_VALUE instead of 0. + (handle_signal) [!HAVE_W32_SYSTEM]: Don't define. + (handle_connections): Port Windows code to NPTH. + * dirmngr/dirmngr.c (handle_connections): Port Windows code to NPTH. + * g13/g13.c (handle_connections): Port Windows code to NPTH. + * scd/scdaemon.c (handle_connections): Port Windows code to NPTH. + + Port to npth. + * configure.ac: Don't check for PTH but for NPTH. + (AH_BOTTOM): Remove PTH_SYSCALL_SOFT. + (have_pth): Rename to ... + (have_npth): ... this. + (USE_GNU_NPTH): Rename to ... + (USE_GNU_PTH): ... this. + * m4/npth.m4: New file. + * agent/Makefile.am, agent/cache.c, agent/call-pinentry.c, + agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c, + agent/trustlist.c, common/Makefile.am, common/estream.c, + common/exechelp-posix.c, common/exechelp-w32.c, + common/exechelp-w32ce.c, common/http.c, common/init.c, + common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c, + dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c, + dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am, + g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am, + scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c, + scd/scdaemon.c, tools/Makefile.am: Port to npth. + +2012-01-25 Werner Koch + + Require gitlog-to-changelog to be installed. + * Makefile.am (GITLOG_TO_CHANGELOG): New. + (gen-ChangeLog): Use installed version of gitlog-to-changelog. + +2012-01-20 David Shaw + + Changes to --min-cert-level should cause a trustdb rebuild (issue 1366) + * g10/gpgv.c, g10/trustdb.c (read_trust_options): Add min_cert_level + + * g10/trustdb.c (check_trustdb_stale): Request a rebuild if + pending_check_trustdb is true (set when we detect a trustdb + parameter has changed). + + * g10/keylist.c (public_key_list): Use 'l' in the "tru" with-colons + listing for min_cert_level not matching. + + * g10/tdbio.c (tdbio_update_version_record, create_version_record, + tdbio_db_matches_options, tdbio_dump_record, tdbio_read_record, + tdbio_write_record): Add a byte for min_cert_level in the tdbio + version record. + +2012-01-20 Werner Koch + + estream: Fix unclean usage of realloc. + * common/estream-printf.c (_ESTREAM_PRINTF_MALLOC): Remove. + (_ESTREAM_PRINTF_FREE): Remove. + (_ESTREAM_PRINTF_REALLOC): New. + (fixed_realloc) [!_ESTREAM_PRINTF_REALLOC]): New. + (estream_vasprintf): Use my_printf_realloc instead of my_printf_malloc + and my_printf_free. + (dynamic_buffer_out): Use my_printf_realloc instead of realloc. + + Do not copy default merge commit log entries into the ChangeLog. + * scripts/gitlog-to-changelog: Skip merge commits. + +2012-01-18 Ben Kibbey + + Add the INQUIRE_MAXLEN status message. + This status message is used to inform the client of the maximum length + of an inquired passphrase and is used in pinentry-mode=loopback. + + * agent/command.c (pinentry_loopback): Send the INQUIRE_MAXLEN status + message before doing the inquire. + +2012-01-16 Jim Meyering + + yat2m: don't dereference pointer to freed memory. + * doc/yat2m.c (top_parse_file): Correct macrolist-freeing loop. + + gpg-agent: fix lc-messages handling not to change Xauthority setting. + * agent/gpg-agent.c (main): Supply omitted "break" statement for + lc-messages option. Otherwise, control would fall through to the + following oXauthority case and use the same value there. + +2012-01-15 Werner Koch + + Fix indentation. + +2012-01-14 Ben Kibbey + + Fix scdaemon pinentry inquire cancelation. + Similar to commit 29af488 but also fixes PKDECRYPT and PKSIGN. + + * agent/call-scd.c (agent_card_pkdecrypt): Check for GPG_ERR_CANCELED + when returning from the PKDECRYPT operation of scdaemon and cancel the + inquire. + (agent_card_pksign): Ditto. + (cancel_inquire): New. + +2012-01-11 Werner Koch + + gpg: Fix segv with RSA_S keys. + * g10/misc.c (pubkey_get_npkey, pubkey_get_nskey) + (pubkey_get_nsig, pubkey_get_nenc): Map all RSA algo ids to + GCRY_PK_RSA. + + estream: Avoid printing leading zeroes by %p on 32 bit systems. + * common/estream-printf.c (pr_pointer): Synchronize definition of + AULONG with its use. + +2012-01-11 David Shaw + + Refresh sample keys. + +2012-01-10 David Shaw + + Adapt HKP fix for fingerprint/long keyid retrievals for dirmngr. + * dirmngr/ks-engine-hkp.c (ks_hkp_get): Use the longest valid keyid form + +2012-01-06 Werner Koch + + gpg: Make the double space in the middle of a fingerprint optional. + This change might help to c+p a fingerprint from an HTML page without + being enclosed in a "pre" tag. + * common/userids.c (classify_user_id): Skip a second blank in the + middle of a fingerprint. + + gpg: Allow use of a standard space separated fingerprint. + * common/userids.c (classify_user_id): Check for space separated GPG + fingerprint. + +2012-01-06 NIIBE Yutaka + + Merge ccid_driver_improvement branch. + * scd/apdu.c (ccid_keypad_operation): Rename from ccid_keypad_verify. + (open_ccid_reader): Use ccid_keypad_operation for verify and modify. + + * scd/ccid-driver.c (VENDOR_VASCO, VASCO_920): New. + (ccid_transceive_apdu_level): Permit sending packet where + apdulen <= 289. Support receiving packets in a chain. + (ccid_transceive_secure): Maximum is 15 for VASCO DIGIPASS 920. + Support keypad_modify method such as CHANGE_REFERENCE_DATA: 0x24. + +2012-01-03 Marcus Brinkmann + + Silence gcc warning. + * sm/call-dirmngr.c (get_cached_cert): Make sure buflen is initialized. + + Revert last change, add comment about link() return values. + * common/dotlock.c (use_hardlinks_p, dotlock_take_unix): Do not check + return value of link(). + + Fix compiler warnings. + * common/dotlock.c (use_hardlinks_p, dotlock_take_unix): Check return + value of link(). + * g13/g13.c: Make sure err is initialized. + * scd/scdaemon.c (main) [!USE_GCRY_THREAD_CBS]: Do not define ERR. + + Fix last change: Only set gcrypt thread callback for older versions. + * dirmngr/dirmngr.c, g13/g13.c: Rename FIX_GCRY_PTH_INIT to + USE_GCRY_THREAD_CBS. + +2012-01-03 Werner Koch + + Terminate csh commands with a semicolon also for dirmngr. + * dirmngr/dirmngr.c (main): Terminate csh style output with a semicolon. + + Terminate csh commands with a semicolon. + Fixes bug#1386. + + * agent/gpg-agent.c (main): Terminate csh style output with a semicolon. + * scd/scdaemon.c: Ditto. + +2012-01-02 Marcus Brinkmann + + Only set gcrypt thread callback for older version of gcrypt. + * agent/gpg-agent.c, dirmngr/dirmngr.c, g13/g13.c, scd/scdaemon.c + (USE_GCRY_THREAD_CBS): New macro, defined if + GCRY_THREAD_OPTION_VERSION is 0. + (fixed_gcry_pth_init) [!USE_GCRY_THREAD_CBS]: Don't define. + (main) [!USE_GCRY_THREAD_CBS]: Do not install thread callbacks. + +2011-12-28 David Shaw + + Use the longest key ID available when talking to a HKP server. + This is issue 1340. Now that PKSD is dead, and SKS supports long key + IDs, this is safe to do. Patch from Daniel Kahn Gillmor + . + +2011-12-20 Werner Koch + + Post-release version number update. + + Release 2.1.0beta3. + + Prepare for the beta3 release. + + po: Update the German translation. + + Add the STEED Self-Signing Nonthority certificate. + * doc/com-certs.pem: Install it when creating a keybox. + + faq: Add section on US export restrictions. + + Require Libassuan 2.0.3. + * configure.ac: Require Libassuan 2.0.3. + * agent/call-scd.c (ASSUAN_CONVEY_COMMENTS): Remove macro replacement. + * agent/command.c (cmd_killagent) [ASSUAN_FORCE_CLOSE]: Remove + dependency. + (cmd_killagent) [ASSUAN_FORCE_CLOSE]: Ditto. + * scd/command.c (cmd_killscd) [ASSUAN_FORCE_CLOSE]: Ditto. + +2011-12-20 NIIBE Yutaka + + Add error log and debug log for pcsc_keypad_verify and pcsc_keypad_modify. + * scd/apdu.c (pcsc_keypad_verify): Add debug log and error log. + (pcsc_keypad_modify): Likewise. + +2011-12-19 Werner Koch + Ben Kibbey + + scd: Fix for card change returning GPG_ERR_CARD_RESET. + * scd/apdu.c (apdu_connect): Do not test for zero atrlen. + +2011-12-16 NIIBE Yutaka + + Don't kill pinentry by SIGKILL but let it quit by SIGINT. + * agent/call-pinentry.c (agent_popup_message_stop): To pinentry, send + SIGINT (was: SIGKILL). + +2011-12-15 David Shaw + + Merge fix for issue 1331 from 1.4. + * photoid.c (generate_photo_id): Check for the JPEG magic numbers + instead of JFIF since some programs generate an EXIF header first. + +2011-12-15 Werner Koch + + scd: Prefer application Geldkarte over DINSIG. + * scd/app.c (select_application): Reorder application tests. + + scd: Add option --dump-atr to command APDU. + * scd/atr.c: Rewrite. + * scd/Makefile.am (scdaemon_SOURCES): Add atr.c and atr.h. + * scd/command.c (cmd_apdu): Add option --dump-atr. + + estream: New function es_fclose_snatch. + * common/estream.c (cookie_ioctl_function_t): New type. + (es_fclose_snatch): New function. + (COOKIE_IOCTL_SNATCH_BUFFER): New constant. + (struct estream_internal): Add field FUNC_IOCTL. + (es_initialize): Clear FUNC_IOCTL. + (es_func_mem_ioctl): New function. + (es_fopenmem, es_fopenmem_init): Init FUNC_IOCTL. + +2011-12-14 Werner Koch + + scd: Skip S/N reading for the "undefined" application. + * scd/app.c (select_application): Skip serial number reading. + + scd: Add more status word values for documentation. + + scd: Add the "undefined" stub application. + * scd/app.c (select_application): Implement the "undefined" + application. + + agent: Pass comment lines from scd verbatim thru gpg-agent. + * agent/call-scd.c (pass_status_thru): Pass comment lines verbatim. + * tools/gpg-connect-agent.c (help_cmd_p): New. + (main): Treat an "SCD HELP" the same as "HELP". + + scd: Fix resetting and closing of the reader. + * scd/command.c (update_card_removed): Do no act on an invalid VRDR. + (do_reset): Ignore apdu_reset error codes for no and inactive card. + Close the reader before setting the slot to -1. + (update_reader_status_file): Notify the application before closing the + reader. + + scd: Add debug option for reader function calls. + * scd/scdaemon.h (DBG_READER_VALUE, DBG_READER): New. + * scd/apdu.c (apdu_open_reader, apdu_close_reader) + (apdu_shutdown_reader, apdu_connect, apdu_disconnect) + (apdu_reset, apdu_get_atr, apdu_get_status): Add debug code. + (apdu_activate): Remove this unused function. + +2011-12-13 Werner Koch + + scd: New option --debug-assuan-log-cats. + * scd/scdaemon.c (oDebugAssuanLogCats): New. + (opts): Add option --debug-assuan-log-cats. + (main): Implement option. + * common/asshelp.c (set_libassuan_log_cats): New. + + scd: Introduce a virtual reader table. + The vreader table makes the code more clear by explicitly talking + about APDU slots and reader indices. It also accommodates for future + extensions. + + * scd/scdaemon.h (server_control_s): Remove READER_SLOT. + * scd/scdaemon.c (scd_init_default_ctrl): Do not init READER_SLOT. + * scd/app.c (check_application_conflict): Add arg SLOT. + * scd/command.c (slot_status_s): Rename to vreader_s. + (server_local_s): Add field VREADER_IDX as replacement for + the READER_SLOT in server_control_s. Change all users. + (slot_table): Rename to vreader_table. Change all users. + (vreader_slot): New. + (do_reset, cmd_apdu): Map vreader to apdu slot. + (get_reader_slot): Rename to get_current_reader. Return -1 on error. + (open_card): Map vreader toapdu slot. Pass slot to + check_application_conflict. + (scd_command_handler): Init VREADER_IDX. + (update_reader_status_file): Reset SLOT field on error. + +2011-12-12 Werner Koch + + scd: Retry command SERIALNO for an inactive card. + * scd/command.c (cmd_serialno): Retry once for an inactive card. + + Fix detection of card removal and insertion. + * scd/apdu.c (apdu_connect): Return status codes for no card available + and inactive card. + * scd/command.c (TEST_CARD_REMOVAL): Also test for GPG_ERR_CARD_RESET. + (open_card): Map apdu_connect status to GPG_ERR_CARD_RESET. + + gitlog-to-changelog: New option --tear-off. + * scripts/gitlog-to-changelog: Add option --tear-off. + * Makefile.am (gen-ChangeLog): Use that option. + +2011-12-07 Werner Koch + + gpgsm: Add new validation model "steed". + * sm/gpgsm.h (VALIDATE_FLAG_STEED): New. + * sm/gpgsm.c (gpgsm_parse_validation_model): Add model "steed". + * sm/server.c (option_handler): Allow validation model "steed". + * sm/certlist.c (gpgsm_cert_has_well_known_private_key): New. + * sm/certchain.c (do_validate_chain): Handle the + well-known-private-key attribute. Support the "steed" model. + (gpgsm_validate_chain): Ditto. + * sm/verify.c (gpgsm_verify): Return "steed" in the trust status line. + * sm/keylist.c (list_cert_colon): Print the new 'w' flag. + + Correct punctuation in the ChangeLog summary line. + * Makefile.am (gen-ChangeLog): Supply --append-dot. + + Allow comments which will not show up in the ChangeLog. + * scripts/gitlog-to-changelog: Ignore lines after a "--" line. + +2011-12-06 Werner Koch + + gpgsm: Allow specification of an AuthorityKeyIdentifier. + * sm/certreqgen.c (pAUTHKEYID): New. + (read_parameters): Add keyword Authority-Key-Id. + (proc_parameters): Check its value. + (create_request): Insert an Authority-Key-Id. + + gpgsm: Allow arbitrary extensions for cert creation. + * sm/certreqgen.c (pSUBJKEYID, pEXTENSION): New. + (read_parameters): Add new keywords. + (proc_parameters): Check values of new keywords. + (create_request): Add SubjectKeyId and extensions. + (parse_parameter_usage): Support "cert" and the encrypt alias "encr". + + gpgsm: Fix storing of the serial number. + * sm/certreqgen.c (create_request): Fix hex-bin conversion. + +2011-12-05 Werner Koch + + Fix last change. + * agent/command.c (start_command_handler): Remove use of removed var. + + Amend the agent code with more comments. + * agent/command.c (server_local_s): Remove unused field MESSAGE_FD. + +2011-12-02 Werner Koch + + Support the Cherry ST-2000 card reader. + * scd/ccid-driver.c (SCM_SCR331, SCM_SCR331DI, SCM_SCR335) + (SCM_SCR3320, SCM_SPR532, CHERRY_ST2000): New constants. + (parse_ccid_descriptor): Use them. + (scan_or_find_usb_device, ccid_transceive_secure): Handle Cherry + ST-2000. Suggested by Matthias-Christian Ott. + + Avoid possible double free in export.c. + * g10/export.c (transfer_format_to_openpgp): Avoid possible double + free of LIST. Reported by NIIBE Yutaka. + +2011-12-02 NIIBE Yutaka + + Fix pinpad input support for passphrase modification. + * apdu.c (pcsc_keypad_verify): Have dummy Lc field with value 0. + (pcsc_keypad_modify): Likewise. + (pcsc_keypad_modify): It's only for ISO7816_CHANGE_REFERENCE_DATA. + bConfirmPIN value is determined by the parameter p0. + + * app-openpgp.c (do_change_pin): The flag use_keypad should be 0 when + reset_mode is on, or resetcode is on. use_keypad only makes sense for + iso7816_change_reference_data_kp. + + * iso7816.h (iso7816_put_data_kp): Remove. + (iso7816_reset_retry_counter_kp): Remove. + (iso7816_reset_retry_counter_with_rc_kp): Remove. + (iso7816_change_reference_data_kp): Add an argument: IS_EXCHANGE. + + * iso7816.c (iso7816_put_data_kp): Remove. + (iso7816_reset_retry_counter_kp): Remove. + (iso7816_reset_retry_counter_with_rc_kp): Remove. + (iso7816_change_reference_data_kp): Add an argument: IS_EXCHANGE. + +2011-12-01 Werner Koch + + Add hook to check the commit log syntax. + * autogen.sh: Install commit-msg hook for git. + + Generate the ChangeLog from commit logs. + * scripts/gitlog-to-changelog: New script. Taken from gnulib. + * scripts/git-log-fix: New file. + * scripts/git-log-footer: New file. + * doc/HACKING: Describe the ChangeLog policy + * ChangeLog: New file. + * Makefile.am (EXTRA_DIST): Add new files. + (gen-ChangeLog): New. + (dist-hook): Run gen-ChangeLog. + + Rename all ChangeLog files to ChangeLog-2011. + +2011-12-01 Werner Koch + + NB: Changes done before December 1st, 2011 are described in + per directory files named ChangeLog-2011. See doc/HACKING for + details. + + ----- + Copyright (C) 2011 Free Software Foundation, Inc. + + Copying and distribution of this file and/or the original GIT + commit log messages, with or without modification, are + permitted provided the copyright notice and this notice are + preserved. diff --git a/ChangeLog-2011 b/ChangeLog-2011 new file mode 100644 index 0000000..01601fa --- /dev/null +++ b/ChangeLog-2011 @@ -0,0 +1,1394 @@ +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-11-29 Werner Koch + + * autogen.sh: Make sure HOME et al have no unsafe characters. + +2011-11-28 Jim Meyering + + accept --with-libgpg-error-prefix as well as --with-gpg-error-prefix + * m4/gpg-error.m4: Update from git master. + +2011-09-23 Werner Koch + + * configure.ac: Remove check for gcry_kdf_derive. + +2011-08-10 Werner Koch + + * configure.ac: Fix new autoconf warnings. + +2011-05-20 Werner Koch + + * configure.ac: Require libgpg-error 1.10. + +2011-03-08 Werner Koch + + Release 2.1.0beta2. + + * configure.ac: Require libgcrypt 1.5.0. + (HAVE_GCRY_PK_ECDH, HAVE_GCRY_PK_GET_CURVE): Remove. + (utmp.h): Check for header. + +2011-02-25 Werner Koch + + * configure.ac: Require libksba 1.2. + +2011-02-04 Werner Koch + + * autogen.sh: Ensure that the git pre-commit hoom has been + enabled. Add a cleanpo filter if not yet set. + +2011-02-03 Werner Koch + + * configure.ac (HAVE_GCRY_PK_GET_CURVE): Use AC_TRY_LINK. + +2011-02-01 Werner Koch + + * configure.ac (HAVE_GCRY_PK_GET_CURVE): Define if availabale. + +2011-01-20 Werner Koch + + * configure.ac (AC_CONFIG_FILES): Remove keyserver/. + +2011-01-19 Werner Koch + + * configure.ac: Add new option --enable-gpg2-is-gpg. + (NAME_OF_INSTALLED_GPG): New ac_define. + * autogen.sh [--build-w32ce]: Use --enable-gpg2-is-gpg. + +2011-01-21 Werner Koch + + * configure.ac: Need Libgcrypt 1.4.6 due to AESWRAP. + (HAVE_GCRY_PK_ECDH): Add new test. + +2011-01-03 Werner Koch + + * README.SVN: Rename to README.GIT. + * Makefile.am (EXTRA_DIST): Adjust for that change. + +2010-12-14 Werner Koch + + * configure.ac (BUILD_WITH_GPG, BUILD_WITH_GPGSM) + (BUILD_WITH_AGENT, BUILD_WITH_SCDAEMON, BUILD_WITH_DIRMNGR) + (BUILD_WITH_G13): New defines. + +2010-11-23 Werner Koch + + * am/cmacros.am (extra_bin_ldflags): New. For W32CE set the stack + size to 256k. + +2010-11-17 Werner Koch + + * configure.ac (ENABLE_CARD_SUPPORT): Define. + +2010-10-27 Werner Koch + + * acinclude.m4 (GNUPG_TIME_T_UNSIGNED): New. + * configure.ac (AC_HEADER_TIME): Include before checking time_t. + (GNUPG_TIME_T_UNSIGNED): Add. + +2010-10-26 Werner Koch + + Release 2.1.0beta1. + +2010-10-18 Werner Koch + + * Makefile.am (install-data-hook): Add W32 specific hook. + +2010-10-08 Werner Koch + + * configure.ac: Add option --enable-dirmngr-auto-start. + (USE_DIRMNGR_AUTO_START): New ac_define. + * autogen.sh <--build-w32ce>: Use new option. + +2010-10-06 Werner Koch + + * configure.ac: Make --enable-standard-socket the default. + +2010-10-04 Werner Koch + + * configure.ac (GNUPG_CHECK_FAQPROG): Remove. + +2010-08-19 Werner Koch + + * configure.ac (AH_BOTTOM): Define GPG_ERR_ENABLE_ERRNO_MACROS. + +2010-08-09 Werner Koch + + * configure.ac (inet_pton): Check for it. + +2010-08-05 Werner Koch + + * configure.ac (AH_BOTTOM): Remove HTTP_USE_ESTREAM. + +2010-08-02 Werner Koch + + * configure.ac: Require libksba 1.1.0 due to the use of + ksba_reader_set_release_notify. + +2010-07-30 Werner Koch + + * configure.ac (GNUPG_PTH_PATH) [W32]: Require version 2.0.3. + +2010-07-25 Werner Koch + + * configure.ac (USE_LDAPWRAPPER): AC_DEFINE and AM_CONDITIONAL it. + +2010-06-09 Werner Koch + + * configure.ac (GNUPG_DIRMNGR_LDAP_PGM): Add option + --with-dirmngr-ldap-pgm. + + * am/cmacros.am (-DGNUPG_LOCALSTATEDIR): New. + (GNUPG_DEFAULT_DIRMNGR_LDAP): New. + +2010-06-08 Werner Koch + + * configure.ac: Add build support for dirmngr. + (try_ldap): Rename to try_ks_ldap. + (GNUPG_CHECK_LDAP): Also test if dirmngr is to be build. + + * Makefile.am (SUBDIRS): Add dirmngr. + +2010-06-07 Werner Koch + + * dirmngr/: New. + + * configure.ac: Add option --enable-gpgtar. + +2010-05-31 Werner Koch + + * configure.ac (AC_CHECK_FUNCS): Check for lstat. + +2010-04-30 Werner Koch + + * configure.ac: Add option --enable-standard-socket. + (USE_STANDARD_SOCKET): ac_define it. + +2010-04-14 Werner Koch + + * Makefile.am (keyserver) [W32CE]: Do not build for now. + + * configure.ac (use_zip): New. + (--disable-zip): New option. + (HAVE_ZIP): New. + * autogen.sh : Disable ZIP. + +2010-04-07 Werner Koch + + * autogen.sh: Take a .gnupg-autogen.rc file in account. + + * gl/mkdtemp.c (getpid) [W32CE]: New macro. + +2010-03-24 Werner Koch + + * configure.ac (AH_BOTTOM): Use /gnupg as the default homedir on + dosish systems which don't support drive letters (e.g. W32CE). + + * am/cmacros.am (extra_sys_libs): New. + +2010-03-23 Werner Koch + + * configure.ac (W32SOCKLIBS): Change value for W32CE. + +2010-03-12 Werner Koch + + * configure.ac (AC_INIT): Prepare for using git. + +2010-03-10 Werner Koch + + * jnlib/: Move all code to common/. + * Makefile.am (SUBDIRS): Remove jnlib. + * configure.ac (AC_CONFIG_FILES): Remove jnlib/Makefile. + + * configure.ac (AM_PATH_LIBASSUAN): Remove double test. + * acinclude.m4 (GNUPG_CHECK_ENDIAN): Remove bogus warning. + +2010-03-09 Werner Koch + + * configure.ac: Add option --disable-ccid-driver. + (AH_BOTTOM): Define GPG_ERR_ENABLE_GETTEXT_MACROS. + +2010-02-26 Werner Koch + + * gl/mkdtemp.c (__set_errno) [W32CE]: Use gpg_err_set_errno. + * gl/setenv.c (__set_errno) [W32CE]: Ditto. + * gl/unsetenv.c (__set_errno) [W32CE]: Ditto. + + * configure.ac (HAVE_W32CE_SYSTEM): New ac_define and + am_conditional. + (signal.h, getenv): Check for them. + + * autogen.sh: New option --build-w32ce. Remove obsolete option + --without-included-gettext. + +2009-12-08 Werner Koch + + * configure.ac (USE_DNS_CERT): Support ADNS. + +2009-12-07 Werner Koch + + * configure.ac: Check for ADNS before checking for the BIND + resolver. + (USE_ADNS): Fallback macro for PKA and CERT lookups. + +2009-10-20 Marcus Brinkmann + + * configure.ac: Check for fusermount and encfs. + +2009-10-16 Marcus Brinkmann + + * configure.ac: Check for libassuan instead of libassuan-pth. + +2009-10-12 Werner Koch + + * configure.ac: Use -O3 because newer gcc versions require that + for uninitialized variable warnings. + +2009-09-23 Werner Koch + + * configure.ac (HAVE_ASSUAN_SET_IO_MONITOR): Remove test. + (_ASSUAN_ONLY_GPG_ERRORS): Remove. + +2009-09-23 Marcus Brinkmann + + * configure.ac (NEED_LIBASSUAN_API, NEED_LIBASSUAN_VERSION): + Update to new API (2, 1.1.0). + +2009-09-21 Werner Koch + + Start a new development branch in the SVN trunk. The stable one + is now known in the SVN as branches/GNUPG-STABLE-2-0. + +2009-09-04 Werner Koch + + Release 2.0.13. + +2009-06-29 Werner Koch + + * configure.ac: Take care of --without-adns. Suggested by + Arfrever Frehtes Taifersar Arahesis. + +2009-06-17 Werner Koch + + Release 2.0.12. + +2009-06-05 David Shaw + + * configure.ac: Remove Camellia restriction. + +2009-04-01 Werner Koch + + * configure.ac: Test for fsync. + +2009-03-18 Werner Koch + + * configure.ac: Test for getrlimit. + +2009-03-03 Werner Koch + + Release 2.0.11. + +2009-01-12 Werner Koch + + Release 2.0.10. + +2008-12-09 Werner Koch + + Release 2.0.10rc1. + +2008-10-17 Werner Koch + + * configure.ac: Use more warning options with modern GCCs. + +2008-09-29 Werner Koch + + * configure.ac: Require libgcrypt 1.4. + +2008-08-27 David Shaw + + * configure.ac: Use printf for the most portable SVN version + detection. + + * configure.ac: Darwin's /bin/sh has a builtin echo that doesn't + understand '-n'. Use tr to trim the carriage return instead. + +2008-04-23 Werner Koch + + * configure.ac: Call gl_HEADER_SYS_SOCKET and gl_TYPE_SOCKLEN_T. + +2008-04-07 Werner Koch + + * configure.ac (ADNSLIBS): Test for adns. + (GPGKEYS_KDNS): New. + +2008-04-01 Werner Koch + + * configure.ac: Require curl 7.10 (Oct 1 2002) or later as we use + curl_version_info(). + (AC_INIT): Fix quoting. + +2008-03-27 Werner Koch + + * Makefile.am (dist_doc_DATA): New. Install README. + +2008-03-26 Werner Koch + + Release 2.0.9. + +2008-02-19 Werner Koch + + * configure.ac: Remove --with-pkits-tests. + +2008-02-15 Werner Koch + + * gl/allocsa.h, gl/m4/allocsa.m4: Replace HAVE_LONG_LONG by + HAVE_LONG_LONG_INT. + +2008-02-15 gettextize + + * configure.ac (AM_GNU_GETTEXT_VERSION): Bump to 0.17. + +2007-12-20 Werner Koch + + Released 2.0.8. + +2007-12-17 Werner Koch + + * configure.ac: Add treatment for HAVE_LDAP_START_TLS_SA. + +2007-12-14 Werner Koch + + Released 2.0.8rc1. + +2007-12-12 Werner Koch + + * configure.ac (USE_CAMELLIA): Define by new option --enable-camellia. + +2007-12-03 Werner Koch + + * configure.ac: Add test gt_LC_MESSAGES.. + +2007-10-01 Werner Koch + + * configure.ac: Require assuan 1.0.4. + +2007-09-14 Werner Koch + + * configure.ac (GNUPG_LIBASSUAN_VERSION): New. + +2007-09-10 Werner Koch + + Released 2.0.7. + +2007-08-27 Werner Koch + + * configure.ac: Remove remaining support for internal regex. + Define DISABLE_REGEX automake conditional. Add option + --with-regex. + * autogen.sh [--build-w32]: Remove --disable-regex. Use --with-regex. + +2007-08-16 Werner Koch + + Released 2.0.6. + +2007-08-08 Werner Koch + + * configure.ac: Use AC_CANONICAL_HOST and not AC_CANONICAL_TARGET. + +2007-07-09 Werner Koch + + * configure.ac (AM_ICONV): Check for it even when building without + NLS. + +2007-07-05 Werner Koch + + Released 2.0.5. + + * configure.ac: Require libassuan 1.0.2. + +2007-07-05 Marcus Brinkmann + + * configure.ac: Invoke AM_LANGINFO_CODESET. + +2007-07-04 Werner Koch + + * Makefile.am (AUTOMAKE_OPTIONS): Add no-dist-gzip. + + Switched entire package to GPLv3+. + + * configure.ac: Require libksba 1.0.2. + + * COPYING: Updated to GPLv3. + * COPYING.LIB: New as jnlib/ uses this license. + + * gl/: Switched to GPLv3+. + + * intl/ Removed. + * configure.ac (AM_GNU_GETTEXT): Add external flag. + (AM_ICONV): New. + +2007-07-03 Werner Koch + + * configure.ac [W32]: Use ws2_32 instead of wsock32. + +2007-06-25 Werner Koch + + * gl/mkdtemp.c (gen_tempname) [MKDIR_TAKES_ONE_ARG]: Avoid + compiler warning by using the proper config macro. + +2007-06-15 Werner Koch + + * configure.ac: Call AM_PO_SUBDIRS. + (W32SOCKLIBS): New. + + * autogen.sh: Use = and not == in test to be POSIXly correct. + : Disable use of regex. + +2007-06-14 Werner Koch + + * configure.ac [AH_BOTTOM]: Remove the hardwired names of modules. + +2007-06-12 Werner Koch + + * configure.ac [AH_BOTTOM]: Define HTTP_NO_WSASTARTUP. + +2007-06-11 Werner Koch + + * am/cmacros.am (libcommonstd, libcommonpth, libcommonstd_ldadd) + (libcommonpth_ldadd): Add macros. + +2007-06-06 Werner Koch + + * configure.ac: Add a few notices message so make browsing of the + log file easier. + (CC_FOR_BUILD): New. + +2007-05-30 Werner Koch + + * configure.ac [W32]: Do not create a symlink to w32-pth.h. + Require the installation of the w32pth package. + +2007-05-29 Werner Koch + + * gl/: Updated to a newer version. + +2007-05-24 Werner Koch + + * configure.ac: Use -Wpointer-arith is possible. + +2007-05-15 Werner Koch + + * configure.ac: Renamed the estream macros. Remove -Wformat-nonliteral. + + * configure.ac: Call ESTREAM_INIT and define the memory + allocators for estream_asprintf. + (gl_MODULES): Remove vasprintf. + +2007-05-09 Werner Koch + + Released 2.0.4. + +2007-05-07 Werner Koch + + * configure.ac: Require libgcrypt 1.2.2 to avoid compiler warnings. + +2007-05-07 gettextize + + * configure.ac (AM_GNU_GETTEXT_VERSION): Bump to 0.16.1. + +2007-05-07 Werner Koch + + * configure.ac: Bail out if no C-89 compiler has been found. + +2007-05-04 Werner Koch + + * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Add --enable-mailto + + * configure.ac: Require automake 1.10 + (AC_CONFIG_FILES): Rename gpgkeys_ to gpg2keys_. + (AM_PROG_CC_C_O): New. + +2007-03-08 Werner Koch + + Released 2.0.3. + + * autogen.sh: Add option --force. + +2007-01-31 Werner Koch + + Released 2.0.2. + +2006-11-30 Werner Koch + + * configure.ac: Save original LIBS when testing for dlopen. + +2006-11-28 Werner Koch + + Released 2.0.1. + +2006-11-23 Werner Koch + + Released 2.0.1rc1. + +2006-11-21 Werner Koch + + * configure.ac [AH_BOTTOM]: Disable PTH soft mapping. + (AC_CHECK_SIZEOF): Check for time_t. + (BUILD_INCLUDED_LIBINTL): Remove AM_PO_SUBDIRS as it is not + required for C. + +2006-11-15 Werner Koch + + * autogen.sh: Add convenience option --build-amd64. + +2006-11-14 Werner Koch + + * configure.ac (HAVE_ASSUAN_SET_IO_MONITOR): Test for it. + +2006-11-11 Werner Koch + + Released 2.0.0. + +2006-11-06 Werner Koch + + Released 1.9.95. + +2006-11-03 Werner Koch + + * configure.ac: Test for pty.h. From Gentoo. + +2006-10-24 Werner Koch + + Released 1.9.94. + +2006-10-20 Werner Koch + + * Makefile.am (stowinstall): Add convenience target. + +2006-10-18 Werner Koch + + * configure.ac: svn revison magic fixes for old bashs. Suggested + by Alain Guibert. + +2006-10-18 Werner Koch + + Released 1.9.93. + +2006-10-17 Werner Koch + + * autogen.sh <--build-w32>: Test also for a host "mingw32". + + * configure.ac: Removed W32LIBS. Use NETLIBS instead. + +2006-10-11 Werner Koch + + Released 1.9.92. + + * configure.ac: Require libassuan 0.9.3. + +2006-10-09 Werner Koch + + * acinclude.m4: Moved pth check to m4/gnupg-pth.m4. + +2006-10-06 Werner Koch + + * configure.ac: Also check for libassuan's pth version. + +2006-10-04 Werner Koch + + Released 1.9.91. + + * configure.ac: Require libassuan 0.9.1 which fixes a problem with + gpgsm. + +2006-09-27 Werner Koch + + * gl/strsep.h, gl/strsep.c, gl/m4/strsep.m4: Removed. + * gl/strpbrk.h, gl/strpbrk.c, gl/m4/strpbrk.m4: Removed. + * gl/Makefile.am: Removed module strsep and strpbrk. + * configure.ac: Check for strsep in the context of jnlib. Remove + check from gl_MODULES. Moved check for timegm into the jnlib context. + +2006-09-27 Marcus Brinkmann + + * Makefile.am: Fix cut & paste error. + +2006-09-25 Werner Koch + + Released 1.9.90. + +2006-09-22 Werner Koch + + * AUTHORS: Add information about used licenses. + +2006-09-20 Werner Koch + + * Makefile.am (dist-hook): Removed distfiles cruft. + (SUBDIRS): Added include + +2006-09-18 Werner Koch + + Released 1.9.23. + + * configure.ac (--enable-agent-only): Donot build tools and doc + (--disable-tools,--disable-doc): New. + * Makefile.am (SUBDIRS): Allow to conditional build tools and doc. + +2006-09-14 Werner Koch + + Replaced all call gpg_error_from_errno(errno) by + gpg_error_from_syserror(). + + * configure.ac: Build gpg by default. + (GNUPG_SYS_SO_PEERCRED): Removed. + +2006-09-13 Werner Koch + + * autogen.sh: Better detection of the cross compiler kit. + +2006-09-06 Marcus Brinkmann + + * configure.ac: New automake conditional RUN_GPG_TESTS. + +2006-09-06 Werner Koch + + * configure.ac: Define _ASSUAN_ONLY_GPG_ERRORS. Require Assuan + 0.9 and libgpg-error 1.4. + +2006-08-31 Werner Koch + + * configure.ac: Require libksba 1.0 and added API check for it. + (GPG_ERR_LOCKED): Removed DECL check as we require 1.2 anyway. + (have_libusb): New to give a feedback about CCID support + +2006-08-21 Werner Koch + + * configure.ac: Removed docbook tests. + (AC_CONFIG_FILES): Added gpgkeys_test and gpgkeys_mailto. + + * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Enable gpg. + +2006-08-17 Werner Koch + + * THANKS: Merged with the 1.4 one. + +2006-08-16 Werner Koch + + * configure.ac: Removed test for capabilities and mlock. + +2006-08-15 Werner Koch + + * Makefile.am (keyserver): Enable building of keyserver helpers. + + * configure.ac: Merged with the current configure from 1.4.5. + Require libgpg-error 1.2 and libksba 0.9.16. + +2006-07-29 Marcus Brinkmann + + * README: Spelling fixes. + +2006-07-27 Werner Koch + + Released 1.9.22. + + * configure.ac: Call AB_INIT. + +2006-07-03 Werner Koch + + * configure.ac: Test for ksba_dn_teststr. + +2006-06-30 Werner Koch + + * keyserver/: New. Taken from 1.4.4 + * Makefile.am (SUBDIRS): Include keyserver/. + * configure.ac: Include keyserver/. + (FAKE_CURL, GPGKEYS_CURL): New. + +2006-06-20 Werner Koch + + Released 1.9.21. + +2006-06-08 Marcus Brinkmann + + * configure.ac (PTH_LIBS): Add --all to pth-config invocation. + +2006-05-24 Werner Koch + + * configure.ac: New option --disable-optimization taked from 1.4.3. + +2006-05-23 Werner Koch + + * configure.ac (ZLIBS): New for zlib link commands. Add bzip2 + support. + +2006-05-22 Werner Koch + + * configure.ac (EXEEXT): New. + +2006-04-18 Werner Koch + + * configure.ac (PK_UID_CACHE_SIZE): New. + +2006-04-07 Werner Koch + + * configure.ac: Use new method to include the SVN revison. Now it + is the actual global revision number. + +2005-12-20 Werner Koch + + Released 1.9.20. + +2005-11-28 Werner Koch + + * configure.ac: Append the revision to the version string. + +2005-11-13 Werner Koch + + * am/cmacros.am (-DGNUPG_SYSCONFDIR): Define it. + +2005-11-11 Werner Koch + + * configure.ac (NEED_KSBA_VERSION: Require 0.9.13. + +2005-09-12 Werner Koch + + Released 1.9.19. + +2005-08-01 Werner Koch + + Released 1.9.18. + + * configure.ac: Require libksba 0.9.12 to match new features in gpgsm. + +2005-06-20 Werner Koch + + Released 1.9.17. + +2005-06-02 Werner Koch + + * configure.ac (HAVE_PTH): Define as alias for USE_GNU_PTH. It is + used by common/estream.c. + +2005-06-01 Werner Koch + + * configure.ac (gl_INIT): Add gnulib stuff. + (fseeko, ftello, ttyname, isascii): Replaced the AC_REPLACE_FUNCS + by a simple check. + (putc_unlocked): Removed check. Not used. + (strsep, mkdtemp, asprintf): Replaced checks by gnulib checks. + (xsize): Added will probably come handy soon. + (CFLAGS): Use -Wformat-security instead of + -Wformat-nonliteral. Add --Wno-format-y2k. + * gl/, gl/m4/: New. + +2005-05-15 Werner Koch + + * configure.ac: Remove option --disable-threads; require the use + of GNU Pth. + +2005-04-27 Werner Koch + + * configure.ac: Removed OpenSC detection and options. + * acinclude.m4: Ditto. + +2005-04-21 Werner Koch + + Released 1.9.16. + + * configure.ac: Do not build gpg by default. + +2005-04-20 Werner Koch + + * configure.ac: Test whether GPG_ERR_LOCKED is declared and + provide a replacement if not. + +2005-04-15 Werner Koch + + * configure.ac: Require libksba 0.9.11. + +2005-04-15 Marcus Brinkmann + + * configure.ac: Check for /usr/bin/shred and define SHRED. + + * configure.ac: Add --enable-symcryptrun, disabled by default. + Define automake variable BUILD_SYMCRYPTRUN. + Check for openpty -lutil, define LIBUTIL_LIBS. + +2005-03-03 Werner Koch + + * acinclude.m4 (GNUPG_PTH_VERSION_CHECK): Accidently used + --ldflags instead of --cflags. Reported by Kazu Yamamoto. + +2005-02-03 Werner Koch + + * AUTHORS: Copied from 1.4 and edited to refelct the changes in + 1.9. + +2005-01-17 Werner Koch + + * configure.ac: Make --without-included-regex work as expected. + Fixed FTP location info for some libraries. + +2005-01-13 Werner Koch + + Released 1.9.15. + + * acinclude.m4 (GNUPG_PTH_VERSION_CHECK): Link a simple test + program to see whether the installation is sane. + +2005-01-07 Werner Koch + + * configure.ac: Require gpg-error 1.0. + +2005-01-04 Werner Koch + + * configure.ac: Remove hack not to build gpg2 for W32. + * autogen.sh : Pass option --disable-gpg instead. + +2004-12-22 Werner Koch + + Released 1.9.14. + +2004-12-20 Werner Koch + + * configure.ac: Add PATHSEP_C and PATHSEP_S. For W32 let all + directories default to c:/gnupg. Require libassuan 0.6.9. + +2004-12-18 Werner Koch + + * configure.ac (AH_BOTTOM): Define EXEEXT_S. + + * autogen.sh: Updated --build-w32 feature. + +2004-12-15 Werner Koch + + * Makefile.am (SUBDIRS) [W32]: Do not build in tests/. + + * acinclude.m4: Add proper macro name quoting for use with + automake 1.9. + + * configure.ac: Add replacement check for ttyname. + Removed support for a included zlib. + +2004-12-06 Werner Koch + + * configure.ac (have_w32_system): New. Disable Pth checks for W32. + Link jnlib/w32-pth.h to pth.h. + +2004-12-03 Werner Koch + + Released 1.9.13. + +2004-11-26 Werner Koch + + * configure.ac: Replace strsep. Replaced use of "target" by + "host". + +2004-10-22 Werner Koch + + Released 1.9.12. + + * Makefile.am (AUTOMAKE_OPTIONS): Set option to create bzip2 tarball. + +2004-10-01 Werner Koch + + Released 1.9.11. + +2004-09-30 Werner Koch + + * README: Minor updates. + +2004-09-30 gettextize + + * configure.ac (AM_GNU_GETTEXT_VERSION): Bump to 0.14.1. + +2004-08-16 Werner Koch + + * configure.ac: Build Makefile for tests/pkits. New option + --with-pkits-tests. + +2004-08-05 Werner Koch + + * configure.ac: Changed tests for libusb to also suuport the + stable version 0.1.x. + +2004-07-22 Werner Koch + + Released 1.9.10. + + * configure.ac: Define AM conditional HAVE_OPENSC. + +2004-07-21 Werner Koch + + * configure.ac: Don't set DIE to no after it might has been set to + yes. + +2004-07-20 Werner Koch + + * Makefile.am (sm): Build kbx only if gpgsm is to be build. + +2004-07-20 Werner Koch + + * configure.ac: New option --enable-agent-only. + +2004-06-08 Werner Koch + + Released 1.9.9. + +2004-06-06 Werner Koch + + * configure.ac: Require libksba 0.9.7. + +2004-04-29 Werner Koch + + Released 1.9.8. + +2004-04-20 Werner Koch + + * configure.ac: Remove the fopencookie test. We don't need the + dummy function because we conditionally use fopencookie, + fpencookie or a replacement at place. + +2004-04-02 Thomas Schwinge + + * autogen.sh: Added ACLOCAL_FLAGS. + +2004-04-06 Werner Koch + + Released 1.9.7. + + * configure.ac: Require libgcrypt 1.1.94. + Introduce PACKAGE_GT and set it to gnupg2. + +2004-03-23 Werner Koch + + * configure.ac: Define SAFE_VERSION_DASH and SAFE_VERSION_DOT. + +2004-03-09 Werner Koch + + * configure.ac (NEED_GPG_ERROR_VERSION): Set to 0.7. + +2004-03-06 Werner Koch + + Released 1.9.6. + + * configure.ac: Check the Libgcrypt API. + +2004-02-25 Werner Koch + + * configure.ac: New option --disable-threads to inhibit + unintentional builds without Pth. + +2004-02-21 Werner Koch + + Released 1.9.5. + +2004-02-20 Werner Koch + + * configure.ac: Fixed URLs in the notice messages. + +2004-02-18 Werner Koch + + * acinclude.m4: Removed macros to detect gpg-error, libgcrypt, + libassuan and ksba as they are now distributed in m4/. + +2004-02-13 Werner Koch + + * configure.ac: Require libksba 0.9.4 and libgcrypt 1.1.92. + +2004-02-12 Werner Koch + + * autogen.sh: Removed cruft from debugging. + + * am/cmacros.am: New. + +2004-02-11 Werner Koch + + * configure.ac: Removed the need for g10defs.h. Reworked the + --with-foo-pgm stuff. + + * autogen.sh (check_version): Removed bashism and simplified. + * acinclude.m4 (AM_PATH_OPENSC): Kludge to avoid error output for + a bad opensc-config. + +2004-01-30 Werner Koch + + Released 1.9.4. + + * configure.ac: Require libksba 0.9.3 due to another bug fix there. + +2004-01-29 Werner Koch + + * README: Updated. + + * configure.ac: Require libksba 0.9.2 due to bug fixes. + +2004-01-24 Werner Koch + + * configure.ac: Now requires libassuan 0.6.3. + +2003-12-23 Werner Koch + + Released 1.9.3. + + * README-alpha: Removed. + * configure.ac, Makefile.am: Add the tests and tools directories. + +2003-12-19 Werner Koch + + * configure.ac: Now require libgcrypt 1.1.91 to help testing the + latest libgcrypt changes. Requires libksab 0.9.1. + +2003-12-17 Werner Koch + + * configure.ac: Requires now libassuan 0.6.2. + (CFLAGS): Add --Wformat-noliteral in gcc mode. + +2003-12-16 Werner Koch + + * configure.ac: Check for funopen and fopencookie as part of the + jnlib checks. + +2003-12-09 Werner Koch + + * configure.ac: Add a min_automake_version. + * README.CVS: New. + * autogen.sh: Revamped except for the --build-w32 hack. + * Makefile.am: Add README.CVS + +2003-11-17 Werner Koch + + Release 1.9.2. + + * configure.ac: Requires now libassuan 0.6.1. + +2003-10-31 Werner Koch + + * configure.ac (NEED_KSBA_VERSION): Set to 0.9.0 due the changed + time interface. + +2003-10-21 Werner Koch + + * configure.ac (PRINTABLE_OS_NAME): Remove special case for The + Hurd; Robert Millan reported that the uname test is now + sufficient. + +2003-10-01 Werner Koch + + * configure.ac (AH_BOTTOM): Define GNUPG_MAJOR_VERSION. + +2003-09-23 Werner Koch + + Merged most of David Shaw's changes in 1.3 since 2003-06-03. + + * configure.ac: Drop all TIGER/192 support. + (uint64_t): Check for UINT64_C to go along with uint64_t. + (getaddrinfo): Check for it. + (sigset_t): Check for sigset_t and struct sigaction. This is for + Forte c89 on Solaris which seems to define only the function call + half of the two pairs by default. + (W32LIBS): Include wsock32 in W32LIBS. This is different from + NETLIBS so we don't need to force other platforms to pull in the + netlibs when they aren't actually needed. + +2003-09-06 Werner Koch + + Released 1.9.1. + + * configure.ac: Require newer versions of some libraries. + +2003-09-02 Werner Koch + + * configure.ac (HAVE_LIBUSB): Added a simple test for libusb. + +2003-08-19 Marcus Brinkmann + + * configure.ac (AM_PATH_GPG_ERROR): Add missing comma in + invocation. + +2003-08-06 Werner Koch + + * configure.ac: Check for libgpg-error. Print infos about missing + libraries more nicely. + * acinclude.m4 (AM_PATH_GPG_ERROR): Added. + +2003-08-05 Werner Koch + + Released 1.9.0. + + * configure.ac (GNUPG_DEFAULT_HONMEDIR): Changed back to ~/.gnupg. + +2003-07-31 Werner Koch + + * Makefile.am (DISTCLEANFILES): Add g10defs.h + +2003-06-18 Werner Koch + + * configure.ac (GNUPG_DEFAULT_HOMEDIR): Changed temporary to + .gnupg2 to avoid accidential use with production keys. + +2003-06-11 Werner Koch + + * configure.ac: Merged all stuff from current 1.3 version in. + * acinclude.m4: Merged required macros from current 1.2 version in. + +2003-06-04 Werner Koch + + * configure.ac, Makefile.am: Enable building of gpg. + +2003-04-29 Werner Koch + + * configure.ac: Build a limited version of scdaemon if libopensc + is not available. + + * configure.ac (ALL_LINUGAS): Removed. + + * Makefile.am (ACLOCAL_AMFLAGS): New. + * configure.ac (AM_GNU_GETTEXT_VERSION): New. Set to 0.11.5. + +2003-04-29 gettextize + + * Makefile.am (SUBDIRS): Add m4. + (ACLOCAL_AMFLAGS): New variable. + (EXTRA_DIST): Add scripts/config.rpath. + * configure.ac (AC_CONFIG_FILES): Add m4/Makefile. + +2003-04-29 Werner Koch + + * assuan/ : Removed. We now use libassuan. + * Makefile.am (SUBDIRS): Removed assuan + + * configure.ac: Check for libassuan. + +2003-01-09 Werner Koch + + * configure.ac (GNUPG_PROTECT_TOOL): New option --with-protect-tool. + (NEED_KSBA_VERSION): Does now require 0.4.6. + + * README: Noted where to find gpg-protect-tool. + +2002-10-31 Neal H. Walfield + + * configure.ac: Check for flockfile and funlockfile. Check for + isascii and putc_unlocked replacing them if not found. + + * configure.ac (PTH_LIBS): If pth is found, add the output of + `$PTH_CONFIG --ldflags`, not just `$PTH_CONFIG --libs`. + +2002-10-19 Werner Koch + + * configure.ac: Bumped version number to GnuPG 1.9.0-cvs. + + NewPG (Aegypten project) to GnuPG merge. + +2002-09-20 Werner Koch + + Released NewPG 0.9.2. + +2002-09-05 Neal H. Walfield + + * configure.ac: Check for makeinfo. + +2002-09-03 Neal H. Walfield + + * autogen.sh (have_version): New function. Generalize and + simplify logic for finding and determining the versions of GNU + programs. Use it. + +2002-08-23 Werner Koch + + Released NewPG 0.9.1. + + * acinclude.m4 (AM_PATH_LIBGCRYPT): Updated from Libgcrypt. + (AM_PATH_OPENSC): Strip non-digits from the micro version. + +2002-08-21 Werner Koch + + Released NewPG 0.9.0. + + * configure.ac: Changed the default homedir to .gnupg. + * README-alpha: Removed. + +2002-08-19 Werner Koch + + * acinclude.m4: Removed -lpcsclite from KSBA_LIBS; copy+paste bug. + +2002-08-13 Werner Koch + + * acinclude.m4 (AM_PATH_OPENSC, AM_PATH_KSBA): New. + * configure.ac: Use them. + +2002-08-10 Werner Koch + + Released NewPG 0.3.10. + + * configure.ac (NEED_LIBKSBA_VERSION): Require 0.4.4. Add support + for gettext. + +2002-07-22 Werner Koch + + * configure.ac: Check for ftello and provide a replacement. + +2002-07-01 Werner Koch + + Released NewPG 0.3.9. + + * README: Short note on how to export in pkcs-12 format. + +2002-06-29 Werner Koch + + * configure.ac: Define --with options to set the default location + of the agent, scdaemon, pinentry and dirmngr. + +2002-06-27 Werner Koch + + * README: Short blurb on how to import a PKCS-12 file. + + * configure.ac (AH_BOTTOM): New to define some constants. + +2002-06-25 Werner Koch + + Released NewPG 0.3.8. + + * configure.ac (NEED_LIBGCRYPT_VERSION): Set to 1.1.8. + +2002-06-12 Werner Koch + + * configure.ac (NEED_LIBKSBA_VERSION): We need 0.4.3 now. + +2002-06-04 Werner Koch + + Released NewPG 0.3.7. + +2002-05-21 Werner Koch + + * configure.ac: We now require libgcrypt 1.1.7 and libksba 0.4.2. + +2002-05-14 Werner Koch + + * doc/: New + * configure.ac, Makefile.am: Added doc/. + +2002-05-03 Werner Koch + + Released NewPG 0.3.6. + +2002-04-25 Werner Koch + + * configure.ac: Check for setlocale. + +2002-04-24 Marcus Brinkmann + + * configure.ac: Check for locale.h. + +2002-04-15 Werner Koch + + Released NewPG 0.3.5. + + * NEWS: Started to describe release notes. + + * configure.ac (NEED_LIBKSBA_VERSION, NEED_LIBGCRYPT_VERSION): Defined + +2002-04-01 Werner Koch + + Released NewPG 0.3.4. + +2002-03-18 Werner Koch + + Released NewPG 0.3.3. + +2002-03-08 Werner Koch + + * README: Add some explanation on how to specify a user ID. + +2002-03-06 Werner Koch + + Released NewPG 0.3.2. + +2002-03-04 Werner Koch + + Released NewPG 0.3.1. + + * README: Explained some options and files. + +2002-02-14 Werner Koch + + * configure.ac: Fixed status messages related to presence of Pth. + +2002-02-13 Werner Koch + + * acinclude.m4 (GNUPG_SYS_SO_PEERCRED): New. + * configure.ac: use it. + +2002-02-12 Werner Koch + + * configure.ac: Check for PTH. Provide replacement fucntions for + apsrintf and fopencookie. + + * acinclude.m4 (GNUPG_PTH_VERSION_CHECK): New. + +2002-02-07 Werner Koch + + Released NewPG 0.3.0. + + * configure.ac: Require libgcrypt 1.1.6. + +2002-02-01 Marcus Brinkmann + + * configure.ac (KSBA_CONFIG): Remove superfluous x in front of + variable. + +2002-01-26 Werner Koch + + * configure.ac: Add options to disable the build of some programs + and print a configure status at the end. + * acinclude.m4 (GNUPG_BUILD_PROGRAM): New. + + * scd/ : New. Added to Makefile and configure. + * configure.ac: Check for libopensc + * Makefile.am: Build scd only when libopensc is available + +2002-01-23 Werner Koch + + * configure.ac (mkdtemp): See whether we have to provide a + replacement. + +2001-12-18 Werner Koch + + Released NewPG 0.0.0. + +2001-12-17 Werner Koch + + * acinclude.m4: Add AM_PATH_LIBGCRYPT macro. + * configure.ac: and use it here. Figure out the location of libksba + +2001-12-15 Werner Koch + + * configure.ac (missing_dir): Bail out if asprintf and fopencookie + are not available. + +2001-12-04 Werner Koch + + * configure.ac (HAVE_JNLIB_LOGGING): always define it. + + + Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007. + 2010 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..5458714 --- /dev/null +++ b/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..735c72b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,147 @@ +# Makefile.am - main makefile for GnuPG +# Copyright (C) 2001, 2004, 2010 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 3 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, see . + +## Process this file with automake to produce Makefile.in + +ACLOCAL_AMFLAGS = -I m4 +DISTCHECK_CONFIGURE_FLAGS = --enable-symcryptrun --enable-g13 \ + --enable-gpg2-is-gpg + +GITLOG_TO_CHANGELOG=gitlog-to-changelog + +EXTRA_DIST = build-aux/config.rpath build-aux/potomo autogen.sh autogen.rc \ + ChangeLog-2011 po/ChangeLog-2011 build-aux/ChangeLog-2011 \ + VERSION README.GIT build-aux/gitlog-to-changelog \ + COPYING.CC0 COPYING.other \ + build-aux/git-log-fix build-aux/git-log-footer \ + build-aux/getswdb.sh \ + build-aux/speedo.mk \ + build-aux/speedo/zlib.pc \ + build-aux/speedo/w32/inst-options.ini \ + build-aux/speedo/w32/inst.nsi \ + build-aux/speedo/w32/pkg-copyright.txt \ + build-aux/speedo/w32/g4wihelp.c \ + build-aux/speedo/w32/pango.modules \ + build-aux/speedo/w32/gdk-pixbuf-loaders.cache \ + build-aux/speedo/w32/exdll.h \ + build-aux/speedo/w32/README.txt \ + build-aux/speedo/w32/gnupg-logo-150x57.bmp \ + build-aux/speedo/w32/gnupg-logo-164x314.bmp \ + build-aux/speedo/patches/atk-1.32.0.patch \ + build-aux/speedo/patches/libiconv-1.14.patch \ + build-aux/speedo/patches/pango-1.29.4.patch \ + build-aux/speedo/patches/sqlite.patch + + +DISTCLEANFILES = g10defs.h + +if BUILD_GPG +gpg = g10 +else +gpg = +endif +if BUILD_GPGSM +sm = sm +else +sm = +endif +if BUILD_AGENT +agent = agent +else +agent = +endif +if BUILD_SCDAEMON +scd = scd +else +scd = +endif +if BUILD_G13 +g13 = g13 +else +g13 = +endif +if BUILD_DIRMNGR +dirmngr = dirmngr +else +dirmngr = +endif +if BUILD_TOOLS +tools = tools +else +tools = +endif +if BUILD_DOC +doc = doc +else +doc = +endif + +SUBDIRS = m4 common kbx \ + ${gpg} ${sm} ${agent} ${scd} ${g13} ${dirmngr} \ + ${tools} po ${doc} tests + +dist_doc_DATA = README + + +dist-hook: gen-ChangeLog + +distcheck-hook: + set -e; ( \ + pref="#+macro: gnupg21_" ;\ + reldate="$$(date -u +%Y-%m-%d)" ;\ + echo "$${pref}ver $(PACKAGE_VERSION)" ;\ + echo "$${pref}date $${reldate}" ;\ + list='$(DIST_ARCHIVES)'; for i in $$list; do \ + case "$$i" in *.tar.bz2) \ + echo "$${pref}size $$(wc -c <$$i|awk '{print int($$1/1024)}')k" ;\ + echo "$${pref}sha1 $$(sha1sum <$$i|cut -d' ' -f1)" ;\ + echo "$${pref}sha2 $$(sha256sum <$$i|cut -d' ' -f1)" ;;\ + esac;\ + done ) | tee $(distdir).swdb + + +if HAVE_W32_SYSTEM +install-data-hook: + set -e; \ + for i in $$($(top_srcdir)/build-aux/potomo \ + --get-linguas $(top_srcdir)/po) ; do \ + $(MKDIR_P) "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES" || true; \ + rm -f "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES/gnupg2.mo" \ + 2>/dev/null || true; \ + $(top_srcdir)/build-aux/potomo $(top_srcdir)/po/$$i.po \ + "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES/gnupg2.mo" ; \ + done +endif + + +gen_start_date = 2011-12-01T06:00:00 +.PHONY: gen-ChangeLog +gen-ChangeLog: + if test -e $(top_srcdir)/.git; then \ + (cd $(top_srcdir) && \ + $(GITLOG_TO_CHANGELOG) --append-dot --tear-off \ + --amend=build-aux/git-log-fix \ + --since=$(gen_start_date) ) > $(distdir)/cl-t; \ + cat $(top_srcdir)/build-aux/git-log-footer >> $(distdir)/cl-t; \ + rm -f $(distdir)/ChangeLog; \ + mv $(distdir)/cl-t $(distdir)/ChangeLog; \ + fi + + +stowinstall: + $(MAKE) $(AM_MAKEFLAGS) install prefix=/usr/local/stow/gnupg diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..0c0b63a --- /dev/null +++ b/Makefile.in @@ -0,0 +1,1045 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# 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. + +@SET_MAKE@ + +# Makefile.am - main makefile for GnuPG +# Copyright (C) 2001, 2004, 2010 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 3 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, see . + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = . +DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ + $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/configure $(am__configure_deps) \ + $(srcdir)/config.h.in $(top_srcdir)/build-aux/mkinstalldirs \ + ABOUT-NLS $(dist_doc_DATA) COPYING COPYING.LIB THANKS TODO \ + build-aux/compile build-aux/config.guess \ + build-aux/config.rpath build-aux/config.sub build-aux/depcomp \ + build-aux/install-sh build-aux/mdate-sh build-aux/missing \ + build-aux/mkinstalldirs build-aux/texinfo.tex \ + $(top_srcdir)/build-aux/compile \ + $(top_srcdir)/build-aux/config.guess \ + $(top_srcdir)/build-aux/config.rpath \ + $(top_srcdir)/build-aux/config.sub \ + $(top_srcdir)/build-aux/install-sh \ + $(top_srcdir)/build-aux/missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ + $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(docdir)" +DATA = $(dist_doc_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = m4 common kbx g10 sm agent scd g13 dirmngr tools po doc \ + tests +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +GZIP_ENV = --best +DIST_ARCHIVES = $(distdir).tar.bz2 +DIST_TARGETS = dist-bzip2 +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_FILEVERSION = @BUILD_FILEVERSION@ +BUILD_HOSTNAME = @BUILD_HOSTNAME@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BUILD_REVISION = @BUILD_REVISION@ +BUILD_TIMESTAMP = @BUILD_TIMESTAMP@ +BUILD_VERSION = @BUILD_VERSION@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DL_LIBS = @DL_LIBS@ +DNSLIBS = @DNSLIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENCFS = @ENCFS@ +EXEEXT = @EXEEXT@ +FUSERMOUNT = @FUSERMOUNT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@ +GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@ +GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@ +GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ +GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ +GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ +GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ +GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ +GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ +GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@ +GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KSBA_CFLAGS = @KSBA_CFLAGS@ +KSBA_CONFIG = @KSBA_CONFIG@ +KSBA_LIBS = @KSBA_LIBS@ +LBER_LIBS = @LBER_LIBS@ +LDAPLIBS = @LDAPLIBS@ +LDAP_CPPFLAGS = @LDAP_CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@ +LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@ +LIBASSUAN_LIBS = @LIBASSUAN_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ +LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBREADLINE = @LIBREADLINE@ +LIBS = @LIBS@ +LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBUTIL_LIBS = @LIBUTIL_LIBS@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NETLIBS = @NETLIBS@ +NPTH_CFLAGS = @NPTH_CFLAGS@ +NPTH_CONFIG = @NPTH_CONFIG@ +NPTH_LIBS = @NPTH_LIBS@ +NTBTLS_CFLAGS = @NTBTLS_CFLAGS@ +NTBTLS_CONFIG = @NTBTLS_CONFIG@ +NTBTLS_LIBS = @NTBTLS_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_GT = @PACKAGE_GT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHRED = @SHRED@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ +STRIP = @STRIP@ +SYSROOT = @SYSROOT@ +SYS_SOCKET_H = @SYS_SOCKET_H@ +TAR = @TAR@ +USE_C99_CFLAGS = @USE_C99_CFLAGS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +W32SOCKLIBS = @W32SOCKLIBS@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZLIBS = @ZLIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I m4 +DISTCHECK_CONFIGURE_FLAGS = --enable-symcryptrun --enable-g13 \ + --enable-gpg2-is-gpg + +GITLOG_TO_CHANGELOG = gitlog-to-changelog +EXTRA_DIST = build-aux/config.rpath build-aux/potomo autogen.sh autogen.rc \ + ChangeLog-2011 po/ChangeLog-2011 build-aux/ChangeLog-2011 \ + VERSION README.GIT build-aux/gitlog-to-changelog \ + COPYING.CC0 COPYING.other \ + build-aux/git-log-fix build-aux/git-log-footer \ + build-aux/getswdb.sh \ + build-aux/speedo.mk \ + build-aux/speedo/zlib.pc \ + build-aux/speedo/w32/inst-options.ini \ + build-aux/speedo/w32/inst.nsi \ + build-aux/speedo/w32/pkg-copyright.txt \ + build-aux/speedo/w32/g4wihelp.c \ + build-aux/speedo/w32/pango.modules \ + build-aux/speedo/w32/gdk-pixbuf-loaders.cache \ + build-aux/speedo/w32/exdll.h \ + build-aux/speedo/w32/README.txt \ + build-aux/speedo/w32/gnupg-logo-150x57.bmp \ + build-aux/speedo/w32/gnupg-logo-164x314.bmp \ + build-aux/speedo/patches/atk-1.32.0.patch \ + build-aux/speedo/patches/libiconv-1.14.patch \ + build-aux/speedo/patches/pango-1.29.4.patch \ + build-aux/speedo/patches/sqlite.patch + +DISTCLEANFILES = g10defs.h +@BUILD_GPG_FALSE@gpg = +@BUILD_GPG_TRUE@gpg = g10 +@BUILD_GPGSM_FALSE@sm = +@BUILD_GPGSM_TRUE@sm = sm +@BUILD_AGENT_FALSE@agent = +@BUILD_AGENT_TRUE@agent = agent +@BUILD_SCDAEMON_FALSE@scd = +@BUILD_SCDAEMON_TRUE@scd = scd +@BUILD_G13_FALSE@g13 = +@BUILD_G13_TRUE@g13 = g13 +@BUILD_DIRMNGR_FALSE@dirmngr = +@BUILD_DIRMNGR_TRUE@dirmngr = dirmngr +@BUILD_TOOLS_FALSE@tools = +@BUILD_TOOLS_TRUE@tools = tools +@BUILD_DOC_FALSE@doc = +@BUILD_DOC_TRUE@doc = doc +SUBDIRS = m4 common kbx \ + ${gpg} ${sm} ${agent} ${scd} ${g13} ${dirmngr} \ + ${tools} po ${doc} tests + +dist_doc_DATA = README +gen_start_date = 2011-12-01T06:00:00 +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +install-dist_docDATA: $(dist_doc_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-dist_docDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && $(MAKE) $(AM_MAKEFLAGS) distcheck-hook \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) config.h +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(docdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +@HAVE_W32_SYSTEM_FALSE@install-data-hook: +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-dist_docDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-dist_docDATA + +.MAKE: $(am__recursive_targets) all install-am install-data-am \ + install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-hook dist-lzip dist-shar dist-tarZ dist-xz \ + dist-zip distcheck distclean distclean-generic distclean-hdr \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-hook \ + install-dist_docDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-dist_docDATA + + +dist-hook: gen-ChangeLog + +distcheck-hook: + set -e; ( \ + pref="#+macro: gnupg21_" ;\ + reldate="$$(date -u +%Y-%m-%d)" ;\ + echo "$${pref}ver $(PACKAGE_VERSION)" ;\ + echo "$${pref}date $${reldate}" ;\ + list='$(DIST_ARCHIVES)'; for i in $$list; do \ + case "$$i" in *.tar.bz2) \ + echo "$${pref}size $$(wc -c <$$i|awk '{print int($$1/1024)}')k" ;\ + echo "$${pref}sha1 $$(sha1sum <$$i|cut -d' ' -f1)" ;\ + echo "$${pref}sha2 $$(sha256sum <$$i|cut -d' ' -f1)" ;;\ + esac;\ + done ) | tee $(distdir).swdb + +@HAVE_W32_SYSTEM_TRUE@install-data-hook: +@HAVE_W32_SYSTEM_TRUE@ set -e; \ +@HAVE_W32_SYSTEM_TRUE@ for i in $$($(top_srcdir)/build-aux/potomo \ +@HAVE_W32_SYSTEM_TRUE@ --get-linguas $(top_srcdir)/po) ; do \ +@HAVE_W32_SYSTEM_TRUE@ $(MKDIR_P) "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES" || true; \ +@HAVE_W32_SYSTEM_TRUE@ rm -f "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES/gnupg2.mo" \ +@HAVE_W32_SYSTEM_TRUE@ 2>/dev/null || true; \ +@HAVE_W32_SYSTEM_TRUE@ $(top_srcdir)/build-aux/potomo $(top_srcdir)/po/$$i.po \ +@HAVE_W32_SYSTEM_TRUE@ "$(DESTDIR)$(localedir)/$$i/LC_MESSAGES/gnupg2.mo" ; \ +@HAVE_W32_SYSTEM_TRUE@ done +.PHONY: gen-ChangeLog +gen-ChangeLog: + if test -e $(top_srcdir)/.git; then \ + (cd $(top_srcdir) && \ + $(GITLOG_TO_CHANGELOG) --append-dot --tear-off \ + --amend=build-aux/git-log-fix \ + --since=$(gen_start_date) ) > $(distdir)/cl-t; \ + cat $(top_srcdir)/build-aux/git-log-footer >> $(distdir)/cl-t; \ + rm -f $(distdir)/ChangeLog; \ + mv $(distdir)/cl-t $(distdir)/ChangeLog; \ + fi + +stowinstall: + $(MAKE) $(AM_MAKEFLAGS) install prefix=/usr/local/stow/gnupg + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..5633c55 --- /dev/null +++ b/NEWS @@ -0,0 +1,3243 @@ +Noteworthy changes in version 2.1.17 (2016-12-20) +------------------------------------------------- + + * gpg: By default new keys expire after 2 years. + + * gpg: New command --quick-set-expire to conveniently change the + expiration date of keys. + + * gpg: Option and command names have been changed for easier + comprehension. The old names are still available as aliases. + + * gpg: Improved the TOFU trust model. + + * gpg: New option --default-new-key-algo. + + * scd: Support OpenPGP card V3 for RSA. + + * dirmngr: Support for the ADNS library has been removed. Instead + William Ahern's Libdns is now source included and used on all + platforms. This enables Tor support on all platforms. The new + option --standard-resolver can be used to disable this code at + runtime. In case of build problems the new configure option + --disable-libdns can be used to build without Libdns. + + * dirmngr: Lazily launch ldap reaper thread. + + * tools: New options --check and --status-fd for gpg-wks-client. + + * The UTF-8 byte order mark is now skipped when reading conf files. + + * Fixed many bugs and regressions. + + * Major improvements to the test suite. For example it is possible + to run the external test suite of GPGME. + + +Noteworthy changes in version 2.1.16 (2016-11-18) +------------------------------------------------- + + * gpg: New algorithm for selecting the best ranked public key when + using a mail address with -r, -R, or --locate-key. + + * gpg: New option --with-tofu-info to print a new "tfs" record in + colon formatted key listings. + + * gpg: New option --compliance as an alternative way to specify + options like --rfc2440, --rfc4880, et al. + + * gpg: Many changes to the TOFU implementation. + + * gpg: Improve usability of --quick-gen-key. + + * gpg: In --verbose mode print a diagnostic when a pinentry is + launched. + + * gpg: Remove code which warns for old versions of gnome-keyring. + + * gpg: New option --override-session-key-fd. + + * gpg: Option --output does now work with --verify. + + * gpgv: New option --output to allow saving the verified data. + + * gpgv: New option --enable-special-filenames. + + * agent, dirmngr: New --supervised mode for use by systemd and alike. + + * agent: By default listen on all available sockets using standard + names. + + * agent: Invoke scdaemon with --homedir. + + * dirmngr: On Linux now detects the removal of its own socket and + terminates. + + * scd: Support ECC key generation. + + * scd: Support more card readers. + + * dirmngr: New option --allow-version-check to download a software + version database in the background. + + * dirmngr: Use system provided CAs if no --hkp-cacert is given. + + * dirmngr: Use a default keyserver if none is explicitly set + + * gpgconf: New command --query-swdb to check software versions + against an copy of an online database. + + * gpgconf: Print the socket directory with --list-dirs. + + * tools: The WKS tools now support draft version -02. + + * tools: Always build gpg-wks-client and install under libexec. + + * tools: New option --supported for gpg-wks-client. + + * The log-file option now accepts a value "socket://" to log to the + socket named "S.log" in the standard socket directory. + + * Provide fake pinentries for use by tests cases of downstream + developers. + + * Fixed many bugs and regressions. + + * Many changes and improvements for the test suite. + + +Noteworthy changes in version 2.1.15 (2016-08-18) +------------------------------------------------- + + * gpg: Remove the --tofu-db-format option and support for the split + TOFU database. + + * gpg: Add option --sender to prepare for coming features. + + * gpg: Add option --input-size-hint to help progress indicators. + + * gpg: Extend the PROGRESS status line with the counted unit. + + * gpg: Avoid publishing the GnuPG version by default with --armor. + + * gpg: Properly ignore legacy keys in the keyring cache. + + * gpg: Always print fingerprint records in --with-colons mode. + + * gpg: Make sure that keygrips are printed for each subkey in + --with-colons mode. + + * gpg: New import filter "drop-sig". + + * gpgsm: Fix a bug in the machine-readable key listing. + + * gpg,gpgsm: Block signals during keyring updates to limits the + effects of a Ctrl-C at the wrong time. + + * g13: Add command --umount and other fixes for dm-crypt. + + * agent: Fix regression in SIGTERM handling. + + * agent: Cleanup of the ssh-agent code. + + * agent: Allow import of overly long keys. + + * scd: Fix problems with card removal. + + * dirmngr: Remove all code for running as a system service. + + * tools: Make gpg-wks-client conforming to the specs. + + * tests: Improve the output of the new regression test tool. + + * tests: Distribute the standalone test runner. + + * tests: Run each test in a clean environment. + + * Spelling and grammar fixes. + + +Noteworthy changes in version 2.1.14 (2016-07-14) +------------------------------------------------- + + * gpg: Removed options --print-dane-records and --print-pka-records. + The new export options "export-pka" and "export-dane" can instead + be used with the export command. + + * gpg: New options --import-filter and --export-filter. + + * gpg: New import options "import-show" and "import-export". + + * gpg: New option --no-keyring. + + * gpg: New command --quick-revuid. + + * gpg: New options -f/--recipient-file and -F/--hidden-recipient-file + to directly specify encryption keys. + + * gpg: New option --mimemode to indicate that the content is a MIME + part. Does only enable --textmode right now. + + * gpg: New option --rfc4880bis to allow experiments with proposed + changes to the current OpenPGP specs. + + * gpg: Fix regression in the "fetch" sub-command of --card-edit. + + * gpg: Fix regression since 2.1 in option --try-all-secrets. + + * gpgv: Change default options for extra security. + + * gpgsm: No more root certificates are installed by default. + + * agent: "updatestartuptty" does now affect more environment + variables. + + * scd: The option --homedir does now work with scdaemon. + + * scd: Support some more GEMPlus card readers. + + * gpgtar: Fix handling of '-' as file name. + + * gpgtar: New commands --create and --extract. + + * gpgconf: Tweak for --list-dirs to better support shell scripts. + + * tools: Add programs gpg-wks-client and gpg-wks-server to implement + a Web Key Service. The configure option --enable-wks-tools is + required to build them; they should be considered Beta software. + + * tests: Complete rework of the openpgp part of the test suite. The + test scripts have been changed from Bourne shell scripts to Scheme + programs. A customized scheme interpreter (gpgscm) is included. + This change was triggered by the need to run the test suite on + non-Unix platforms. + + * The rendering of the man pages has been improved. + + +Noteworthy changes in version 2.1.13 (2016-06-16) +------------------------------------------------- + + * gpg: New command --quick-addkey. Extend the --quick-gen-key + command. + + * gpg: New --keyid-format "none" which is now also the default. + + * gpg: New option --with-subkey-fingerprint. + + * gpg: Include Signer's UID subpacket in signatures if the secret key + has been specified using a mail address and the new option + --disable-signer-uid is not used. + + * gpg: Allow unattended deletion of a secret key. + + * gpg: Allow export of non-passphrase protected secret keys. + + * gpg: New status lines KEY_CONSIDERED and NOTATION_FLAGS. + + * gpg: Change status line TOFU_STATS_LONG to use '~' as + a non-breaking-space character. + + * gpg: Speedup key listings in Tofu mode. + + * gpg: Make sure that the current and total values of a PROGRESS + status line are small enough. + + * gpgsm: Allow the use of AES192 and SERPENT ciphers. + + * dirmngr: Adjust WKD lookup to current specs. + + * dirmngr: Fallback to LDAP v3 if v2 is is not supported. + + * gpgconf: New commands --create-socketdir and --remove-socketdir, + new option --homedir. + + * If a /run/user/$UID directory exists, that directory is now used + for IPC sockets instead of the GNUPGHOME directory. This fixes + problems with NFS and too long socket names and thus avoids the + need for redirection files. + + * The Speedo build systems now uses the new versions.gnupg.org server + to retrieve the default package versions. + + * Fix detection of libusb on FreeBSD. + + * Speedup fd closing after a fork. + + +Noteworthy changes in version 2.1.12 (2016-05-04) +------------------------------------------------- + + * gpg: New --edit-key sub-command "change-usage" for testing + purposes. + + * gpg: Out of order key-signatures are now systematically detected + and fixed by --edit-key. + + * gpg: Improved detection of non-armored messages. + + * gpg: Removed the extra prompt needed to create Curve25519 keys. + + * gpg: Improved user ID selection for --quick-sign-key. + + * gpg: Use the root CAs provided by the system with --fetch-key. + + * gpg: Add support for the experimental Web Key Directory key + location service. + + * gpg: Improve formatting of Tofu messages and emit new Tofu specific + status lines. + + * gpgsm: Add option --pinentry-mode to support a loopback pinentry. + + * gpgsm: A new pubring.kbx is now created with the header blob so + that gpg can detect that the keybox format needs to be used. + + * agent: Add read support for the new private key protection format + openpgp-s2k-ocb-aes. + + * agent: Add read support for the new extended private key format. + + * agent: Default to --allow-loopback-pinentry and add option + --no-allow-loopback-pinentry. + + * scd: Changed to use the new libusb 1.0 API for the internal CCID + driver. + + * dirmngr: The dirmngr-client does now auto-detect the PEM format. + + * g13: Add experimental support for dm-crypt. + + * w32: Tofu support is now available with the Speedo build method. + + * w32: Removed the need for libiconv.dll. + + * The man pages for gpg and gpgv are now installed under the correct + name (gpg2 or gpg - depending on a configure option). + + * Lots of internal cleanups and bug fixes. + + +Noteworthy changes in version 2.1.11 (2016-01-26) +------------------------------------------------- + + * gpg: New command --export-ssh-key to replace the gpgkey2ssh tool. + + * gpg: Allow to generate mail address only keys with --gen-key. + + * gpg: "--list-options show-usage" is now the default. + + * gpg: Make lookup of DNS CERT records holding an URL work. + + * gpg: Emit PROGRESS status lines during key generation. + + * gpg: Don't check for ambigious or non-matching key specification in + the config file or given to --encrypt-to. This feature will return + in 2.3.x. + + * gpg: Lock keybox files while updating them. + + * gpg: Solve rare error on Windows during keyring and Keybox updates. + + * gpg: Fix possible keyring corruption. (bug#2193) + + * gpg: Fix regression of "bkuptocard" sub-command in --edit-key and + remove "checkbkupkey" sub-command introduced with 2.1. (bug#2169) + + * gpg: Fix internal error in gpgv when using default keyid-format. + + * gpg: Fix --auto-key-retrieve to work with dirmngr.conf configured + keyservers. (bug#2147). + + * agent: New option --pinentry-timeout. + + * scd: Improve unplugging of USB readers under Windows. + + * scd: Fix regression for generating RSA keys on card. + + * dirmmgr: All configured keyservers are now searched. + + * dirmngr: Install CA certificate for hkps.pool.sks-keyservers.net. + Use this certiticate even if --hkp-cacert is not used. + + * gpgtar: Add actual encryption code. gpgtar does now fully replace + gpg-zip. + + * gpgtar: Fix filename encoding problem on Windows. + + * Print a warning if a GnuPG component is using an older version of + gpg-agent, dirmngr, or scdaemon. + + +Noteworthy changes in version 2.1.10 (2015-12-04) +------------------------------------------------- + + * gpg: New trust models "tofu" and "tofu+pgp". + + * gpg: New command --tofu-policy. New options --tofu-default-policy + and --tofu-db-format. + + * gpg: New option --weak-digest to specify hash algorithms which + should be considered weak. + + * gpg: Allow the use of multiple --default-key options; take the last + available key. + + * gpg: New option --encrypt-to-default-key. + + * gpg: New option --unwrap to only strip the encryption layer. + + * gpg: New option --only-sign-text-ids to exclude photo IDs from key + signing. + + * gpg: Check for ambigious or non-matching key specification in the + config file or given to --encrypt-to. + + * gpg: Show the used card reader with --card-status. + + * gpg: Print export statistics and an EXPORTED status line. + + * gpg: Allow selecting subkeys by keyid in --edit-key. + + * gpg: Allow updating the expiration time of multiple subkeys at + once. + + * dirmngr: New option --use-tor. For full support this requires + libassuan version 2.4.2 and a patched version of libadns + (e.g. adns-1.4-g10-7 as used by the standard Windows installer). + + * dirmngr: New option --nameserver to specify the nameserver used in + Tor mode. + + * dirmngr: Keyservers may again be specified by IP address. + + * dirmngr: Fixed problems in resolving keyserver pools. + + * dirmngr: Fixed handling of premature termination of TLS streams so + that large numbers of keys can be refreshed via hkps. + + * gpg: Fixed a regression in --locate-key [since 2.1.9]. + + * gpg: Fixed another bug for keyrings with legacy keys. + + * gpgsm: Allow combinations of usage flags in --gen-key. + + * Make tilde expansion work with most options. + + * Many other cleanups and bug fixes. + + +Noteworthy changes in version 2.1.9 (2015-10-09) +------------------------------------------------ + + * gpg: Allow fetching keys via OpenPGP DANE (--auto-key-locate). New + option --print-dane-records. [Update: --print-dane-records replaced + in 2.1.4.] + + * gpg: Fix for a problem with PGP-2 keys in a keyring. + + * gpg: Fail with an error instead of a warning if a modern cipher + algorithm is used without a MDC. + + * agent: New option --pinentry-invisible-char. + + * agent: Always do a RSA signature verification after creation. + + * agent: Fix a regression in ssh-add-ing Ed25519 keys. + + * agent: Fix ssh fingerprint computation for nistp384 and EdDSA. + + * agent: Fix crash during passphrase entry on some platforms. + + * scd: Change timeout to fix problems with some 2.1 cards. + + * dirmngr: Displayed name is now Key Acquirer. + + * dirmngr: Add option --keyserver. Deprecate that option for gpg. + Install a dirmngr.conf file from a skeleton for new installations. + + +Noteworthy changes in version 2.1.8 (2015-09-10) +------------------------------------------------ + + * gpg: Sending very large keys to the keyservers works again. + + * gpg: Validity strings in key listings are now again translatable. + + * gpg: Emit FAILURE status lines to help GPGME. + + * gpg: Does not anymore link to Libksba to reduce dependencies. + + * gpgsm: Export of secret keys via Assuan is now possible. + + * agent: Raise the maximum passphrase length from 100 to 255 bytes. + + * agent: Fix regression using EdDSA keys with ssh. + + * Does not anymore use a build timestamp by default. + + * The fallback encoding for broken locale settings changed + from Latin-1 to UTF-8. + + * Many code cleanups and improved internal documentation. + + * Various minor bug fixes. + + +Noteworthy changes in version 2.1.7 (2015-08-11) +------------------------------------------------ + + * gpg: Support encryption with Curve25519 if Libgcrypt 1.7 is used. + + * gpg: In the --edit-key menu: Removed the need for "toggle", changed + how secret keys are indicated, new commands "fpr *" and "grip". + + * gpg: More fixes related to legacy keys in a keyring. + + * gpgv: Does now also work with a "trustedkeys.kbx" file. + + * scd: Support some feature from the OpenPGP card 3.0 specs. + + * scd: Improved ECC support + + * agent: New option --force for the DELETE_KEY command. + + * w32: Look for the Pinentry at more places. + + * Dropped deprecated gpgsm-gencert.sh + + * Various other bug fixes. + + +Noteworthy changes in version 2.1.6 (2015-07-01) +------------------------------------------------ + + * agent: New option --verify for the PASSWD command. + + * gpgsm: Add command option "offline" as an alternative to + --disable-dirmngr. + + * gpg: Do not prompt multiple times for a password in pinentry + loopback mode. + + * Allow the use of debug category names with --debug. + + * Using gpg-agent and gpg/gpgsm with different locales will now show + the correct translations in Pinentry. + + * gpg: Improve speed of --list-sigs and --check-sigs. + + * gpg: Make --list-options show-sig-subpackets work again. + + * gpg: Fix an export problem for old keyrings with PGP-2 keys. + + * scd: Support PIN-pads on more readers. + + * dirmngr: Properly cleanup zombie LDAP helper processes and avoid + hangs on dirmngr shutdown. + + * Various other bug fixes. + + +Noteworthy changes in version 2.1.5 (2015-06-11) +------------------------------------------------ + + * Support for an external passphrase cache. + + * Support for the forthcoming version 3 OpenPGP smartcard. + + * Manuals now show the actual used file names. + + * Prepared for improved integration with Emacs. + + * Code cleanups and minor bug fixes. + + +Noteworthy changes in version 2.1.4 (2015-05-12) +------------------------------------------------ + + * gpg: Add command --quick-adduid to non-interactively add a new user + id to an existing key. + + * gpg: Do no enable honor-keyserver-url by default. Make it work if + enabled. + + * gpg: Display the serial number in the --card-status output again. + + * agent: Support for external password managers. + Add option --no-allow-external-cache. + + * scdaemon: Improved handling of extended APDUs. + + * Make HTTP proxies work again. + + * All network access including DNS as been moved to Dirmngr. + + * Allow building without LDAP support. + + * Fixed lots of smaller bugs. + + +Noteworthy changes in version 2.1.3 (2015-04-11) +------------------------------------------------ + + * gpg: LDAP keyservers are now supported by 2.1. + + * gpg: New option --with-icao-spelling. + + * gpg: New option --print-pka-records. Changed the PKA method to use + CERT records and hashed names. [Update: --print-pka-records + replaced in 2.1.4.] + + * gpg: New command --list-gcrypt-config. New parameter "curve" + for --list-config. + + * gpg: Print a NEWSIG status line like gpgsm always did. + + * gpg: Print MPI values with --list-packets and --verbose. + + * gpg: Write correct MPI lengths with ECC keys. + + * gpg: Skip legacy PGP-2 keys while searching. + + * gpg: Improved searching for mail addresses when using a keybox. + + * gpgsm: Changed default algos to AES-128 and SHA-256. + + * gpgtar: Fixed extracting files with sizes of a multiple of 512. + + * dirmngr: Fixed SNI handling for hkps pools. + + * dirmngr: extra-certs and trusted-certs are now always loaded from + the sysconfig dir instead of the homedir. + + * Fixed possible problems due to compiler optimization, two minor + regressions, and other bugs. + + +Noteworthy changes in version 2.1.2 (2015-02-11) +------------------------------------------------ + + * gpg: The parameter 'Passphrase' for batch key generation works + again. + + * gpg: Using a passphrase option in batch mode now has the expected + effect on --quick-gen-key. + + * gpg: Improved reporting of unsupported PGP-2 keys. + + * gpg: Added support for algo names when generating keys using + --command-fd. + + * gpg: Fixed DoS based on bogus and overlong key packets. + + * agent: When setting --default-cache-ttl the value + for --max-cache-ttl is adjusted to be not lower than the former. + + * agent: Fixed problems with the new --extra-socket. + + * agent: Made --allow-loopback-pinentry changeable with gpgconf. + + * agent: Fixed importing of unprotected openpgp keys. + + * agent: Now tries to use a fallback pinentry if the standard + pinentry is not installed. + + * scd: Added support for ECDH. + + * Fixed several bugs related to bogus keyrings and improved some + other code. + + +Noteworthy changes in version 2.1.1 (2014-12-16) +------------------------------------------------ + + * gpg: Detect faulty use of --verify on detached signatures. + + * gpg: New import option "keep-ownertrust". + + * gpg: New sub-command "factory-reset" for --card-edit. + + * gpg: A stub key for smartcards is now created by --card-status. + + * gpg: Fixed regression in --refresh-keys. + + * gpg: Fixed regresion in %g and %p codes for --sig-notation. + + * gpg: Fixed best matching hash algo detection for ECDSA and EdDSA. + + * gpg: Improved perceived speed of secret key listisngs. + + * gpg: Print number of skipped PGP-2 keys on import. + + * gpg: Removed the option aliases --throw-keyid and --notation-data; + use --throw-keyids and --set-notation instead. + + * gpg: New import option "keep-ownertrust". + + * gpg: Skip too large keys during import. + + * gpg,gpgsm: New option --no-autostart to avoid starting gpg-agent or + dirmngr. + + * gpg-agent: New option --extra-socket to provide a restricted + command set for use with remote clients. + + * gpgconf --kill does not anymore start a service only to kill it. + + * gpg-pconnect-agent: Add convenience option --uiserver. + + * Fixed keyserver access for Windows. + + * Fixed build problems on Mac OS X + + * The Windows installer does now install development files + + * More translations (but most of them are not complete). + + * To support remotely mounted home directories, the IPC sockets may + now be redirected. This feature requires Libassuan 2.2.0. + + * Improved portability and the usual bunch of bug fixes. + + +Noteworthy changes in version 2.1.0 (2014-11-06) +------------------------------------------------ + + This release introduces a lot of changes. Most of them are internal + and thus not user visible. However, some long standing behavior has + slightly changed and it is strongly suggested that an existing + "~/.gnupg" directory is backed up before this version is used. + + A verbose description of the major new features and changes can be + found in the file doc/whats-new-in-2.1.txt. + + * gpg: All support for v3 (PGP 2) keys has been dropped. All + signatures are now created as v4 signatures. v3 keys will be + removed from the keyring. + + * gpg: With pinentry-0.9.0 the passphrase "enter again" prompt shows + up in the same window as the "new passphrase" prompt. + + * gpg: Allow importing keys with duplicated long key ids. + + * dirmngr: May now be build without support for LDAP. + + * For a complete list of changes see the lists of changes for the + 2.1.0 beta versions below. Note that all relevant fixes from + versions 2.0.14 to 2.0.26 are also applied to this version. + + + [Noteworthy changes in version 2.1.0-beta864 (2014-10-03)] + + * gpg: Removed the GPG_AGENT_INFO related code. GnuPG does now + always use a fixed socket name in its home directory. + + * gpg: Renamed --gen-key to --full-gen-key and re-added a --gen-key + command with less choices. + + * gpg: Use SHA-256 for all signature types also on RSA keys. + + * gpg: Default keyring is now created with a .kbx suffix. + + * gpg: Add a shortcut to the key capabilies menu (e.g. "=e" sets the + encryption capabilities). + + * gpg: Fixed obsolete options parsing. + + * Further improvements for the alternative speedo build system. + + + [Noteworthy changes in version 2.1.0-beta834 (2014-09-18)] + + * gpg: Improved passphrase caching. + + * gpg: Switched to algorithm number 22 for EdDSA. + + * gpg: Removed CAST5 from the default preferences. + + * gpg: Order SHA-1 last in the hash preferences. + + * gpg: Changed default cipher for --symmetric to AES-128. + + * gpg: Fixed export of ECC keys and import of EdDSA keys. + + * dirmngr: Fixed the KS_FETCH command. + + * The speedo build system now downloads related packages and works + for non-Windows platforms. + + + [Noteworthy changes in version 2.1.0-beta783 (2014-08-14)] + + * gpg: Add command --quick-gen-key. + + * gpg: Make --quick-sign-key promote local key signatures. + + * gpg: Added "show-usage" sub-option to --list-options. + + * gpg: Screen keyserver responses to avoid importing unwanted keys + from rogue servers. + + * gpg: Removed the option --pgp2 and --rfc1991 and the ability to + create PGP-2 compatible messages. + + * gpg: Removed options --compress-keys and --compress-sigs. + + * gpg: Cap attribute packets at 16MB. + + * gpg: Improved output of --list-packets. + + * gpg: Make with-colons output of --search-keys work again. + + * gpgsm: Auto-create the ".gnupg" directory like gpg does. + + * agent: Fold new passphrase warning prompts into one. + + * scdaemon: Add support for the Smartcard-HSM card. + + * scdaemon: Remove the use of the pcsc-wrapper. + + + [Noteworthy changes in version 2.1.0-beta751 (2014-07-03)] + + * gpg: Create revocation certificates during key generation. + + * gpg: Create exported secret keys and revocation certifciates with + mode 0700 + + * gpg: The validity of user ids is now shown by default. To revert + this add "list-options no-show-uid-validity" to gpg.conf. + + * gpg: Make export of secret keys work again. + + * gpg: The output of --list-packets does now print the offset of the + packet and information about the packet header. + + * gpg: Avoid DoS due to garbled compressed data packets. [CVE-2014-4617] + + * gpg: Print more specific reason codes with the INV_RECP status. + + * gpg: Cap RSA and Elgamal keysize at 4096 bit also for unattended + key generation. + + * scdaemon: Support reader Gemalto IDBridge CT30 and pinpad of SCT + cyberJack go. + + * The speedo build system has been improved. It is now also possible + to build a partly working installer for Windows. + + + [Noteworthy changes in version 2.1.0-beta442 (2014-06-05)] + + * gpg: Changed the format of key listings. To revert to the old + format the option --legacy-list-mode is available. + + * gpg: Add experimental signature support using curve Ed25519 and + with a patched Libgcrypt also encryption support with Curve25519. + [Update: this encryption support has been removed from 2.1.0 until + we have agreed on a suitable format.] + + * gpg: Allow use of Brainpool curves. + + * gpg: Accepts a space separated fingerprint as user ID. This + allows to copy and paste the fingerprint from the key listing. + + * gpg: The hash algorithm is now printed for signature records in key + listings. + + * gpg: Reject signatures made using the MD5 hash algorithm unless the + new option --allow-weak-digest-algos or --pgp2 are given. + + * gpg: Print a warning if the Gnome-Keyring-Daemon intercepts the + communication with the gpg-agent. + + * gpg: New option --pinentry-mode. + + * gpg: Fixed decryption using an OpenPGP card. + + * gpg: Fixed bug with deeply nested compressed packets. + + * gpg: Only the major version number is by default included in the + armored output. + + * gpg: Do not create a trustdb file if --trust-model=always is used. + + * gpg: Protect against rogue keyservers sending secret keys. + + * gpg: The format of the fallback key listing ("gpg KEYFILE") is now + more aligned to the regular key listing ("gpg -k"). + + * gpg: The option--show-session-key prints its output now before the + decryption of the bulk message starts. + + * gpg: New %U expando for the photo viewer. + + * gpg,gpgsm: New option --with-secret. + + * gpgsm: By default the users are now asked via the Pinentry whether + they trust an X.509 root key. To prohibit interactive marking of + such keys, the new option --no-allow-mark-trusted may be used. + + * gpgsm: New commands to export a secret RSA key in PKCS#1 or PKCS#8 + format. + + * gpgsm: Improved handling of re-issued CA certificates. + + * agent: The included ssh agent does now support ECDSA keys. + + * agent: New option --enable-putty-support to allow gpg-agent on + Windows to act as a Pageant replacement with full smartcard support. + + * scdaemon: New option --enable-pinpad-varlen. + + * scdaemon: Various fixes for pinpad equipped card readers. + + * scdaemon: Rename option --disable-pinpad (was --disable-keypad). + + * scdaemon: Better support fo CCID readers. Now, internal CCID + driver supports readers with no auto configuration feature. + + * dirmngr: Removed support for the original HKP keyserver which is + not anymore used by any site. + + * dirmngr: Improved support for keyserver pools. + + * tools: New option --dirmngr for gpg-connect-agent. + + * The GNU Pth library has been replaced by the new nPth library. + + * Support installation as portable application under Windows. + + * All kind of other improvements - see the git log. + + + [Noteworthy changes in version 2.1.0beta3 (2011-12-20)] + + * gpg: Fixed regression in the secret key export function. + + * gpg: Allow generation of card keys up to 4096 bit. + + * gpgsm: Preliminary support for the validation model "steed". + + * gpgsm: Improved certificate creation. + + * agent: Support the SSH confirm flag. + + * agent: New option to select a passphrase mode. The loopback + mode may be used to bypass Pinentry. + + * agent: The Assuan commands KILLAGENT and KILLSCD are working again. + + * scdaemon: Does not anymore block after changing a card (regression + fix). + + * tools: gpg-connect-agent does now proberly display the help output + for "SCD HELP" commands. + + + [Noteworthy changes in version 2.1.0beta2 (2011-03-08)] + + * gpg: ECC support as described by draft-jivsov-openpgp-ecc-06.txt + [Update: now known as RFC-6637]. + + * gpg: Print "AES128" instead of "AES". This change introduces a + little incompatibility for tools using "gpg --list-config". We + hope that these tools are written robust enough to accept this new + algorithm name as well. + + * gpgsm: New feature to create certificates from a parameter file. + Add prompt to the --gen-key UI to create self-signed certificates. + + * agent: TMPDIR is now also honored when creating a socket using + the --no-standard-socket option and with symcryptrun's temp files. + + * scdaemon: Fixed a bug where scdaemon sends a signal to gpg-agent + running in non-daemon mode. + + * dirmngr: Fixed CRL loading under W32 (bug#1010). + + * Dirmngr has taken over the function of the keyserver helpers. Thus + we now have a specified direct interface to keyservers via Dirmngr. + LDAP, DNS and mail backends are not yet implemented. + + * Fixed TTY management for pinentries and session variable update + problem. + + + [Noteworthy changes in version 2.1.0beta1 (2010-10-26)] + + * gpg: secring.gpg is not anymore used but all secret key operations + are delegated to gpg-agent. The import command moves secret keys + to the agent. + + * gpg: The OpenPGP import command is now able to merge secret keys. + + * gpg: Encrypted OpenPGP messages with trailing data (e.g. other + OpenPGP packets) are now correctly parsed. + + * gpg: Given sufficient permissions Dirmngr is started automagically. + + * gpg: Fixed output of "gpgconf --check-options". + + * gpg: Removed options --export-options(export-secret-subkey-passwd) + and --simple-sk-checksum. + + * gpg: New options --try-secret-key. + + * gpg: Support DNS lookups for SRV, PKA and CERT on W32. + + * gpgsm: The --audit-log feature is now more complete. + + * gpgsm: The default for --include-cert is now to include all + certificates in the chain except for the root certificate. + + * gpgsm: New option --ignore-cert-extension. + + * g13: The G13 tool for disk encryption key management has been + added. + + * agent: If the agent's --use-standard-socket option is active, all + tools try to start and daemonize the agent on the fly. In the past + this was only supported on W32; on non-W32 systems the new + configure option --disable-standard-socket may now be used to + disable this new default. + + * agent: New and changed passphrases are now created with an + iteration count requiring about 100ms of CPU work. + + * dirmngr: Dirmngr is now a part of this package. It is now also + expected to run as a system service and the configuration + directories are changed to the GnuPG name space. [Update: 2.1.0 + starts dirmngr on demand as user daemon.] + + * Support for Windows CE. [Update: This has not been tested for the + 2.1.0 release] + + * Numerical values may now be used as an alternative to the + debug-level keywords. + + +Version 2.0.28 (2015-06-02) +Version 2.0.27 (2015-02-18) +Version 2.0.26 (2014-08-12) +Version 2.0.25 (2014-06-30) +Version 2.0.24 (2014-06-24) +Version 2.0.23 (2014-06-03) +Version 2.0.22 (2013-10-04) +Version 2.0.21 (2013-08-19) +Version 2.0.20 (2013-05-10) +Version 2.0.19 (2012-03-27) +Version 2.0.18 (2011-08-04) +Version 2.0.17 (2011-01-13) +Version 2.0.16 (2010-07-19) +Version 2.0.15 (2010-03-09) +Version 2.0.14 (2009-12-21) + + +Noteworthy changes in version 2.0.13 (2009-09-04) +------------------------------------------------- + + * GPG now generates 2048 bit RSA keys by default. The default hash + algorithm preferences has changed to prefer SHA-256 over SHA-1. + 2048 bit DSA keys are now generated to use a 256 bit hash algorithm + + * The envvars XMODIFIERS, GTK_IM_MODULE and QT_IM_MODULE are now + passed to the Pinentry to make SCIM work. + + * The GPGSM command --gen-key features a --batch mode and implements + all features of gpgsm-gencert.sh in standard mode. + + * New option --re-import for GPGSM's IMPORT server command. + + * Enhanced writing of existing keys to OpenPGP v2 cards. + + * Add hack to the internal CCID driver to allow the use of some + Omnikey based card readers with 2048 bit keys. + + * GPG now repeatly asks the user to insert the requested OpenPGP + card. This can be disabled with --limit-card-insert-tries=1. + + * Minor bug fixes. + + +Noteworthy changes in version 2.0.12 (2009-06-17) +------------------------------------------------- + + * GPGSM now always lists ephemeral certificates if specified by + fingerprint or keygrip. + + * New command "KEYINFO" for GPG_AGENT. GPGSM now also returns + information about smartcards. + + * Made sure not to leak file descriptors if running gpg-agent with a + command. Restore the signal mask to solve a problem in Mono. + + * Changed order of the confirmation questions for root certificates + and store negative answers in trustlist.txt. + + * Better synchronization of concurrent smartcard sessions. + + * Support 2048 bit OpenPGP cards. + + * Support Telesec Netkey 3 cards. + + * The gpg-protect-tool now uses gpg-agent via libassuan. Under + Windows the Pinentry will now be put into the foreground. + + * Changed code to avoid a possible Mac OS X system freeze. + + +Noteworthy changes in version 2.0.11 (2009-03-03) +------------------------------------------------- + + * Fixed a problem in SCDAEMON which caused unexpected card resets. + + * SCDAEMON is now aware of the Geldkarte. + + * The SCDAEMON option --allow-admin is now used by default. + + * GPGCONF now restarts SCdaemon if necessary. + + * The default cipher algorithm in GPGSM is now again 3DES. This is + due to interoperability problems with Outlook 2003 which still + can't cope with AES. + + +Noteworthy changes in version 2.0.10 (2009-01-12) +------------------------------------------------- + + * [gpg] New keyserver helper gpg2keys_kdns as generic DNS CERT + lookup. Run with --help for a short description. Requires the + ADNS library. + + * [gpg] New mechanisms "local" and "nodefault" for --auto-key-locate. + Fixed a few problems with this option. + + * [gpg] New command --locate-keys. + + * [gpg] New options --with-sig-list and --with-sig-check. + + * [gpg] The option "-sat" is no longer an alias for --clearsign. + + * [gpg] The option --fixed-list-mode is now implicitly used and obsolete. + + * [gpg] New control statement %ask-passphrase for the unattended key + generation. + + * [gpg] The algorithm to compute the SIG_ID status has been changed. + + * [gpgsm] Now uses AES by default. + + * [gpgsm] Made --output option work with --export-secret-key-p12. + + * [gpg-agent] Terminate process if the own listening socket is not + anymore served by ourself. + + * [scdaemon] Made it more robust on W32. + + * [gpg-connect-agent] Accept commands given as command line arguments. + + * [w32] Initialized the socket subsystem for all keyserver helpers. + + * [w32] The sysconf directory has been moved from a subdirectory of + the installation directory to %CSIDL_COMMON_APPDATA%/GNU/etc/gnupg. + + * [w32] The gnupg2.nls directory is not anymore used. The standard + locale directory is now used. + + * [w32] Fixed a race condition between gpg and gpgsm in the use of + temporary file names. + + * The gpg-preset-passphrase mechanism works again. An arbitrary + string may now be used for a custom cache ID. + + * Admin PINs are cached again (bug in 2.0.9). + + * Support for version 2 OpenPGP cards. + + * Libgcrypt 1.4 is now required. + + +Noteworthy changes in version 2.0.9 (2008-03-26) +------------------------------------------------ + + * Gpgsm always tries to locate missing certificates from a running + Dirmngr's cache. + + * Tweaks for Windows. + + * The Admin PIN for OpenPGP cards may now be entered with the pinpad. + + * Improved certificate chain construction. + + * Extended the PKITS framework. + + * Fixed a bug in the ambigious name detection. + + * Fixed possible memory corruption while importing OpenPGP keys (bug + introduced with 2.0.8). [CVE-2008-1530] + + * Minor bug fixes. + + +Noteworthy changes in version 2.0.8 (2007-12-20) +------------------------------------------------ + + * Enhanced gpg-connect-agent with a small scripting language. + + * New option --list-config for gpgconf. + + * Fixed a crash in gpgconf. + + * Gpg-agent now supports the passphrase quality bar of the latest + Pinentry. + + * The envvars XAUTHORITY and PINENTRY_USER_DATA are now passed to the + Pinentry. + + * Fixed the auto creation of the key stub for smartcards. + + * Fixed a rare bug in decryption using the OpenPGP card. + + * Creating DSA2 keys is now possible. + + * New option --extra-digest-algo for gpgsm to allow verification of + broken signatures. + + * Allow encryption with legacy Elgamal sign+encrypt keys with option + --rfc2440. + + * Windows is now a supported platform. + + * Made sure that under Windows the file permissions of the socket are + taken into account. This required a change of our socket emulation + code and changed the IPC protocol under Windows. + + +Noteworthy changes in version 2.0.7 (2007-09-10) +------------------------------------------------ + + * Fixed encryption problem if duplicate certificates are in the + keybox. + + * Made it work on Windows Vista. Note that the entire Windows port + is still considered Beta. + + * Add new options min-passphrase-nonalpha, check-passphrase-pattern, + enforce-passphrase-constraints and max-passphrase-days to + gpg-agent. + + * Add command --check-components to gpgconf. Gpgconf now uses the + installed versions of the programs and does not anymore search via + PATH for them. + + +Noteworthy changes in version 2.0.6 (2007-08-16) +------------------------------------------------ + + * GPGSM does now grok --default-key. + + * GPGCONF is now aware of --default-key and --encrypt-to. + + * GPGSM does again correctly print the serial number as well the the + various keyids. This was broken since 2.0.4. + + * New option --validation-model and support for the chain-model. + + * Improved Windows support. + + +Noteworthy changes in version 2.0.5 (2007-07-05) +------------------------------------------------ + + * Switched license to GPLv3. + + * Basic support for Windows. Run "./autogen.sh --build-w32" to build + it. As usual the mingw cross compiling toolchain is required. + + * Fixed bug when using the --p12-charset without --armor. + + * The command --gen-key may now be used instead of the + gpgsm-gencert.sh script. + + * Changed key generation to reveal less information about the + machine. Bug fixes for gpg2's card key generation. + + +Noteworthy changes in version 2.0.4 (2007-05-09) +------------------------------------------------ + + * The server mode key listing commands are now also working for + systems without the funopen/fopencookie API. + + * PKCS#12 import now tries several encodings in case the passphrase + was not utf-8 encoded. New option --p12-charset for gpgsm. + + * Improved the libgcrypt logging support in all modules. + + +Noteworthy changes in version 2.0.3 (2007-03-08) +------------------------------------------------ + + * By default, do not allow processing multiple plaintexts in a single + stream. Many programs that called GnuPG were assuming that GnuPG + did not permit this, and were thus not using the plaintext boundary + status tags that GnuPG provides. This change makes GnuPG reject + such messages by default which makes those programs safe again. + --allow-multiple-messages returns to the old behavior. [CVE-2007-1263]. + + * New --verify-option show-primary-uid-only. + + * gpgconf may now reads a global configuration file to select which + options are changeable by a frontend. The new applygnupgdefaults + tool may be used by an admin to set default options for all users. + + * The PIN pad of the Cherry XX44 keyboard is now supported. The + DINSIG and the NKS applications are now also aware of PIN pads. + + +Noteworthy changes in version 2.0.2 (2007-01-31) +------------------------------------------------ + + * Fixed a serious and exploitable bug in processing encrypted + packages. [CVE-2006-6235]. + + * Added --passphrase-repeat to set the number of times GPG will + prompt for a new passphrase to be repeated. This is useful to help + memorize a new passphrase. The default is 1 repetition. + + * Using a PIN pad does now also work for the signing key. + + * A warning is displayed by gpg-agent if a new passphrase is too + short. New option --min-passphrase-len defaults to 8. + + * The status code BEGIN_SIGNING now shows the used hash algorithms. + + +Noteworthy changes in version 2.0.1 (2006-11-28) +------------------------------------------------ + + * Experimental support for the PIN pads of the SPR 532 and the Kaan + Advanced card readers. Add "disable-keypad" scdaemon.conf if you + don't want it. Does currently only work for the OpenPGP card and + its authentication and decrypt keys. + + * Fixed build problems on some some platforms and crashes on amd64. + + * Fixed a buffer overflow in gpg2. [bug#728,CVE-2006-6169] + + +Noteworthy changes in version 2.0.0 (2006-11-11) +------------------------------------------------ + + * First stable version of a GnuPG integrating OpenPGP and S/MIME. + + +Noteworthy changes in version 1.9.95 (2006-11-06) +------------------------------------------------- + + * Minor bug fixes. + + +Noteworthy changes in version 1.9.94 (2006-10-24) +------------------------------------------------- + + * Keys for gpgsm may now be specified using a keygrip. A keygrip is + indicated by a prefixing it with an ampersand. + + * gpgconf now supports switching the CMS cipher algo (e.g. to AES). + + * New command --gpgconf-test for all major tools. This may be used to + check whether the configuration file is sane. + + +Noteworthy changes in version 1.9.93 (2006-10-18) +------------------------------------------------- + + * In --with-validation mode gpgsm will now also ask whether a root + certificate should be trusted. + + * Link to Pth only if really necessary. + + * Fixed a pubring corruption bug in gpg2 occurring when importing + signatures or keys with insane lengths. + + * Fixed v3 keyID calculation bug in gpg2. + + * More tweaks for certificates without extensions. + + +Noteworthy changes in version 1.9.92 (2006-10-11) +------------------------------------------------- + + * Bug fixes. + + +Noteworthy changes in version 1.9.91 (2006-10-04) +------------------------------------------------- + + * New "relax" flag for trustlist.txt to allow root CA certificates + without BasicContraints. + + * [gpg2] Removed the -k PGP 2 compatibility hack. -k is now an + alias for --list-keys. + + * [gpg2] Print a warning if "-sat" is used instead of "--clearsign". + + +Noteworthy changes in version 1.9.90 (2006-09-25) +------------------------------------------------- + + * Made readline work for gpg. + + * Cleanups und minor bug fixes. + + * Included translations from gnupg 1.4.5. + + +Noteworthy changes in version 1.9.23 (2006-09-18) +------------------------------------------------- + + * Regular man pages for most tools are now build directly from the + Texinfo source. + + * The gpg code from 1.4.5 has been fully merged into this release. + The configure option --enable-gpg is still required to build this + gpg part. For production use of OpenPGP the gpg version 1.4.5 is + still recommended. Note, that gpg will be installed under the name + gpg2 to allow coexisting with an 1.4.x gpg. + + * API change in gpg-agent's pkdecrypt command. Thus an older gpgsm + may not be used with the current gpg-agent. + + * The scdaemon will now call a script on reader status changes. + + * gpgsm now allows file descriptor passing for "INPUT", "OUTPUT" and + "MESSAGE". + + * The gpgsm server may now output a key listing to the output file + handle. This needs to be enabled using "OPTION list-to-output=1". + + * The --output option of gpgsm has now an effect on list-keys. + + * New gpgsm commands --dump-chain and list-chain. + + * gpg-connect-agent has new options to utilize descriptor passing. + + * A global trustlist may now be used. See doc/examples/trustlist.txt. + + * When creating a new pubring.kbx keybox common certificates are + imported. + + +Noteworthy changes in version 1.9.22 (2006-07-27) +------------------------------------------------- + + * Enhanced pkcs#12 support to allow import from simple keyBags. + + * Exporting to pkcs#12 now create bag attributes so that Mozilla is + able to import the files. + + * Fixed uploading of certain keys to the smart card. + + +Noteworthy changes in version 1.9.21 (2006-06-20) +------------------------------------------------- + + * New command APDU for scdaemon to allow using it for general card + access. Might be used through gpg-connect-agent by using the SCD + prefix command. + + * Support for the CardMan 4040 PCMCIA reader (Linux 2.6.15 required). + + * Scdaemon does not anymore reset cards at the end of a connection. + + * Kludge to allow use of Bundesnetzagentur issued X.509 certificates. + + * Added --hash=xxx option to scdaemon's PKSIGN command. + + * Pkcs#12 files are now created with a MAC. This is for better + interoperability. + + * Collected bug fixes and minor other changes. + + +Noteworthy changes in version 1.9.20 (2005-12-20) +------------------------------------------------- + + * Importing pkcs#12 files created be recent versions of Mozilla works + again. + + * Basic support for qualified signatures. + + * New debug tool gpgparsemail. + + +Noteworthy changes in version 1.9.19 (2005-09-12) +------------------------------------------------- + + * The Belgian eID card is now supported for signatures and ssh. + Other pkcs#15 cards should work as well. + + * Fixed bug in --export-secret-key-p12 so that certificates are again + included. + + +Noteworthy changes in version 1.9.18 (2005-08-01) +------------------------------------------------- + + * [gpgsm] Now allows for more than one email address as well as URIs + and dnsNames in certificate request generation. A keygrip may be + given to create a request from an existing key. + + * A couple of minor bug fixes. + + +Noteworthy changes in version 1.9.17 (2005-06-20) +------------------------------------------------- + + * gpg-connect-agent has now features to handle Assuan INQUIRE + commands. + + * Internal changes for OpenPGP cards. New Assuan command WRITEKEY. + + * GNU Pth is now a hard requirement. + + * [scdaemon] Support for OpenSC has been removed. Instead a new and + straightforward pkcs#15 modules has been written. As of now it + does allows only signing using TCOS cards but we are going to + enhance it to match all the old capabilities. + + * [gpg-agent] New option --write-env-file and Assuan command + UPDATESTARTUPTTY. + + * [gpg-agent] New option --default-cache-ttl-ssh to set the TTL for + SSH passphrase caching independent from the other passphrases. + + +Noteworthy changes in version 1.9.16 (2005-04-21) +------------------------------------------------- + + * gpg-agent does now support the ssh-agent protocol and thus allows + to use the pinentry as well as the OpenPGP smartcard with ssh. + + * New tool gpg-connect-agent as a general client for the gpg-agent. + + * New tool symcryptrun as a wrapper for certain encryption tools. + + * The gpg tool is not anymore build by default because those gpg + versions available in the gnupg 1.4 series are far more matured. + + +Noteworthy changes in version 1.9.15 (2005-01-13) +------------------------------------------------- + + * Fixed passphrase caching bug. + + * Better support for CCID readers; the reader from Cherry RS 6700 USB + does now work. + + +Noteworthy changes in version 1.9.14 (2004-12-22) +------------------------------------------------- + + * [gpg-agent] New option --use-standard-socket to allow the use of a + fixed socket. gpgsm falls back to this socket if GPG_AGENT_INFO + has not been set. + + * Ported to MS Windows with some functional limitations. + + * New tool gpg-preset-passphrase. + + +Noteworthy changes in version 1.9.13 (2004-12-03) +------------------------------------------------- + + * [gpgsm] New option --prefer-system-dirmngr. + + * Minor cleanups and debugging aids. + + +Noteworthy changes in version 1.9.12 (2004-10-22) +------------------------------------------------- + + * [scdaemon] Partly rewrote the PC/SC code. + + * Removed the sc-investigate tool. It is now in a separate package + available at ftp://ftp.g10code.com/g10code/gscutils/ . + + * [gpg-agent] Fixed logging problem. + + +Noteworthy changes in version 1.9.11 (2004-10-01) +------------------------------------------------- + + * When using --import along with --with-validation, the imported + certificates are validated and only imported if they are fully + valid. + + * [gpg-agent] New option --max-cache-ttl. + + * [gpg-agent] When used without --daemon or --server, gpg-agent now + check whether a agent is already running and usable. + + * Fixed some i18n problems. + + +Noteworthy changes in version 1.9.10 (2004-07-22) +------------------------------------------------- + + * Fixed a serious bug in the checking of trusted root certificates. + + * New configure option --enable-agent-pnly allows to build and + install just the agent. + + * Fixed a problem with the log file handling. + + +Noteworthy changes in version 1.9.9 (2004-06-08) +------------------------------------------------ + + * [gpg-agent] The new option --allow-mark-trusted is now required to + allow gpg-agent to add a key to the trustlist.txt after user + confirmation. + + * Creating PKCS#10 requests does now honor the key usage. + + +Noteworthy changes in version 1.9.8 (2004-04-29) +------------------------------------------------ + + * [scdaemon] Overhauled the internal CCID driver. + + * [scdaemon] Status files named ~/.gnupg/reader_.status are now + written when using the internal CCID driver. + + * [gpgsm] New commands --dump-{,secret,external}-keys to show a very + detailed view of the certificates. + + * The keybox gets now compressed after 3 hours and ephemeral + stored certificates are deleted after about a day. + + * [gpg] Usability fixes for --card-edit. Note, that this has already + been ported back to gnupg-1.3 + + +Noteworthy changes in version 1.9.7 (2004-04-06) +------------------------------------------------ + + * Instrumented the modules for gpgconf. + + * Added support for DINSIG card applications. + + * Include the smimeCapabilities attribute with signed messages. + + * Now uses the gettext domain "gnupg2" to avoid conflicts with gnupg + versions < 1.9. + + +Noteworthy changes in version 1.9.6 (2004-03-06) +------------------------------------------------ + + * Code cleanups and bug fixes. + + +Noteworthy changes in version 1.9.5 (2004-02-21) +------------------------------------------------ + + * gpg-protect-tool gets now installed into libexec as it ought to be. + Cleaned up the build system to better comply with the coding + standards. + + * [gpgsm] The --import command is now able to autodetect pkcs#12 + files and import secret and private keys from this file format. + A new command --export-secret-key-p12 is provided to allow + exporting of secret keys in PKCS\#12 format. + + * [gpgsm] The pinentry will now present a description of the key for + whom the passphrase is requested. + + * [gpgsm] New option --with-validation to check the validity of key + while listing it. + + * New option --debug-level={none,basic,advanced,expert,guru} to map + the debug flags to sensitive levels on a per program base. + + +Noteworthy changes in version 1.9.4 (2004-01-30) +------------------------------------------------ + + * Added support for the Telesec NKS 2.0 card application. + + * Added simple tool addgnupghome to create .gnupg directories from + /etc/skel/.gnupg. + + * Various minor bug fixes and cleanups; mainly gpgsm and gpg-agent + related. + + +Noteworthy changes in version 1.9.3 (2003-12-23) +------------------------------------------------ + + * New gpgsm options --{enable,disable}-ocsp to validate keys using + OCSP. This option requires a not yet released DirMngr version. + Default is disabled. + + * The --log-file option may now be used to print logs to a socket. + Prefix the socket name with "socket://" to enable this. This does + not work on all systems and falls back to stderr if there is a + problem with the socket. + + * The options --encrypt-to and --no-encrypt-to now work the same in + gpgsm as in gpg. Note, they are also used in server mode. + + * Duplicated recipients are now silently removed in gpgsm. + + +Noteworthy changes in version 1.9.2 (2003-11-17) +------------------------------------------------ + + * On card key generation is no longer done using the --gen-key + command but from the menu provided by the new --card-edit command. + + * PINs are now properly cached and there are only 2 PINs visible. + The 3rd PIN (CHV2) is internally syncronized with the regular PIN. + + * All kind of other internal stuff. + + +Noteworthy changes in version 1.9.1 (2003-09-06) +------------------------------------------------ + + * Support for OpenSC is back. scdaemon supports a --disable-opensc to + disable OpenSC use at runtime, so that PC/SC or ct-API can still be + used directly. + + * Rudimentary support for the SCR335 smartcard reader using an + internal driver. Requires current libusb from CVS. + + * Bug fixes. + + +Noteworthy changes in version 1.9.0 (2003-08-05) +------------------------------------------------ + + ====== PLEASE SEE README-alpha ======= + + * gpg has been renamed to gpg2 and gpgv to gpgv2. This is a + temporary change to allow co-existing with stable gpg versions. + + * ~/.gnupg/gpg.conf-1.9.0 is fist tried as config file before the + usual gpg.conf. + + * Removed the -k, -kv and -kvv commands. -k is now an alias to + --list-keys. New command -K as alias for --list-secret-keys. + + * Removed --run-as-shm-coprocess feature. + + * gpg does now also use libgcrypt, libgpg-error is required. + + * New gpgsm commands --call-dirmngr and --call-protect-tool. + + * Changing a passphrase is now possible using "gpgsm --passwd" + + * The content-type attribute is now recognized and created. + + * The agent does now reread certain options on receiving a HUP. + + * The pinentry is now forked for each request so that clients with + different environments are supported. When running in daemon mode + and --keep-display is not used the DISPLAY variable is ignored. + + * Merged stuff from the newpg branch and started this new + development branch. + + +Version 1.4.19 (2015-02-27) +Version 1.4.18 (2014-06-30) +Version 1.4.17 (2014-06-23) +Version 1.4.16 (2013-12-18) +Version 1.4.15 (2013-10-04) +Version 1.4.14 (2013-07-25) +Version 1.4.13 (2012-12-20) +Version 1.4.12 (2012-01-30) +Version 1.4.11 (2010-10-18) +Version 1.4.10 (2009-09-02) +Version 1.4.9 (2008-03-26) +Version 1.4.8 (2007-12-20) +Version 1.4.7 (2007-03-05) +Version 1.4.6 (2006-12-06) +Version 1.4.5 (2006-08-01) +Version 1.4.4 (2006-06-25) +Version 1.4.3 (2006-04-03) +Version 1.4.2 (2005-07-26) +Version 1.4.1 (2005-03-15) +Version 1.4.0 (2004-12-16) + + +Noteworthy changes in version 1.3.2 (2003-05-27) +------------------------------------------------ + + * New "--gnupg" option (set by default) that disables --openpgp, + and the various --pgpX emulation options. This replaces + --no-openpgp, and --no-pgpX, and also means that GnuPG has + finally grown a --gnupg option to make GnuPG act like GnuPG. + + * A bug in key validation has been fixed. This bug only affects + keys with more than one user ID (photo IDs do not count here), + and results in all user IDs on a given key being treated with + the validity of the most-valid user ID on that key. + + * Notation names that do not contain a '@' are no longer allowed + unless --expert is set. This is to help prevent pollution of + the (as yet unused) IETF notation namespace. + + * Multiple trust models are now supported via the --trust-model + option. The options are "pgp" (web-of-trust plus trust + signatures), "classic" (web-of-trust only), and "always" + (identical to the --always-trust option). + + * The --personal-{cipher|digest|compression}-preferences are now + consulted to get default algorithms before resorting to the + last-ditch defaults of --s2k-cipher-algo, SHA1, and ZIP + respectively. This allows a user to set algorithms to use in a + safe manner so they are used when legal to do so, without + forcing them on for all messages. + + * New --primary-keyring option to designate the keyring that the + user wants new keys imported into. + + * --s2k-digest-algo is now used for all password mangling. + Earlier versions used both --s2k-digest-algo and --digest-algo + for passphrase mangling. + + * Handling of --hidden-recipient or --throw-keyid messages is now + easier - the user only needs to give their passphrase once, and + GnuPG will try it against all of the available secret keys. + + * Care is taken to prevent compiler optimization from removing + memory wiping code. + + * New option --no-mangle-dos-filenames so that filenames are not + truncated in the W32 version. + + * A "convert-from-106" script has been added. This is a simple + script that automates the conversion from a 1.0.6 or earlier + version of GnuPG to a 1.0.7 or later version. + + * Disabled keys are now skipped when selecting keys for + encryption. If you are using the --with-colons key listings to + detect disabled keys, please see doc/DETAILS for a minor format + change in this release. + + * Minor trustdb changes to make the trust calculations match + common usage. + + * New command "revuid" in the --edit-key menu to revoke a user ID. + This is a simpler interface to the old method (which still + works) of revoking the user ID self-signature. + + * Status VALIDSIG does now also print the primary key's + fingerprint, as well as the signature version, pubkey algorithm, + hash algorithm, and signature class. + + * Add read-only support for the SHA-256 hash, and optional + read-only support for the SHA-384 and SHA-512 hashes. + + * New option --enable-progress-filter for use with frontends. + + * DNS SRV records are used in HKP keyserver lookups to allow + administrators to load balance and select keyserver ports + automatically. This is as specified in + draft-shaw-openpgp-hkp-00.txt. + + * When using the "keyid!" syntax during a key export, only that + specified key is exported. If the key in question is a subkey, + the primary key plus only that subkey is exported. + + * configure --disable-xxx options to disable individual algorithms + at build time. This can be used to build a smaller gpg binary + for embedded uses where space is tight. See the README file for + the algorithms that can be used with this option, or use + --enable-minimal to build the smallest gpg possible (disables + all optional algorithms, disables keyserver access, and disables + photo IDs). + + * The keyserver no-modify flag on a key can now be displayed and + modified. + + * Note that the TIGER/192 digest algorithm is in the process of + being dropped from the OpenPGP standard. While this release of + GnuPG still contains it, it is disabled by default. To ensure + you will still be able to use your messages with future versions + of GnuPG and other OpenPGP programs, please do not use this + algorithm. + + +Noteworthy changes in version 1.3.1 (2002-11-12) +------------------------------------------------ + + * Trust signature support. This is based on the Maurer trust + model where a user can specify the trust level along with the + signature with multiple levels so users can delegate + certification ability to other users, possibly restricted by a + regular expression on the user ID. Note that full trust + signature support requires a regular expression parsing library. + The regexp code from glibc 2.3.1 is included for those platforms + that don't have working regexp functions available. The + configure option --disable-regex may be used to disable any + regular expression code, which will make GnuPG ignore any trust + signature with a regular expression included. + + * Two new commands --hidden-recipient (-R) and --hidden-encrypt-to + encrypt to a user, but hide the identity of that user. This is + the same functionality as --throw-keyid, but can be used on a + per-user basis. + + * Full algorithm names (e.g. "3DES", "SHA1", "ZIP") can now be + used interchangeably with the short algorithm names (e.g. "S2", + "H2", "Z1") anywhere algorithm names are used in GnuPG. + + +Noteworthy changes in version 1.3.0 (2002-10-18) +------------------------------------------------ + + * The last piece of internal keyserver support has been removed, + and now all keyserver access is done via the keyserver plugins. + There is also a newer keyserver protocol used between GnuPG and + the plugins, so plugins from earlier versions of GnuPG may not + work properly. + + * The HKP keyserver plugin supports the new machine-readable key + listing format for those keyservers that provide it. + + * When using a HKP keyserver with multiple DNS records (such as + wwwkeys.pgp.net which has the addresses of multiple servers + around the world), try all records until one succeeds. Note + that it depends on the LDAP library used whether the LDAP + keyserver plugin does this as well. + + * The library dependencies for OpenLDAP seem to change fairly + frequently, and GnuPG's configure script cannot guess all the + combinations. Use ./configure LDAPLIBS="-L libdir -l libs" to + override the script and use the libraries selected. + + * Secret keys generated with --export-secret-subkeys are now + indicated in key listings with a '#' after the "sec", and in + --with-colons listings by showing no capabilities (no lowercase + characters). + + * --trusted-key has been un-obsoleted, as it is useful for adding + ultimately trusted keys from the config file. It is identical + to using --edit and "trust" to change a key to ultimately + trusted. + + * Translations other than de are no longer distributed with the + development branch. This is due to the frequent text changes + during development, which cause the translations to rapidly go + out of date. + + +Version 1.2.8 (2006-12-07) +Version 1.2.7 (2004-12-27) +Version 1.2.6 (2004-08-25) +Version 1.2.5 (2004-07-26) +Version 1.2.4 (2003-12-23) +Version 1.2.3 (2003-08-21) +Version 1.2.2 (2003-05-01) +Version 1.2.1 (2002-10-25) +Version 1.2.0 (2002-09-21) + + +Noteworthy changes in version 1.1.92 (2002-09-11) +------------------------------------------------- + + * [IMPORTANT] The default configuration file is now + ~/.gnupg/gpg.conf. If an old ~/.gnupg/options is found it will + still be used. This change is required to have a more + consistent naming scheme with forthcoming tools. + + * The use of MDCs have increased. A MDC will be used if the + recipients directly request it, if the recipients have AES, + AES192, AES256, or TWOFISH in their cipher preferences, or if + the chosen cipher has a blocksize not equal to 64 bits + (currently this is also AES, AES192, AES256, and TWOFISH). + + * GnuPG will no longer automatically disable compression when + processing an already-compressed file unless a MDC is being + used. This is to give the message a certain amount of + resistance to the chosen-ciphertext attack while communicating + with other programs (most commonly PGP earlier than version 7.x) + that do not support MDCs. + + * The option --interactive now has the desired effect when + importing keys. + + * The file permission and ownership checks on files have been + clarified. Specifically, the homedir (usually ~/.gnupg) is + checked to protect everything within it. If the user specifies + keyrings outside this homedir, they are presumed to be shared + keyrings and therefore *not* checked. Configuration files + specified with the --options option and the IDEA cipher + extension specified with --load-extension are checked, along + with their enclosing directories. + + * The configure option --with-static-rnd=auto allows to build gpg + with all available entropy gathering modules included. At + runtime the best usable one will be selected from the list + linux, egd, unix. This is also the default for systems lacking + a /dev/random device. + + * The default character set is now taken from the current locale; + it can still be overridden by the --charset option. Using the + option -vvv shows the used character set. + + * [REMOVED] --emulate-checksum-bug and --emulate-3des-s2k-bug have + been removed. + + +Noteworthy changes in version 1.1.91 (2002-08-04) +------------------------------------------------- + + * All modules are now linked statically; the --load-extension + option is in general not useful anymore. The only exception is + to specify the deprecated idea cipher. + + * The IDEA plugin has changed. Previous versions of the IDEA + plugin will no longer work with GnuPG. However, the current + version of the plugin will work with earlier GnuPG versions. + + * When using --batch with one of the --delete-key commands, the + key must be specified by fingerprint. See the man page for + details. + + * There are now various ways to restrict the ability GnuPG has to + exec external programs (for the keyserver helpers or photo ID + viewers). Read the README file for the complete list. + + * New export option to leave off attribute packets (photo IDs) + during export. This is useful when exporting to HKP keyservers + which do not understand attribute packets. + + * New import option to repair during import the HKP keyserver + mangling multiple subkeys bug. Note that this cannot completely + repair the damaged key as some crucial data is removed by the + keyserver, but it does at least give you back one subkey. This + is on by default for keyserver --recv-keys, and off by default + for regular --import. + + * The keyserver helper programs now live in + /usr/[local/]libexec/gnupg by default. If you are upgrading + from 1.0.7, you might want to delete your old copies in + /usr/[local/]bin. If you use an OS that does not use libexec + for whatever reason, use configure --libexecdir=/usr/local/lib + to place the keyserver helpers there. + + * The LDAP keyserver handler now works properly with very old + (version 1) LDAP keyservers. + + +Noteworthy changes in version 1.1.90 (2002-07-01) +------------------------------------------------- + + * New commands: --personal-cipher-preferences, + --personal-digest-preferences, and + --personal-compress-preferences allow the user to specify which + algorithms are to be preferred. Note that this does not permit + using an algorithm that is not present in the recipient's + preferences (which would violate the OpenPGP standard). This + just allows sorting the preferences differently. + + * New "group" command to refer to several keys with one name. + + * A warning is issued if the user forces the use of an algorithm + that is not listed in the recipient's preferences. + + * Full revocation key (aka "designated revoker") support. + + * The preferred hash algorithms on a key are consulted when + encrypting a signed message to that key. Note that this is + disabled by default by a SHA1 preference in + --personal-digest-preferences. + + * --cert-digest-algo allows the user to specify the hash algorithm + to use when signing a key rather than the default SHA1 (or MD5 + for PGP2 keys). Do not use this feature unless you fully + understand the implications of this. + + * --pgp7 mode automatically sets all necessary options to ensure + that the resulting message will be usable by a user of PGP 7.x. + + * New --attribute-fd command for frontends and scripts to get the + contents of attribute packets (i.e. photos) + + * In expert mode, the user can now re-sign a v3 key with a v4 + self-signature. This does not change the v3 key into a v4 key, + but it does allow the user to use preferences, primary ID flags, + etc. + + * Significantly improved photo ID support on non-unixlike + platforms. + + * The version number has jumped ahead to 1.1.90 to skip over the + old version 1.1 and to get ready for the upcoming 1.2. + + * ElGamal sign and encrypt is not anymore allowed in the key + generation dialog unless in expert mode. RSA sign and encrypt + has been added with the same restrictions. + + * [W32] Keyserver access does work with Windows NT. + + +Noteworthy changes in version 1.0.7 (2002-04-29) +------------------------------------------------ + + * Secret keys are now stored and exported in a new format which + uses SHA-1 for integrity checks. This format renders the + Rosa/Klima attack useless. Other OpenPGP implementations might + not yet support this, so the option --simple-sk-checksum creates + the old vulnerable format. + + * The default cipher algorithm for encryption is now CAST5, + default hash algorithm is SHA-1. This will give us better + interoperability with other OpenPGP implementations. + + * Symmetric encrypted messages now use a fixed file size if + possible. This is a tradeoff: it breaks PGP 5, but fixes PGP 2, + 6, and 7. Note this was only an issue with RFC-1991 style + symmetric messages. + + * Photographic user ID support. This uses an external program to + view the images. + + * Enhanced keyserver support via keyserver "plugins". GnuPG comes + with plugins for the NAI LDAP keyserver as well as the HKP email + keyserver. It retains internal support for the HKP HTTP + keyserver. + + * Nonrevocable signatures are now supported. If a user signs a + key nonrevocably, this signature cannot be taken back so be + careful! + + * Multiple signature classes are usable when signing a key to + specify how carefully the key information (fingerprint, photo + ID, etc) was checked. + + * --pgp2 mode automatically sets all necessary options to ensure + that the resulting message will be usable by a user of PGP 2.x. + + * --pgp6 mode automatically sets all necessary options to ensure + that the resulting message will be usable by a user of PGP 6.x. + + * Signatures may now be given an expiration date. When signing a + key with an expiration date, the user is prompted whether they + want their signature to expire at the same time. + + * Revocation keys (designated revokers) are now supported if + present. There is currently no way to designate new keys as + designated revokers. + + * Permissions on the .gnupg directory and its files are checked + for safety. + + * --expert mode enables certain silly things such as signing a + revoked user id, expired key, or revoked key. + + * Some fixes to build cleanly under Cygwin32. + + * New tool gpgsplit to split OpenPGP data formats into packets. + + * New option --preserve-permissions. + + * Subkeys created in the future are not used for encryption or + signing unless the new option --ignore-valid-from is used. + + * Revoked user-IDs are not listed unless signatures are listed too + or we are in verbose mode. + + * There is no default comment string with ascii armors anymore + except for revocation certificates and --enarmor mode. + + * The command "primary" in the edit menu can be used to change the + primary UID, "setpref" and "updpref" can be used to change the + preferences. + + * Fixed the preference handling; since 1.0.5 they were erroneously + matched against against the latest user ID and not the given one. + + * RSA key generation. + + * Merged Stefan's patches for RISC OS in. See comments in + scripts/build-riscos. + + * It is now possible to sign and conventional encrypt a message (-cs). + + * The MDC feature flag is supported and can be set by using + the "updpref" edit command. + + * The status messages GOODSIG and BADSIG are now returning the primary + UID, encoded using %XX escaping (but with spaces left as spaces, + so that it should not break too much) + + * Support for GDBM based keyrings has been removed. + + * The entire keyring management has been revamped. + + * The way signature stati are store has changed so that v3 + signatures can be supported. To increase the speed of many + operations for existing keyrings you can use the new + --rebuild-keydb-caches command. + + * The entire key validation process (trustdb) has been revamped. + See the man page entries for --update-trustdb, --check-trustdb + and --no-auto-check-trustdb. + + * --trusted-keys is again obsolete, --edit can be used to set the + ownertrust of any key to ultimately trusted. + + * A subkey is never used to sign keys. + + * Read only keyrings are now handled as expected. + + +Noteworthy changes in version 1.0.6 (2001-05-29) +------------------------------------------------ + + * Security fix for a format string bug in the tty code. + + * Fixed format string bugs in all PO files. + + * Removed Russian translation due to too many bugs. The FTP + server has an unofficial but better translation in the contrib + directory. + + * Fixed expire time calculation and keyserver access. + + * The usual set of minor bug fixes and enhancements. + + * non-writable keyrings are now correctly handled. + + +Noteworthy changes in version 1.0.5 (2001-04-29) +------------------------------------------------ + + * WARNING: The semantics of --verify have changed to address a + problem with detached signature detection. --verify now ignores + signed material given on stdin unless this is requested by using + a "-" as the name for the file with the signed material. Please + check all your detached signature handling applications and make + sure that they don't pipe the signed material to stdin without + using a filename together with "-" on the the command line. + + * WARNING: Corrected hash calculation for input data larger than + 512M - it was just wrong, so you might notice bad signature in + some very big files. It may be wise to keep an old copy of + GnuPG around. + + * Secret keys are no longer imported unless you use the new option + --allow-secret-key-import. This is a kludge and future versions will + handle it in another way. + + * New command "showpref" in the --edit-key menu to show an easier + to understand preference listing. + + * There is now the notation of a primary user ID. For example, it + is printed with a signature verification as the first user ID; + revoked user IDs are not printed there anymore. In general the + primary user ID is the one with the latest self-signature. + + * New --charset=utf-8 to bypass all internal conversions. + + * Large File Support (LFS) is now working. + + * New options: --ignore-crc-error, --no-sig-create-check, + --no-sig-cache, --fixed-list-mode, --no-expensive-trust-checks, + --enable-special-filenames and --use-agent. See man page. + + * New command --pipemode, which can be used to run gpg as a + co-process. Currently only the verification of detached + signatures are working. See doc/DETAILS. + + * Keyserver support for the W32 version. + + * Rewritten key selection code so that GnuPG can better cope with + multiple subkeys, expire dates and so. The drawback is that it + is slower. + + * A whole lot of bug fixes. + + * The verification status of self-signatures are now cached. To + increase the speed of key list operations for existing keys you + can do the following in your GnuPG homedir (~/.gnupg): + cp pubring.gpg pubring.gpg.save && gpg --export-all >x && \ + rm pubring.gpg && gpg --import x + Only v4 keys (i.e not the old RSA keys) benefit from this caching. + + * New translations: Estonian, Turkish. + + +Noteworthy changes in version 1.0.4 (2000-10-17) +------------------------------------------------ + + * Fixed a serious bug which could lead to false signature verification + results when more than one signature is fed to gpg. This is the + primary reason for releasing this version. + + * New utility gpgv which is a stripped down version of gpg to + be used to verify signatures against a list of trusted keys. + + * Rijndael (AES) is now supported and listed with top preference. + + * --with-colons now works with --print-md[s]. + +Noteworthy changes in version 1.0.3 (2000-09-18) +------------------------------------------------ + + * Fixed problems with piping to/from other MS-Windows software + + * Expiration time of the primary key can be changed again. + + * Revoked user IDs are now marked in the output of --list-key + + * New options --show-session-key and --override-session-key + to help the British folks to somewhat minimize the danger + of this Orwellian RIP bill. + + * New options --merge-only and --try-all-secrets. + + * New configuration option --with-egd-socket. + + * The --trusted-key option is back after it left us with 0.9.5 + + * RSA is supported. Key generation does not yet work but will come + soon. + + * CAST5 and SHA-1 are now the default algorithms to protect the key + and for symmetric-only encryption. This should solve a couple + of compatibility problems because the old algorithms are optional + according to RFC2440 + + * Twofish and MDC enhanced encryption is now used. PGP 7 supports + this. Older versions of GnuPG don't support it, so they should be + upgraded to at least 1.0.2 + + +Noteworthy changes in version 1.0.2 (2000-07-12) +---------------------------------------------- + + * Fixed expiration handling of encryption keys. + + * Add an experimental feature to do unattended key generation. + + * The user is now asked for the reason of revocation as required + by the new OpenPGP draft. + + * There is a ~/.gnupg/random_seed file now which saves the + state of the internal RNG and increases system performance + somewhat. This way the full entropy source is only used in + cases were it is really required. + Use the option --no-random-seed-file to disable this feature. + + * New options --ignore-time-conflict and --lock-never. + + * Some fixes for the W32 version. + + * The entropy.dll is not anymore used by the W32 version but replaced + by code derived from Cryptlib. + + * Encryption is now much faster: About 2 times for 1k bit keys + and 8 times for 4k keys. + + * New encryption keys are generated in a way which allows a much + faster decryption. + + * New command --export-secret-subkeys which outputs the + the _primary_ key with it's secret parts deleted. This is + useful for automated decryption/signature creation as it + allows to keep the real secret primary key offline and + thereby protecting the key certificates and allowing to + create revocations for the subkeys. See the FAQ for a + procedure to install such secret keys. + + * Keygeneration now writes to the first writeable keyring or + as default to the one in the homedirectory. Prior versions + ignored all --keyring options. + + * New option --command-fd to take user input from a file descriptor; + to be used with --status-fd by software which uses GnuPG as a backend. + + * There is a new status PROGRESS which is used to show progress during + key generation. + + * Support for the new MDC encryption packets. To create them either + --force-mdc must be use or cipher algorithm with a blocksize other + than 64 bits is to be used. --openpgp currently disables MDC packets + entirely. This option should not yet be used. + + * New option --no-auto-key-retrieve to disable retrieving of + a missing public key from a keyserver, when a keyserver has been set. + + * Danish translation + +Noteworthy changes in version 1.0.1 (1999-12-16) +----------------------------------- + + * New command --verify-files. New option --fast-list-mode. + + * $http_proxy is now used when --honor-http-proxy is set. + + * Fixed some minor bugs and the problem with conventional encrypted + packets which did use the gpg v3 partial length headers. + + * Add Indonesian and Portugese translations. + + * Fixed a bug with symmetric-only encryption using the non-default 3DES. + The option --emulate-3des-s2k-bug may be used to decrypt documents + which have been encrypted this way; this should be done immediately + as this workaround will be remove in 1.1 + + * Can now handle (but not display) PGP's photo IDs. I don't know the + format of that packet but after stripping a few bytes from the start + it looks like a JPEG (at least my test data). Handling of this + package is required because otherwise it would mix up the + self signatures and you can't import those keys. + + * Passing non-ascii user IDs on the commandline should now work in all + cases. + + * New keys are now generated with an additional preference to Blowfish. + + * Removed the GNU Privacy Handbook from the distribution as it will go + into a separate one. + + +Noteworthy changes in version 1.0.0 (1999-09-07) +----------------------------------- + + * Add a very preliminary version of the GNU Privacy Handbook to + the distribution (lynx doc/gph/index.html). + + * Changed the version number to GnuPG 2001 ;-) + + +Noteworthy changes in version 0.9.11 (1999-09-03) +------------------------------------ + + * UTF-8 strings are now correctly printed (if --charset is set correctly). + Output of --with-colons remains C-style escaped UTF-8. + + * Workaround for a problem with PGP 5 detached signature in textmode. + + * Fixed a problem when importing new subkeys (duplicated signatures). + + +Noteworthy changes in version 0.9.10 (1999-07-23) +------------------------------------ + + * Some strange new options to help pgpgpg + + * Cleaned up the dox a bit. + + +Noteworthy changes in version 0.9.9 +----------------------------------- + + * New options --[no-]utf8-strings. + + * New edit-menu commands "enable" and "disable" for entire keys. + + * You will be asked for a filename if gpg cannot deduce one. + + * Changes to support libtool which is needed for the development + of libgcrypt. + + * New script tools/lspgpot to help transferring assigned + trustvalues from PGP to GnuPG. + + * New commands --lsign-key and made --sign-key a shortcut for --edit + and sign. + + * New options (#122--126 ;-) --[no-]default-recipient[-self], + --disable-{cipher,pubkey}-algo. See the man page. + + * Enhanced info output in case of multiple recipients and fixed exit code. + + * New option --allow-non-selfsigned-uid to work around a problem with + the German IN way of separating signing and encryption keys. + + +Noteworthy changes in version 0.9.8 (1999-06-26) +----------------------------------- + + * New subcommand "delsig" in the edit menu. + + * The name of the output file is not anymore the one which is + embedded in the processed message, but the used filename with + the extension stripped. To revert to the old behaviour you can + use the option --use-embedded-filename. + + * Another hack to cope with pgp2 generated detached signatures. + + * latin-2 character set works (--charset=iso-8859-2). + + * New option --with-key-data to list the public key parameters. + New option -N to insert notations and a --set-policy-url. + A couple of other options to allow reseting of options. + + * Better support for HPUX. + + +Noteworthy changes in version 0.9.7 (1999-05-23) +----------------------------------- + + * Add some work arounds for a bugs in pgp 2 which led to bad signatures + when used with canonical texts in some cases. + + * Enhanced some status outputs. + + +Noteworthy changes in version 0.9.6 (1999-05-06) +----------------------------------- + + * Twofish is now statically linked by default. The experimental 128 bit + version is now disabled. Full support will be available as soon as + the OpenPGP WG has decided on an interpretation of rfc2440. + + * Dropped support for the ancient Blowfish160 which is not OpenPGP. + + * Merged gpgm and gpg into one binary. + + * Add "revsig" and "revkey" commands to the edit menu. It is now + possible to revoke signature and subkeys. + + +Noteworthy changes in version 0.9.5 (1999-03-20) +----------------------------------- + + * New command "lsign" in the keyedit menu to create non-exportable + signatures. Removed --trusted-keys option. + + * A bunch of changes to the key validation code. + + * --list-trust-path now has an optional --with-colons format. + + * New command --recv-keys to import keys from an keyserver. + + +Noteworthy changes in version 0.9.4 (1999-03-08) +----------------------------------- + + * New configure option --enable-static-rnd=[egd|linux|unix|none] + to select a random gathering module for static linking. + + * The original text is now verbatim copied to a cleartext signed message. + + * Bugfixes but there are still a couple of bugs. + + +Noteworthy changes in version 0.9.3 (1999-02-19) +----------------------------------- + + * Changed the internal design of getkey which now allows a + efficient lookup of multiple keys and add a word match mode. + + * New options --[no-]encrypt-to. + + * Some changes to the configure stuff. Switched to automake 1.4. + Removed intl/ from CVS, autogen.sh now uses gettextize. + + * Preferences now include Twofish. Removed preference to Blowfish with + a special hack to suppress the "not listed in preferences" warning; + this is to allow us to switch completely to Twofish in the near future. + + * Changed the locking stuff. + + * Print all user ids of a good signature. + + +Noteworthy changes in version 0.9.2 (1999-01-01) +----------------------------------- + + * add some additional time warp checks. + + * Option --keyserver and command --send-keys to utilize HKP servers. + + * Upgraded to zlib 1.1.3 and fixed an inflate bug + + * More cleanup on the cleartext signatures. + + +Noteworthy changes in version 0.9.1 (1999-01-01) +----------------------------------- + + * Polish language support. + + * When querying the passphrase, the key ID of the primary key is + displayed along with the one of the used secondary key. + + * Fixed a bug occurring when decrypting pgp 5 encrypted messages, + fixed an infinite loop bug in the 3DES code and in the code + which looks for trusted signatures. + + * Fixed a bug in the mpi library which caused signatures not to + compare okay. + + * Rewrote the handling of cleartext signatures; the code is now + better maintainable (I hope so). + + * New status output VALIDSIG only for valid signatures together + with the fingerprint of the signer's key. + + +Noteworthy changes in version 0.9.0 (1998-12-23) +----------------------------------- + + * --export does now only exports rfc2440 compatible keys; the + old behaviour is available with --export-all. + Generation of v3 ElGamal (sign and encrypt) keys is not longer + supported. + + * Fixed the uncompress bug. + + * Rewrote the rndunix module. There are two environment variables + used for debugging now: GNUPG_RNDUNIX_DBG give the file to write + debugging information (use "-" for stdout) and if GNUPG_RNDUNIX_DBGALL + is set, all programs which are only tried are also printed. + + * New option --escape-from-lines to "dash-escape" "From " lines to + prevent mailers to change them to ">From ". This is not enabled by + default because it is not in compliance with rfc2440 - however, you + should turn it on. + + +Noteworthy changes in version 0.4.5 (1998-12-08) +----------------------------------- + + * The keyrings and the trustdb is now locked, so that + other GnuPG processes won't damage these files. You + may want to put the option --lock-once into your options file. + + * The latest self-signatures are now used; this enables --import + to see updated preferences etc. + + * Import of subkeys should now work. + + * Random gathering modules may now be loaded as extensions. Add + such a module for most Unices but it is very experimental! + + * Brazilian language support. + + +Noteworthy changes in version 0.4.4 (1998-11-20) +----------------------------------- + + * Fixed the way the key expiration time is stored. If you have + an expiration time on your key you should fix it with --edit-key + and the command "expire". I apologize for this inconvenience. + + * Add option --charset to support "koi8-r" encoding of user ids. + (Not yet tested). + + * Preferences should now work again. You should run + "gpgm --check-trustdb \*" to rebuild all preferences. + + * Checking of certificates should now work but this needs a lot + of testing. Key validation values are now cached in the + trustdb; they should be recalculated as needed, but you may + use --check-trustdb or --update-trustdb to do this. + + * Spanish translation by Urko Lusa. + + * Patch files are from now on signed. See the man page + for the new option --not-dash-escaped. + + * New syntax: --edit-key [] + If you run it without --batch the commands are executed and then + you are put into normal mode unless you use "quit" or "save" as + one of the commands. When in batch mode, the program quits after + the last command, so you have to use "save" if you did some changes. + It does not yet work completely, but may be used to list so the + keys etc. + + +Noteworthy changes in version 0.4.3 (1998-11-08) +----------------------------------- + + * Fixed the gettext configure bug. + + * Kludge for RSA keys: keyid and length of a RSA key are + correctly reported, but you get an error if you try to use + this key (If you do not have the non-US version). + + * Experimental support for keyrings stored in a GDBM database. + This is *much* faster than a standard keyring. You will notice + that the import gets slower with time; the reason is that all + new keys are used to verify signatures of previous inserted + keys. Use "--keyring gnupg-gdbm:". This is + not (yet) supported for secret keys. + + * A Russian language file in the distribution (alternatives are in + the contrib directory of the FTP servers) + + * commandline option processing now works as expected for GNU programs + with the exception that you can't mix options and normal arguments. + + * Now --list-key lists all matching keys. This is needed in some + other places too. + + +Noteworthy changes in version 0.4.2 (1998-10-18) +----------------------------------- + + * This is only a snapshot: There are still a few bugs. + + * Fixed this huge memory leak. + + * Redesigned the trust database: You should run "gpgm --check-trustdb". + New command --update-trustdb, which adds new key from the public + keyring into your trustdb + + * Fixed a bug in the armor code, leading to invalid packet errors. + (a workaround for this was to use --no-armor). The shorten line + length (64 instead of 72) fixes a problem with pgp5 and keyservers. + + * comment packets are not anymore generated. "--export" filters + them out. One Exception: The comment packets in a secret keyring + are still used because they carry the factorization of the public + prime product. + + * --import now only looks for KEYBLOCK headers, so you can now simply + remove the "- " in front of such a header if someone accidentally signed + such a message or the keyblock is part of a cleartext signed message. + + * --with-colons now lists the key expiration time and not anymore + the valid period. + + * Some keyblocks created with old releases have a wrong sequence + of packets, so that the keyservers don't accept these keys. + Simply using "--edit-key" fixes the problem. + + * New option --force-v3-sigs to generate signed messages which are + compatible to PGP 5. + + * Add some code to support DLD (for non ELF systems) - but this is + not tested because my BSD box is currently broken. + + * New command "expire" in the edit-key menu. + + + +Noteworthy changes in version 0.4.1 (1998-10-07) +----------------------------------- + + * A secondary key is used when the primary key is specified but cannot + be used for the operation (if it is a sign-only key). + + * GNUPG can now handle concatenated armored messages: There is still a + bug if different kinds of messages are mixed. + + * Iterated+Salted passphrases now work. If want to be sure that PGP5 + is able to handle them you may want to use the options + "--s2k-mode 3 --s2k-cipher-algo cast5 --s2k-digest-algo sha1" + when changing a passphrase. + + * doc/OpenPGP talks about OpenPGP compliance, doc/HACKING gives + a few hints about the internal structure. + + * Checked gnupg against the August 1998 draft (07) and I believe + it is in compliance with this document (except for one point). + + * Fixed some bugs in the import merging code and rewrote some + code for the trustdb. + + +Noteworthy changes in version 0.4.0 (1998-09-18) +----------------------------------- + + * Triple DES is now supported. Michael Roth did this piece of + needed work. We have now all the coded needed to be OpenPGP + compliant. + + * Added a simple rpm spec file (see INSTALL). + + * detached and armored signatures are now using "PGP SIGNATURE", + except when --rfc1991 is used. + + * All times which are not in the yyyy-mm-dd format are now printed + in local time. + + +Noteworthy changes in version 0.3.5 (1998-09-14) +----------------------------------- + + * New option --throw-keyid to create anonymous enciphered messages. + If gpg detects such a message it tires all available secret keys + in turn so decode it. This is a gnupg extension and not in OpenPGP + but it has been discussed there and afaik some products use this + scheme too (Suggested by Nimrod Zimmerman). + + * Fixed a bug with 5 byte length headers. + + * --delete-[secret-]key is now also available in gpgm. + + * cleartext signatures are not anymore converted to LF only. + + * Fixed a trustdb problem. Run "gpgm --check-trustdb" to fix old + trust dbs. + + * Building in another directory should now work. + + * Weak key detection mechanism (Niklas Hernaeus). + + +Noteworthy changes in version 0.3.4 (1998-08-11) +----------------------------------- + + * New options --comment and --set-filename; see g10/OPTIONS + + * yes/no, y/n localized. + + * Fixed some bugs. + + +Noteworthy changes in version 0.3.3 (1998-08-08) +----------------------------------- + + * IMPORTANT: I found yet another bug in the way the secret keys + are encrypted - I did it the way pgp 2.x did it, but OpenPGP + and pgp 5.x specify another (in some aspects simpler) method. + To convert your secret keys you have to do this: + 1. Build the new release but don't install it and keep + a copy of the old program. + 2. Disable the network, make sure that you are the only + user, be sure that there are no Trojan horses etc .... + 3. Use your old gpg (version 0.3.[12]) and set the + passphrases of ALL your secret keys to empty! + (gpg --change-passphrase your-user-id). + 4. Save your ownertrusts (see the next point) + 5. rm ~/.gnupg/trustdb.gpg + 6. install the new version of gpg (0.3.3) + 7. For every secret key call "gpg --edit-key your-user-id", + enter "passwd" at the prompt, follow the instructions and + change your password back, enter "save" to store it. + 8. Restore the ownertrust (see next point). + + * The format of the trust database has changed; you must delete + the old one, so gnupg can create a new one. + IMPORTANT: Use version 0.3.[12] to save your assigned ownertrusts + ("gpgm --list-ownertrust >saved-trust"); then build this new version + and restore the ownertrust with this new version + ("gpgm --import-ownertrust saved-trust"). Please note that + --list-ownertrust has been renamed to --export-ownertrust in this + release and it does now only export defined ownertrusts. + + * The command --edit-key now provides a commandline driven menu + which can be used for various tasks. --sign-key is only an + an alias to --edit-key and maybe removed in future: use the + command "sign" of this new menu - you can select which user ids + you want to sign. + + * Alternate user ids can now be created an signed. + + * Owner trust values can now be changed with --edit-key (trust) + + * GNUPG can now run as a coprocess; this enables sophisticated + frontends. tools/shmtest.c is a simple sample implementation. + This needs some more work: all tty_xxx() are to be replaced + by cpr_xxx() and some changes in the display logics is needed. + + * Removed options --gen-prime and --gen-random. + + * Removed option --add-key; use --edit-key instead. + + * Removed option --change-passphrase; use --edit-key instead. + + * Signatures are now checked even if the output file could not + be created. Command "--verify" tries to find the detached data. + + * gpg now disables core dumps. + + * compress and symmetric cipher preferences are now used. + Because there is no 3DES yet, this is replaced by Blowfish. + + * We have added the Twofish as an experimental cipher algorithm. + Many thanks to Matthew Skala for doing this work. + Twofish is the AES submission from Schneier et al.; see + "www.counterpane.com/twofish.html" for more information. + + * Started with a help system: If you enter a question mark at some + prompt; you should get a specific help for this prompt. + + * There is no more backup copy of the secret keyring. + + * A lot of new bugs. I think this release is not as stable as + the previous one. + + +Noteworthy changes in version 0.3.2 (1998-07-09) +----------------------------------- + + * Fixed some bugs when using --textmode (-seat) + + * Now displays the trust status of a positive verified message. + + * Keyrings are now scanned in the sequence they are added with + --[secret-]keyring. Note that the default keyring is implicitly + added as the very first one unless --no-default-keyring is used. + + * Fixed setuid and dlopen bug. + + +Noteworthy changes in version 0.3.1 (1998-07-06) +----------------------------------- + + * Partial headers are now written in the OpenPGP format if + a key in a v4 packet is used. + + * Removed some unused options, removed the gnupg.sig stuff. + + * Key lookup by name now returns a key which can be used for + the desired action. + + * New options --list-ownertrust (gpgm) to make a backup copy + of the ownertrust values you assigned. + + * clear signature headers are now in compliance with OpenPGP. + + +Noteworthy changes in version 0.3.0 (1998-06-25) +----------------------------------- + + * New option --emulate-checksum-bug. If your passphrase does not + work anymore, use this option and --change-passphrase to rewrite + your passphrase. + + * More complete v4 key support: Preferences and expiration time + is set into the self signature. + + * Key generation defaults to DSA/ElGamal keys, so that new keys are + interoperable with pgp5 + + * DSA key generation is faster and key generation does not anymore + remove entropy from the random generator (the primes are public + parameters, so there is really no need for a cryptographic secure + prime number generator which we had used). + + * A complete new structure for representing the key parameters. + + * Removed most public key knowledge into the cipher library. + + * Support for dynamic loading of new algorithms. + + * Moved tiger to an extension module. + + +Noteworthy changes in version 0.2.19 (1998-05-29) +------------------------------------ + + * Replaced /dev/urandom in checks with new tool mk-tdata. + + * Some assembler file cleanups; some more functions for the Alpha. + + * Tiger has now the OpenPGP assigned number 6. Because the OID has + changed, old signatures using this algorithm can't be verified. + + * gnupg now encrypts the compressed packed and not any longer in the + reverse order; anyway it can decrypt both versions. Thanks to Tom + for telling me this (not security related) bug. + + * --add-key works and you are now able to generate subkeys. + + * It is now possible to generate ElGamal keys in v4 packets to create + valid OpenPGP keys. + + * Some new features for better integration into MUAs. + + +Noteworthy changes in version 0.2.18 (1998-05-15) +------------------------------------ + + * Splitted cipher/random.c, add new option "--disable-dev-random" + to configure to support the development of a random source for + other systems. Prepared sourcefiles rand-unix.c, rand-w32.c + and rand-dummy.c (which is used to allow compilation on systems + without a random source). + + * Fixed a small bug in the key generation (it was possible that 48 bits + of a key were not taken from the random pool) + + * Add key generation for DSA and v4 signatures. + + * Add a function trap_unaligned(), so that a SIGBUS is issued on + Alphas and not the slow emulation code is used. And success: rmd160 + raised a SIGBUS. + + * Enhanced the formatting facility of argparse and changed the use of + \r,\v to @ because gettext does not like it. + + * New option "--compress-algo 1" to allow the creation of compressed + messages which are readable by PGP and "--print-md" (gpgm) to make + speed measurement easier. + + +Noteworthy changes in version 0.2.17 (1998-05-04) +------------------------------------ + + * Comment packets are now of private type 61. + + * Passphrase code still used a 160 bit blowfish key, added a + silly workaround. Please change your passphrase again - sorry. + + * Conventional encryption now uses a type 3 packet to describe the + used algorithms. + + * The new algorithm number for Blowfish is 20, 16 is still used for + encryption only; for signing it is only used when it is in a v3 packet, + so that GNUPG keys are still valid. + + +Noteworthy changes in version 0.2.16 (1998-04-28) +------------------------------------ + + * Add experimental support for the TIGER/192 message digest algorithm. + (But there is only a dummy ASN OID). + + * Standard cipher is now Blowfish with 128 bit key in OpenPGP's CFB + mode. I renamed the old cipher to Blowfish160. Because the OpenPGP + group refused to assign me a number for Blowfish160, I have to + drop support for this in the future. You should use + "--change-passphrase" to recode your current passphrase with 128 + bit Blowfish. + + +Noteworthy changes in version 0.2.15 (1998-04-09) +------------------------------------ + + * Fixed a bug with the old checksum calculation for secret keys. + If you run the program without --batch, a warning does inform + you if your secret key needs to be converted; simply use + --change-passphrase to recalculate the checksum. Please do this + soon, as the compatible mode will be removed sometime in the future. + + * CAST5 works (using the PGP's special CFB mode). + + * Again somewhat more PGP 5 compatible. + + * Some new test cases + +Noteworthy changes in version 0.2.14 (1998-04-02) +------------------------------------ + + * Changed the internal handling of keyrings. + + * Add support to list PGP 5 keyrings with subkeys + + * Timestamps of signatures are now verified. + + * A expiration time can now be specified during key generation. + + * Some speedups for Blowfish and SHA-1, rewrote SHA-1 transform. + Reduced the amount of random bytes needed for key generation in + some cases. + + +Noteworthy changes in version 0.2.13 (1998-03-10) +------------------------------------ + + * Verify of DSA signatures works. + + * Re-implemented the slower random number generator. + + +Noteworthy changes in version 0.2.12 (1998-03-07) +------------------------------------ + + * --delete-key checks that there is no secret key. The new + option --delete-secret-key maybe used to delete a secret key. + + * "-kv" now works as expected. Options "--list-{keys,sigs]" + and "--check-sigs" are now working. + + * New options "--verify" and "--decrypt" to better support integration + into MUAs (partly done for Mutt). + + * New option "--with-colons" to make parsing of key lists easier. + +Noteworthy changes in version 0.2.11 (1998-03-02) +------------------------------------ + + * GPG now asks for a recipient's name if option "-r" is not used. + + * If there is no good trust path, the program asks whether to use + the public keys anyway. + + * "--delete-key" works for public keys. What semantics shall I use + when there is a secret key too? Delete the secret key or leave him + and auto-regenerate the public key, next time the secret key is used? + +Noteworthy changes in version 0.2.10 (1998-02-27) +------------------------------------ + + * Code for the alpha is much faster (about 20 times); the data + was misaligned and the kernel traps this, so nearly all time + was used by system to trap the misalignments and to write + syslog messages. Shame on me and thanks to Ralph for + pointing me at this while drinking some beer yesterday. + + * Changed some configure options and add an option + --disable-m-guard to remove the memory checking code + and to compile everything with optimization on. + + * New environment variable GNUPGHOME, which can be used to set + another homedir than ~/.gnupg. Changed default homedir for + Windoze version to c:/gnupg. + + * Fixed detached signatures; detached PGP signatures caused a SEGV. + + * The Windoze version works (as usual w/o a strong RNG). + + +Noteworthy changes in version 0.2.9 (1998-02-26) +----------------------------------- + + * Fixed FreeBSD bug. + + * Added a simple man page. + + * Switched to automake1.2f and a newer gettext. + +Noteworthy changes in version 0.2.8 (1998-02-24) +----------------------------------- + + * Changed the name to GNUPG, the binaries are called gpg and gpgm. + You must rename rename the directory "~/.g10" to ~/.gnupg/, rename + {pub,sec}ring.g10 to {pub,sec}ring.gpg, trustdb.g10 to trustdb.gpg + and g10.sig to gnupg.sig. + + * New or changed passphrases are now salted. + + +Noteworthy changes in version 0.2.7 (1998-02-18) +----------------------------------- + + * New command "gen-revoke" to create a key revocation certificate. + + * New option "homedir" to set the homedir (which defaults to "~/.g10"). + This directory is created if it does not exists (only the last + part of the name and not the complete hierarchy) + + * Command "import" works. (Try: "finger gcrypt@ftp.guug.de|g10 --import") + + * New commands "dearmor/enarmor" for g10maint. These are mainly + used for internal test purposes. + + * Option --version now conforming to the GNU standards and lists + the available ciphers, message digests and public key algorithms. + + * Assembler code for m68k (not tested). + + * "make check" works. + +Noteworthy changes in version 0.2.6 (1998-02-13) +----------------------------------- + + * Option "--export" works. + + +Noteworthy changes in version 0.2.5 (1998-02-12) +----------------------------------- + + * Added zlib for systems which don't have it. + Use "./configure --with-zlib" to link with the static version. + + * Generalized some more functions and rewrote the encoding of + message digests into MPIs. + + * Enhanced the checkit script + + +Noteworthy changes in version 0.2.4 (1998-02-11) +----------------------------------- + + * nearly doubled the speed of the ElGamal signature verification. + + * backup copies of keyrings are created. + + * assembler stuff for Pentium; gives about 15% better performance. + + * fixed a lot of bugs. + + +Noteworthy changes in version 0.2.3 (1998-02-09) +----------------------------------- + + * Found a bug in the calculation of ELG fingerprints. This is now + fixed, but all existing fingerprints and keyids for ELG keys + are not any more valid. + + * armor should now work; including clear signed text. + + * moved some options to the new program g10maint + + * It's now 64 bit clean and runs fine on an alpha--linux. + + * Key generation is much faster now. I fixed this by using not + so strong random number for the primes (this was a bug because the + ElGamal primes are public parameters and it does not make sense + to generate them from strong random). The real secret is the x value + which is still generated from strong (okay: /dev/random) random bits. + + * added option "--status-fd": see g10/OPTIONS + + * We have secure memory on systems which support mlock(). + It is not complete yet, because we do not have signal handler + which does a cleanup in very case. + We should also check the ulimit for the user in the case + that the admin does not have set a limit on locked pages. + + * started with internationalization support. + + * The logic to handle the web of trust is now implemented. It is + has some bugs; but I'm going to change the algorithm anyway. + It works by calculating the trustlevel on the fly. It may ask + you to provide trust parameters if the calculated trust probability + is too low. I will write a paper which discusses this new approach. + + * a couple of changes to the configure script. + + * New option "--quick-random" which uses a much quicker random + number generator. Keys generated while this option is in effect + are flags with "INSECURE!" in the user-id. This is a development + only option. + + * Read support for new version packets (OpenPGP). + + * Comment packets are now of correct OpenPGP type 16. Old comment + packets written by G10 are detected because they always start with + a hash which is an invalid version byte. + + * The string "(INSECURE!)" is appended to a new user-id if this + is generated on a system without a good random number generator. + + +Version 0.2.2 (1998-02-09) +Version 0.2.1 (1998-01-28) +Version 0.2.0 (1998-01-25) +Version 0.1.3 (1998-01-12) +Version 0.1.2 (1998-01-07) +Version 0.1.1 (1998-01-07) +Version 0.1.0 (1998-01-05) +Version 0.0.0 (1997-12-20) + + + Copyright (C) 1998-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2016 Werner Koch + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/README b/README new file mode 100644 index 0000000..18cc316 --- /dev/null +++ b/README @@ -0,0 +1,244 @@ + The GNU Privacy Guard 2 + ========================= + Version 2.1 + + Copyright 1997-2016 Werner Koch + Copyright 1998-2016 Free Software Foundation, Inc. + + +* INTRODUCTION + + GnuPG is a complete and free implementation of the OpenPGP standard + as defined by RFC4880 (also known as PGP). GnuPG enables encryption + and signing of data and communication, and features a versatile key + management system as well as access modules for public key + directories. + + GnuPG, also known as GPG, is a command line tool with features for + easy integration with other applications. A wealth of frontend + applications and libraries are available that make use of GnuPG. + Starting with version 2 GnuPG provides support for S/MIME and Secure + Shell in addition to OpenPGP. + + GnuPG is Free Software (meaning that it respects your freedom). It + can be freely used, modified and distributed under the terms of the + GNU General Public License. + + We are currently maintaining three branches of GnuPG: + + - 2.1 (i.e. this release) is the latest stable version with a lot of + new features. + + - 2.0 is an often used stable version. This branch will reach + end-of-life on 2017-12-31. + + - 1.4 is the old standalone version which is most suitable for older + or embedded platforms. + + You may not install 2.1 and 2.0 at the same time. However, it is + possible to install 1.4 along with any of the 2.x versions. + + +* BUILD INSTRUCTIONS + + GnuPG 2.1 depends on the following GnuPG related packages: + + npth (ftp://ftp.gnupg.org/gcrypt/npth/) + libgpg-error (ftp://ftp.gnupg.org/gcrypt/libgpg-error/) + libgcrypt (ftp://ftp.gnupg.org/gcrypt/libgcrypt/) + libksba (ftp://ftp.gnupg.org/gcrypt/libksba/) + libassuan (ftp://ftp.gnupg.org/gcrypt/libassuan/) + + You should get the latest versions of course, the GnuPG configure + script complains if a version is not sufficient. + + For some advanced features several other libraries are required. + The configure script prints diagnostic messages if one of these + libraries is not available and a feature will not be available.. + + You also need the Pinentry package for most functions of GnuPG; + however it is not a build requirement. Pinentry is available at + ftp://ftp.gnupg.org/gcrypt/pinentry/ . + + After building and installing the above packages in the order as + given above, you may continue with GnuPG installation (you may also + just try to build GnuPG to see whether your already installed + versions are sufficient). + + As with all packages, you just have to do + + ./configure + make + make install + + (Before doing install you might need to become root.) + + If everything succeeds, you have a working GnuPG with support for + OpenPGP, S/MIME, ssh-agent, and smartcards. Note that there is no + binary gpg but a gpg2 so that this package won't conflict with a + GnuPG 1.4 installation. gpg2 behaves just like gpg. + + In case of problem please ask on the gnupg-users@gnupg.org mailing + list for advise. + + Instruction on how to build for Windows can be found in the file + doc/HACKING in the section "How to build an installer for Windows". + This requires some experience as developer. + + Note that the PKITS tests are always skipped unless you copy the + PKITS test data file into the tests/pkits directory. There is no + need to run these test and some of them may even fail because the + test scripts are not yet complete. + + You may run + + gpgconf --list-dirs + + to view the default directories used by GnuPG. + + To quickly build all required software without installing it, the + Speedo method may be used: + + make -f build-aux/speedo.mk native + + This method downloads all required libraries and does a native build + of GnuPG to PLAY/inst/. GNU make is required and you need to set + LD_LIBRARY_PATH to $(pwd)/PLAY/inst/lib to test the binaries. + +** Specific build problems on some machines: + +*** Apple OSX 10.x using XCode + + On some versions the correct location of a header file can't be + detected by configure. To fix that you should run configure like + this + + ./configure gl_cv_absolute_stdint_h=/usr/include/stdint.h + + Add other options as needed. + + +*** Systems without a full C99 compiler + + If you run into problems with your compiler complaining about dns.c + you may use + + ./configure --disable-libdns + + Add other options as needed. + + +* MIGRATION from 1.4 or 2.0 to 2.1 + + The major change in 2.1 is gpg-agent taking care of the OpenPGP + secret keys (those managed by GPG). The former file "secring.gpg" + will not be used anymore. Newly generated keys are stored in the + agent's key store directory "~/.gnupg/private-keys-v1.d/". The + first time gpg needs a secret key it checks whether a "secring.gpg" + exists and copies them to the new store. The old secring.gpg is + kept for use by older versions of gpg. + + Note that gpg-agent now uses a fixed socket. All tools will start + the gpg-agent as needed. The formerly used environment variable + GPG_AGENT_INFO is ignored by 2.1. The SSH_AUTH_SOCK environment + variable should be set to a fixed value. + + The Dirmngr is now part of GnuPG proper and also used to access + OpenPGP keyservers. The directory layout of Dirmngr changed to make + use of the GnuPG directories. Dirmngr is started by gpg or gpgsm as + needed. There is no more need to install a separate Dirmngr package. + + +* DOCUMENTATION + + The complete documentation is in the texinfo manual named + `gnupg.info'. Run "info gnupg" to read it. If you want a a + printable copy of the manual, change to the "doc" directory and + enter "make pdf" For a HTML version enter "make html" and point your + browser to gnupg.html/index.html. Standard man pages for all + components are provided as well. An online version of the manual is + available at [[https://gnupg.org/documentation/manuals/gnupg/]] . A + version of the manual pertaining to the current development snapshot + is at [[https://gnupg.org/documentation/manuals/gnupg-devel/]] . + + +* GnuPG 1.4 and GnuPG 2.0 + + GnuPG 2.0 is a newer version of GnuPG with additional support for + S/MIME. It has a different design philosophy that splits + functionality up into several modules. Both versions may be + installed simultaneously without any conflict (gpg is called gpg2 in + GnuPG 2). In fact, the gpg version from GnuPG 1.4 is able to make + use of the gpg-agent as included in GnuPG 2 and allows for seamless + passphrase caching. The advantage of GnuPG 1.4 is its smaller size + and no dependency on other modules at run and build time. + + +* HOW TO GET MORE INFORMATION + + A description of new features and changes in version 2.1 can be + found in the file "doc/whats-new-in-2.1.txt" and online at + "https://gnupg.org/faq/whats-new-in-2.1.html" . + + The primary WWW page is "https://www.gnupg.org" + or using Tor "http://ic6au7wa3f6naxjq.onion" + The primary FTP site is "ftp://ftp.gnupg.org/gcrypt/" + + See [[https://gnupg.org/download/mirrors.html]] for a list of + mirrors and use them if possible. You may also find GnuPG mirrored + on some of the regular GNU mirrors. + + We have some mailing lists dedicated to GnuPG: + + gnupg-announce@gnupg.org For important announcements like new + versions and such stuff. This is a + moderated list and has very low traffic. + Do not post to this list. + + gnupg-users@gnupg.org For general user discussion and + help (English). + + gnupg-de@gnupg.org German speaking counterpart of + gnupg-users. + + gnupg-ru@gnupg.org Russian speaking counterpart of + gnupg-users. + + gnupg-devel@gnupg.org GnuPG developers main forum. + + You subscribe to one of the list by sending mail with a subject of + "subscribe" to x-request@gnupg.org, where x is the name of the + mailing list (gnupg-announce, gnupg-users, etc.). See + https://www.gnupg.org/documentation/mailing-lists.html for archives + of the mailing lists. + + Please direct bug reports to http://bugs.gnupg.org or post them + direct to the mailing list . + + Please direct questions about GnuPG to the users mailing list or one + of the PGP newsgroups; please do not direct questions to one of the + authors directly as we are busy working on improvements and bug + fixes. The English and German mailing lists are watched by the + authors and we try to answer questions when time allows us. + + Commercial grade support for GnuPG is available; for a listing of + offers see https://www.gnupg.org/service.html . Maintaining and + improving GnuPG requires a lot of time. Since 2001, g10 Code GmbH, + a German company owned and headed by GnuPG's principal author Werner + Koch, is bearing the majority of these costs. To keep GnuPG in a + healthy state, they need your support. + + Please consider to donate at https://gnupg.org/donate/ . + + +# This file is Free Software; as a special exception the authors gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. For conditions +# of the whole package, please see the file COPYING. 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. +# +# Local Variables: +# mode:org +# End: diff --git a/README.GIT b/README.GIT new file mode 100644 index 0000000..57dab7a --- /dev/null +++ b/README.GIT @@ -0,0 +1,49 @@ +If you are building from GIT, run the script + +./autogen.sh + +first, to make sure that you have all the necessary maintainer tools +are installed and to build the actual configuration files. If you +have just checked out from GIT, you should add the option "--force" to +autogen.sh so that meta data is noticed by autom4te.cache. Then run + +./configure --enable-maintainer-mode + +followed by the usual make. + +If autogen.sh complains about insufficient versions of the required +tools, or the tools are not installed, you may use environment +variables to override the default tool names: + + AUTOMAKE_SUFFIX is used as a suffix for all tools from the automake + package. For example + AUTOMAKE_SUFFIX="-1.14" ./autogen.sh + uses "automake-1.14" and "aclocal-1.14. + AUTOMAKE_PREFIX is used as a prefix for all tools from the automake + page and may be combined with AUTOMAKE_SUFFIX. e.g.: + AUTOMAKE_PREFIX=/usr/foo/bin ./autogen.sh + uses "automake" and "aclocal" in the /usr/foo/bin + directory. + AUTOCONF_SUFFIX is used as a suffix for all tools from the automake + package + AUTOCONF_PREFIX is used as a prefix for all tools from the automake + package + GETTEXT_SUFFIX is used as a suffix for all tools from the gettext + package + GETTEXT_PREFIX is used as a prefix for all tools from the gettext + package + +It is also possible to use the variable name AUTOMAKE, AUTOCONF, +ACLOCAL, AUTOHEADER, GETTEXT and MSGMERGE to directly specify the name +of the programs to run. It is however better to use the suffix and +prefix forms as described above because that does not require +knowledge about the actual tools used by autogen.sh. + + +Please don't use autopoint, libtoolize or autoreconf unless you are +the current maintainer and want to update the standard configuration +files. All those files should be in GIT and only updated manually +if the maintainer decides that newer versions are required. The +maintainer should also make sure that the required version of automake +et al. are properly indicated at the top of configure.ac and take care +to copy the files and not merely use symlinks. diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..d366707 --- /dev/null +++ b/THANKS @@ -0,0 +1,297 @@ +GnuPG was originally written by Werner Koch. Other people contributed +by reporting problems, suggesting various improvements or submitting +actual code. Here is a list of those people. Help us keep it +complete and free of errors. + + +Adam Mitchell adam at cafe21.org +Alain Guibert alguibert+gpd at free.fr +Albert Chin china at thewrittenword.com +Alec Habig habig at budoe2.bu.edu +Alexander Belopolsky belopolsky at mac.com +Allan Clark allanc at sco.com +Anand Kumria wildfire at progsoc.uts.edu.au +Andreas Haumer andreas at xss.co.at +Andrew J. Schorr aschorr at telemetry-investments.com +Anthony Carrico acarrico at memebeam.org +Anthony Mulcahy anthony at kcn.ne.jp +Ariel T Glenn ariel at columbia.edu +ARIGA Seiji ariga at os.rim.or.jp +Benjamin Donnachie benjamin at py-soft.co.uk +Bernhard Herzog bh at intevation.de +Bernard Leak thisisnotapipe.a-t.hotmail.com +Bernhard Reiter bernhard at intevation.de +Billy Halsey bshalsey at paxoo.com +Bob Dunlop bob at xyzzy.org.uk +Bob Mathews bobmathews at mindspring.com +Bodo Moeller Bodo_Moeller at public.uni-hamburg.de +Brendan O'Dea bod at debian.org +Brenno de Winter brenno at dewinter.com +Brian M. Carlson karlsson at hal-pc.org +Brian Moore bem at cmc.net +Brian Warner warner at lothar.com +Bryan Fullerton bryanf at samurai.com +Bryce Nichols bryce at bnichols.org +Carl Meijer carlm at prism.co.za +Caskey L. Dickson caskey at technocage.com +Cees van de Griend cees-list at griend.xs4all.nl +Charles Levert charles at comm.polymtl.ca +Charly Avital shavital at mac.com +Chip Salzenberg chip at valinux.com +Chris Adams cmadams at hiwaay.net +Christian Biere christianbiere at gmx.de +Christian Kurz shorty at debian.org +Christian von Roques roques at pond.sub.org +Christopher Oliver oliver at fritz.traverse.net +Christian Recktenwald chris at citecs.de +Colin Tuckley colin at tuckley.org +Daiki Ueno ueno at unixuser.org +Dan Winship danw at helixcode.com +Daniel Eisenbud eisenbud at cs.swarthmore.edu +Daniel Kahn Gillmor dkg at fifthhorseman dot net +Daniel Koening dan at chaosdorf.de +Daniel Leidert daniel leidert at wgdd.de +Daniel Resare daniel at resare.com +Dany Nativel dany at natzo.com +Dave Dykstra dwd at bell-labs.com +David C Niemi niemi at tuxers.net +David Champion dgc at uchicago.edu +David D. Scribner dscribner at bigfoot.com +David Ellement ellement at sdd.hp.com +David Hallinan hallinan at rtd.com +David Hollenberg dhollen at ISI.EDU +David Mathog MATHOG at seqaxp.bio.caltech.edu +David R. Bergstein dbergstein at home.com +David Shaw dshaw at jabberwocky.com +Detlef Lannert lannert at lannert.rz.uni-duesseldorf.de +Dimitri dmitri at advantrix.com +Dirk Lattermann dlatt at t-online.de +Dirk Meyer dirk.meyer at dinoex.sub.org +Disastry Disastry at saiknes.lv +Douglas Calvert dfc at anize.org +Ed Boraas ecxjo at esperanto.org +Edmund GRIMLEY EVANS edmundo at rano.org +Edwin Woudt edwin at woudt.nl +Enzo Michelangeli em at MailAndNews.com +Ernst Molitor ernst.molitor at uni-bonn.de +Evgeny Legerov +Fabian Keil fk at fabiankeil de +Fabio Coatti cova at ferrara.linux.it +Felix von Leitner leitner at amdiv.de +fish stiqz fish at analog.org +Florian Weimer Florian.Weimer at rus.uni-stuttgart.de +Francesco Potorti pot at gnu.org +Frank Donahoe fdonahoe at wilkes1.wilkes.edu +Frank Heckenbach heckenb at mi.uni-erlangen.de +Frank Stajano frank.stajano at cl.cam.ac.uk +Frank Tobin ftobin at uiuc.edu +Gabriel Rosenkoetter gr at eclipsed.net +Gaël Quéri gael at lautre.net +Gene Carter gcarter at lanier.com +Geoff Keating geoffk at ozemail.com.au +Georg Schwarz georg.schwarz at iname.com +Giampaolo Tomassoni g.tomassoni at libero.it +Gilbert Fernandes gilbert_fernandes at hotmail.com +Grant Olson kgo at grant-olson net +Greg Louis glouis at dynamicro.on.ca +Greg Troxel gdt at ir.bbn.com +Gregory Steuck steuck at iname.com +Harald Denker harry at hal.westfalen.de +Holger Baust Holger.Baust at freenet-ag.de +Henrik Nordstrom henrik at henriknordstrom.net +Hendrik Buschkamp buschkamp at rheumanet.org +Holger Schurig holger at d.om.org +Holger Smolinski smolinsk at de.ibm.com +Holger Trapp Holger.Trapp at informatik.tu-chemnitz.de +Hugh Daniel hugh at toad.com +Huy Le huyle at ugcs.caltech.edu +Ian Abbott abbotti at mev.co.uk +Ian McKellar imckellar at harvestroad.com.au +Ingo Klöcker kloecker at kde.org +Ivo Timmermans itimmermans at bigfoot.com +Jan Krueger max at physics.otago.ac.nz +Jan Niehusmann jan at gondor.com +Jan-0liver Wagner jan @ intevation.de +Janusz A. Urbanowicz alex at bofh.torun.pl +James Troup james at nocrew.org +Jean-loup Gailly gzip at prep.ai.mit.edu +Jeff Long long at kestrel.cc.ukans.edu +Jeffery Von Ronne jronne at ics.uci.edu +Jens Bachem bachem at rrz.uni-koeln.de +Jens Seidel jensseidel at users.sf.net +Jeroen C. van Gelderen jeroen at vangelderen.org +Jeroen Schot schot at a-eskwadraat nl +J Horacio MG homega at ciberia.es +J. Michael Ashley jashley at acm.org +Jim Bauer jfbauer at home.com +Jim Small cavenewt at my-deja.com +Joachim Backes backes at rhrk.uni-kl.de +Joe Rhett jrhett at isite.net +Joerg Honegger Joerg.Honegger at hp.com +John A. Martin jam at jamux.com +John Clizbe JPClizbe at comcast.net +John R. Shannon john at johnrshannon.com +Johnny Teveßen j.tevessen at gmx.de +Jörg Schilling schilling at fokus.gmd.de +Jos Backus Jos.Backus at nl.origin-it.com +Joseph Walton joe at kafsemo.org +Juan F. Codagnone juam at arnet.com.ar +Jun Kuriyama kuriyama at sky.rim.or.jp +Kahil D. Jallad kdj4 at cs.columbia.edu +Karl Fogel kfogel at guanabana.onshore.com +Karsten Thygesen karthy at kom.auc.dk +Katsuhiro Kondou kondou at nec.co.jp +Kazu Yamamoto kazu at iij.ad.jp +Kazuyoshi Kakihara +Keith Clayton keith at claytons.org +Ken Takusagawa ken.takusagawa.2 at gmail.com +Kevin Ryde user42 at zip.com.au +Kiss Gabor kissg at ssg.ki.iif.hu +Klaus Flittner klaus at flittner org +Klaus Singvogel ks at caldera.de +Kurt Garloff garloff at suse.de +Lars Kellogg-Stedman lars at bu.edu +L. Sassaman rabbi at quickie.net +M Taylor mctaylor at privacy.nb.ca +Marcel Waldvogel mwa at arl.wustl.edu +Marco d'Itri md at linux.it +Marco Parrone marc0 at autistici.org +Marcus Brinkmann Marcus.Brinkmann at ruhr-uni-bochum.de +Mark Adler madler at alumni.caltech.edu +Mark Elbrecht snowball3 at bigfoot.com +Mark Pettit pettit at yahoo-inc.com +Markus Friedl Markus.Friedl at informatik.uni-erlangen.de +Martin Kahlert martin.kahlert at provi.de +Martin Hamilton +Martin Schulte schulte at thp.uni-koeln.de +Matt Kraai kraai at alumni.carnegiemellon.edu +Matthew Skala mskala at ansuz.sooke.bc.ca +Matthew Wilcox matthew at wil.cx +Matthias-Christian Ott ott at mirix.org +Matthias Urlichs smurf at noris.de +Max Valianskiy maxcom at maxcom.ml.org +Michael Engels michael.engels at uni-duesseldorf.de +Michael Fischer v. Mollard mfvm at gmx.de +Michael Nottebrock michaelnottebrock at gmx.net +Michael Roth mroth at nessie.de +Michael Sobolev mss at despair.transas.com +Michael Tokarev mjt at tls.msk.ru +Mike Dowling ML.Dowling at tu-bs.de +Mike McEwan mike at lotusland.demon.co.uk +Moritz Schulte moritz at chaosdorf.de +Neal H Walfield neal at cs.uml.edu +Nelson H. F. Beebe beebe at math.utah.edu +Nicolas Graner Nicolas.Graner at cri.u-psud.fr +NIIBE Yutaka gniibe at chroot.org +Niklas Hernaeus +Nimrod Zimerman zimerman at forfree.at +Norihiko Murase skeleten at shillest.net +N J Doye nic at niss.ac.uk +Oliver Haakert haakert at hsp.de +Oskari Jääskeläinen f33003a at cc.hut.fi +Pascal Scheffers Pascal at scheffers.net +Paul D. Smith psmith at baynetworks.com +Per Cederqvist ceder at lysator.liu.se +Petr Cerny pcerny at suse.cz +Phil Blundell pb at debian.org +Philippe Laliberte arsphl at oeil.qc.ca +Peter Fales psfales at lucent.com +Peter Gutmann pgut001 at cs.auckland.ac.nz +Peter Marschall Peter.Marschall at gedos.de +Peter Valchev pvalchev at openbsd.org +Petr Uzel petr.uzel at suse cz +Phong Nguyen Phong.Nguyen at ens.fr +Piotr Krukowiecki piotr at pingu.ii.uj.edu.pl +QingLong qinglong at bolizm.ihep.su +Ralph Gillen gillen at theochem.uni-duesseldorf.de +Rat ratinox at peorth.gweep.net +Ray Link rlink at pitt.edu +Reinhard Wobst R.Wobst at ifw-dresden.de +Rémi Guyomarch rguyom at mail.dotcom.fr +Reuben Sumner rasumner at wisdom.weizmann.ac.il +Richard Lefebvre rick at cerca.umontreal.ca +Richard Outerbridge outer at interlog.com +Richard Patterson vectro at yahoo.com +Robert Joop rj at rainbow.in-berlin.de +Roddy Strachan roddy at satlink.com.au +Roger Sondermann r.so at bigfoot.com +Roland Rosenfeld roland at spinnaker.rhein.de +Roman Pavlik rp at tns.cz +Ross Golder rossigee at bigfoot.com +Russell Coker russell at coker.com.au +Ryan Malayter rmalayter at bai.org +Sam Roberts sam at cogent.ca +Sami Tolvanen sami at tolvanen.com +Sascha Kiefer sk at intertivity.com +Scott Worley sworley at chkno.net +Sean MacLennan seanm at netwinder.org +Sebastian Klemke packet at convergence.de +Serge Munhoven munhoven at mema.ucl.ac.be +SL Baur steve at xemacs.org +Sten Lindgren ged at solace dot miun dot se +Stefan Bellon sbellon at sbellon.de +Dr.Stefan.Dalibor Dr.Stefan.Dalibor at bfa.de +Stefan Karrmann S.Karrmann at gmx.net +Stefan Keller dres at cs.tu-berlin.de +Steffen Ullrich ccrlphr at xensei.com +Steffen Zahn zahn at berlin.snafu.de +Steven Bakker steven at icoe.att.com +Steven Murdoch sjmurdoch at bigfoot.com +Stoyan Angelov s_angelov at filibeto.org +Susanne Schultz schultz at hsp.de +Tavis Ormandy taviso at gentoo.org +Ted Cabeen secabeen at pobox.com +Thiago Jung Bauermann jungmann at cwb.matrix.com.br +Thijmen Klok thijmen at xs4all.nl +Thomas Roessler roessler at guug.de +Tim Mooney mooney at dogbert.cc.ndsu.nodak.edu +Timo Schulz twoaday at freakmail.de +Tobias Winkler tobias.winkler at s1998.tu-chemnitz.de +Todd Vierling tv at pobox.com +TOGAWA Satoshi Satoshi.Togawa at jp.yokogawa.com +Tom Duerbusch DuerbuschT at stlouiscity.com +Tom Pegios tomp at idirect.com +Tom Spindler dogcow at home.merit.edu +Tom Zerucha tzeruch at ceddec.com +Tomas Fasth tomas.fasth at twinspot.net +Tommi Komulainen Tommi.Komulainen at iki.fi +Thomas Klausner wiz at danbala.ifoer.tuwien.ac.at +Tomasz Kozlowski tomek at rentec.com +Thomas Mikkelsen tbm at image.dk +Ulf Möller 3umoelle at informatik.uni-hamburg.de +Urko Lusa ulusa at euskalnet.net +Vincent P. Broman broman at spawar.navy.mil +Volker Quetschke quetschke at scytek.de +W Lewis wiml at hhhh.org +Walter Hofmann Walter.Hofmann at physik.stud.uni-erlangen.de +Walter Koch koch at hsp.de +Wayne Chapeskie waynec at spinnaker.com +Werner Koch wk at gnupg.org +Wim Vandeputte bunbun at reptile.rug.ac.be +Winona Brown win at huh.org +Yosiaki IIDA iida at ring.gr.jp +Yoshihiro Kajiki kajiki at ylug.org + nbecker at hns.com + + +Thanks to the German Unix User Group for sponsoring this project, +Martin Hamilton for hosting the first mailing list and OpenIT for +hosting the server. + +The development of this software has partly (i.e. the Windows port) +been funded by the German Ministry for Economics and Technology under +grant VIB3-68553.168-001/1999. + +Many thanks to my wife Gerlinde for having so much patience with +me while hacking late in the evening. + + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 + 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 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/TODO b/TODO new file mode 100644 index 0000000..5182fc8 --- /dev/null +++ b/TODO @@ -0,0 +1,118 @@ + -*- outline -*- + +* src/base64 +** Make parsing more robust + Currently we don't cope with overlong lines in the best way. +** Check that we really release the ksba reader/writer objects. + +* sm/call-agent.c +** Some code should go into import.c +** When we allow concurrent service request in gpgsm, we + might want to have an agent context for each service request + (i.e. Assuan context). + +* sm/certchain.c +** Try to keep certificate references somewhere + This will help with some of our caching code. We also need to test + that caching; in particular "regtp_ca_chainlen". + +* sm/decrypt.c +** replace leading zero in integer hack by a cleaner solution + +* sm/gpgsm.c +** Implement --default-key +** support the anyPolicy semantic +** Should we prefer nonRepudiation certs over plain signing certs? + Also: Do we need a way to allow the selection of a qualSig cert + over a plain one? The background is that the Telesec cards have 3 + certs capable of signing all with the same subject name. + +* sm/keydb.c +** Check file permissions +** Check that all error code mapping is done. +** Remove the inter-module dependencies between gpgsm and keybox +** Add an source_of_key field + +* agent/ +** If we detect that a private key has been deleted + Bump the key event counter. + +* agent/command.c +** Make sure that secure memory is used where appropriate + +* agent/pkdecrypt.c, agent/pksign.c +** Support DSA + +* Move pkcs-1 encoding into libgcrypt. + +* Use a MAC to protect sensitive files. + The problem here is that we need yet another key and it is unlikely + that users are willing to remember that key too. It is possible to + do this with a smartcard, though. + +* sm/export.c +** Return an error code or a status info per user ID. + +* common/tlv.c + The parse_sexp function should not go into this file. Check whether + we can change all S-expression handling code to make use of this + function. + +* scd +** Application context vs. reader slot + We have 2 concurrent method of tracking whether a reader is in use: + Using the session_list in command.c and the lock_table in app.c. It + would be better to do this just at one place. First we need to see + how we can support cards with multiple applications. +** Resolve fixme in do_sign of app-dinsig. +** Disconnect + Card timeout is currently used as a boolean. + Add disconnect support for the ccid driver. + +* Regression tests +** Add a regression test to check the extkeyusage. + +* Windows port (W32) +** Regex support is disabled + We need to adjust the test to find the regex we have anyway in + gpg4win. Is that regex compatible to the OpenPGP requirement? + + +* sm/ +** check that we issue NO_SECKEY xxx if a -u key was not found + We don't. The messages returned are also wrong (recipient vs. signer). + +* g10/ +** issue a NO_SECKEY xxxx if a -u key was not found. + +* Extend selinux support to other modules + See also http://etbe.coker.com.au/2008/06/06/se-linux-support-gpg/ + +* UTF-8 specific TODOs + None. + +* Manual +** Document all gpgsm options. + + +* Pinpad Reader + We do not yet support P15 applications. The trivial thing using + ASCII characters will be easy to implement but the other cases need + some more work. + +* Bugs + + +* Howtos +** Migrate OpenPGP keys to another system + +* Gpg-Agent Locale + Although we pass LC_MESSAGE from gpgsm et al. to Pinentry, this has + only an effect on the stock GTK strings (e.g. "OK") and not on any + strings gpg-agent generates and passes to Pinentry. This defeats + our design goal to allow changing the locale without changing + gpg-agent's default locale (e.g. by the command updatestartuptty). + +* RFC 4387: Operational Protocols: Certificate Store Access via HTTP + Do we support this? + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..2f1a5aa --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.1.17 diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..690dc42 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,351 @@ +dnl macros to configure gnupg +dnl Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. +dnl +dnl This file is part of GnuPG. +dnl +dnl GnuPG is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3 of the License, or +dnl (at your option) any later version. +dnl +dnl GnuPG is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, see . + +dnl GNUPG_CHECK_TYPEDEF(TYPE, HAVE_NAME) +dnl Check whether a typedef exists and create a #define $2 if it exists +dnl +AC_DEFUN([GNUPG_CHECK_TYPEDEF], + [ AC_MSG_CHECKING(for $1 typedef) + AC_CACHE_VAL(gnupg_cv_typedef_$1, + [AC_TRY_COMPILE([#define _GNU_SOURCE 1 + #include + #include ], [ + #undef $1 + int a = sizeof($1); + ], gnupg_cv_typedef_$1=yes, gnupg_cv_typedef_$1=no )]) + AC_MSG_RESULT($gnupg_cv_typedef_$1) + if test "$gnupg_cv_typedef_$1" = yes; then + AC_DEFINE($2,1,[Defined if a `]$1[' is typedef'd]) + fi + ]) + + +dnl GNUPG_CHECK_GNUMAKE +dnl +AC_DEFUN([GNUPG_CHECK_GNUMAKE], + [ + if ${MAKE-make} --version 2>/dev/null | grep '^GNU ' >/dev/null 2>&1; then + : + else + AC_MSG_WARN([[ +*** +*** It seems that you are not using GNU make. Some make tools have serious +*** flaws and you may not be able to build this software at all. Before you +*** complain, please try GNU make: GNU make is easy to build and available +*** at all GNU archives. It is always available from ftp.gnu.org:/gnu/make. +***]]) + fi + ]) + +dnl GNUPG_CHECK_FAQPROG +dnl +AC_DEFUN([GNUPG_CHECK_FAQPROG], + [ AC_MSG_CHECKING(for faqprog.pl) + if faqprog.pl -V 2>/dev/null | grep '^faqprog.pl ' >/dev/null 2>&1; then + working_faqprog=yes + FAQPROG="faqprog.pl" + else + working_faqprog=no + FAQPROG=": " + fi + AC_MSG_RESULT($working_faqprog) + AC_SUBST(FAQPROG) + AM_CONDITIONAL(WORKING_FAQPROG, test "$working_faqprog" = "yes" ) + +dnl if test $working_faqprog = no; then +dnl AC_MSG_WARN([[ +dnl *** +dnl *** It seems that the faqprog.pl program is not installed; +dnl *** however it is only needed if you want to change the FAQ. +dnl *** (faqprog.pl should be available at: +dnl *** ftp://ftp.gnupg.org/gcrypt/contrib/faqprog.pl ) +dnl *** No need to worry about this warning. +dnl ***]]) +dnl fi + ]) + +dnl GNUPG_CHECK_DOCBOOK_TO_TEXI +dnl +AC_DEFUN([GNUPG_CHECK_DOCBOOK_TO_TEXI], + [ + AC_CHECK_PROG(DOCBOOK_TO_TEXI, docbook2texi, yes, no) + AC_MSG_CHECKING(for sgml to texi tools) + working_sgmltotexi=no + if test "$ac_cv_prog_DOCBOOK_TO_TEXI" = yes; then + if sgml2xml -v /dev/null 2>&1 | grep 'SP version' >/dev/null 2>&1 ; then + working_sgmltotexi=yes + fi + fi + AC_MSG_RESULT($working_sgmltotexi) + AM_CONDITIONAL(HAVE_DOCBOOK_TO_TEXI, test "$working_sgmltotexi" = "yes" ) + ]) + + + +dnl GNUPG_CHECK_ENDIAN +dnl define either LITTLE_ENDIAN_HOST or BIG_ENDIAN_HOST +dnl +AC_DEFUN([GNUPG_CHECK_ENDIAN], + [ + tmp_assumed_endian=big + tmp_assume_warn="" + if test "$cross_compiling" = yes; then + case "$host_cpu" in + i@<:@345678@:>@* ) + tmp_assumed_endian=little + ;; + *) + ;; + esac + fi + AC_MSG_CHECKING(endianness) + AC_CACHE_VAL(gnupg_cv_c_endian, + [ gnupg_cv_c_endian=unknown + # See if sys/param.h defines the BYTE_ORDER macro. + AC_TRY_COMPILE([#include + #include ], [ + #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros + #endif], [# It does; now see whether it defined to BIG_ENDIAN or not. + AC_TRY_COMPILE([#include + #include ], [ + #if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif], gnupg_cv_c_endian=big, gnupg_cv_c_endian=little)]) + if test "$gnupg_cv_c_endian" = unknown; then + AC_TRY_RUN([main () { + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); + }], + gnupg_cv_c_endian=little, + gnupg_cv_c_endian=big, + gnupg_cv_c_endian=$tmp_assumed_endian + tmp_assumed_warn=" (assumed)" + ) + fi + ]) + AC_MSG_RESULT([${gnupg_cv_c_endian}${tmp_assumed_warn}]) + if test "$gnupg_cv_c_endian" = little; then + AC_DEFINE(LITTLE_ENDIAN_HOST,1, + [Defined if the host has little endian byte ordering]) + else + AC_DEFINE(BIG_ENDIAN_HOST,1, + [Defined if the host has big endian byte ordering]) + fi + ]) + + + + +# GNUPG_BUILD_PROGRAM(NAME,DEFAULT) +# Add a --enable-NAME option to configure an set the +# shell variable build_NAME either to "yes" or "no". DEFAULT must +# either be "yes" or "no" and decided on the default value for +# build_NAME and whether --enable-NAME or --disable-NAME is shown with +# ./configure --help +AC_DEFUN([GNUPG_BUILD_PROGRAM], + [m4_define([my_build], [m4_bpatsubst(build_$1, [[^a-zA-Z0-9_]], [_])]) + my_build=$2 + m4_if([$2],[yes],[ + AC_ARG_ENABLE([$1], AC_HELP_STRING([--disable-$1], + [do not build the $1 program]), + my_build=$enableval, my_build=$2) + ],[ + AC_ARG_ENABLE([$1], AC_HELP_STRING([--enable-$1], + [build the $1 program]), + my_build=$enableval, my_build=$2) + ]) + case "$my_build" in + no|yes) + ;; + *) + AC_MSG_ERROR([only yes or no allowed for feature --enable-$1]) + ;; + esac + m4_undefine([my_build]) + ]) + + + +# GNUPG_DISABLE_GPG_ALGO(NAME,DESCRIPTION) +# +# Add a --disable-gpg-NAME option and the corresponding ac_define +# GPG_USE_. +AC_DEFUN([GNUPG_GPG_DISABLE_ALGO], + [AC_MSG_CHECKING([whether to enable the $2 for gpg]) + AC_ARG_ENABLE([gpg-$1], AC_HELP_STRING([--disable-gpg-$1], + [disable the $2 algorithm in gpg]), + , enableval=yes) + AC_MSG_RESULT($enableval) + if test x"$enableval" = xyes ; then + AC_DEFINE(GPG_USE_[]m4_toupper($1), 1, [Define to support the $2]) + fi + ]) + + + + +# Check whether mlock is broken (hpux 10.20 raises a SIGBUS if mlock +# is not called from uid 0 (not tested whether uid 0 works) +# For DECs Tru64 we have also to check whether mlock is in librt +# mlock is there a macro using memlk() +dnl GNUPG_CHECK_MLOCK +dnl +AC_DEFUN([GNUPG_CHECK_MLOCK], + [ AC_CHECK_FUNCS(mlock) + if test "$ac_cv_func_mlock" = "no"; then + AC_CHECK_HEADERS(sys/mman.h) + if test "$ac_cv_header_sys_mman_h" = "yes"; then + # Add librt to LIBS: + AC_CHECK_LIB(rt, memlk) + AC_CACHE_CHECK([whether mlock is in sys/mman.h], + gnupg_cv_mlock_is_in_sys_mman, + [AC_TRY_LINK([ + #include + #ifdef HAVE_SYS_MMAN_H + #include + #endif + ], [ + int i; + + /* glibc defines this for functions which it implements + * to always fail with ENOSYS. Some functions are actually + * named something starting with __ and the normal name + * is an alias. */ + #if defined (__stub_mlock) || defined (__stub___mlock) + choke me + #else + mlock(&i, 4); + #endif + ; return 0; + ], + gnupg_cv_mlock_is_in_sys_mman=yes, + gnupg_cv_mlock_is_in_sys_mman=no)]) + if test "$gnupg_cv_mlock_is_in_sys_mman" = "yes"; then + AC_DEFINE(HAVE_MLOCK,1, + [Defined if the system supports an mlock() call]) + fi + fi + fi + if test "$ac_cv_func_mlock" = "yes"; then + AC_MSG_CHECKING(whether mlock is broken) + AC_CACHE_VAL(gnupg_cv_have_broken_mlock, + AC_TRY_RUN([ + #include + #include + #include + #include + #include + #include + + int main() + { + char *pool; + int err; + long int pgsize = getpagesize(); + + pool = malloc( 4096 + pgsize ); + if( !pool ) + return 2; + pool += (pgsize - ((long int)pool % pgsize)); + + err = mlock( pool, 4096 ); + if( !err || errno == EPERM ) + return 0; /* okay */ + + return 1; /* hmmm */ + } + + ], + gnupg_cv_have_broken_mlock="no", + gnupg_cv_have_broken_mlock="yes", + gnupg_cv_have_broken_mlock="assume-no" + ) + ) + if test "$gnupg_cv_have_broken_mlock" = "yes"; then + AC_DEFINE(HAVE_BROKEN_MLOCK,1, + [Defined if the mlock() call does not work]) + AC_MSG_RESULT(yes) + AC_CHECK_FUNCS(plock) + else + if test "$gnupg_cv_have_broken_mlock" = "no"; then + AC_MSG_RESULT(no) + else + AC_MSG_RESULT(assuming no) + fi + fi + fi + ]) + + +dnl Stolen from gcc +dnl Define MKDIR_TAKES_ONE_ARG if mkdir accepts only one argument instead +dnl of the usual 2. +AC_DEFUN([GNUPG_FUNC_MKDIR_TAKES_ONE_ARG], +[AC_CHECK_HEADERS(sys/stat.h unistd.h direct.h) +AC_CACHE_CHECK([if mkdir takes one argument], gnupg_cv_mkdir_takes_one_arg, +[AC_TRY_COMPILE([ +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_DIRECT_H +# include +#endif], [mkdir ("foo", 0);], + gnupg_cv_mkdir_takes_one_arg=no, gnupg_cv_mkdir_takes_one_arg=yes)]) +if test $gnupg_cv_mkdir_takes_one_arg = yes ; then + AC_DEFINE(MKDIR_TAKES_ONE_ARG,1, + [Defined if mkdir() does not take permission flags]) +fi +]) + + +# GNUPG_TIME_T_UNSIGNED +# Check whether time_t is unsigned +# +AC_DEFUN([GNUPG_TIME_T_UNSIGNED], + [ AC_CACHE_CHECK(whether time_t is unsigned, gnupg_cv_time_t_unsigned, + [AC_REQUIRE([AC_HEADER_TIME])dnl + AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY( + [AC_INCLUDES_DEFAULT([]) +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +], + [((time_t)-1) < 0])], + gnupg_cv_time_t_unsigned=no, gnupg_cv_time_t_unsigned=yes)]) + if test $gnupg_cv_time_t_unsigned = yes; then + AC_DEFINE(HAVE_UNSIGNED_TIME_T,1,[Defined if time_t is an unsigned type]) + fi +])# GNUPG_TIME_T_UNSIGNED diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..45092af --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1267 @@ +# generated automatically by aclocal 1.14.1 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# 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. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# intlmacosx.m4 serial 5 (gettext-0.18.2) +dnl Copyright (C) 2004-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Checks for special options needed on Mac OS X. +dnl Defines INTL_MACOSX_LIBS. +AC_DEFUN([gt_INTL_MACOSX], +[ + dnl Check for API introduced in Mac OS X 10.2. + AC_CACHE_CHECK([for CFPreferencesCopyAppValue], + [gt_cv_func_CFPreferencesCopyAppValue], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[CFPreferencesCopyAppValue(NULL, NULL)]])], + [gt_cv_func_CFPreferencesCopyAppValue=yes], + [gt_cv_func_CFPreferencesCopyAppValue=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then + AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1], + [Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework.]) + fi + dnl Check for API introduced in Mac OS X 10.3. + AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[CFLocaleCopyCurrent();]])], + [gt_cv_func_CFLocaleCopyCurrent=yes], + [gt_cv_func_CFLocaleCopyCurrent=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFLocaleCopyCurrent = yes; then + AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1], + [Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework.]) + fi + INTL_MACOSX_LIBS= + if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then + INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" + fi + AC_SUBST([INTL_MACOSX_LIBS]) +]) + +# Copyright (C) 2002-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.14' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.14.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.14.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/autobuild.m4]) +m4_include([m4/codeset.m4]) +m4_include([m4/gettext.m4]) +m4_include([m4/gpg-error.m4]) +m4_include([m4/iconv.m4]) +m4_include([m4/isc-posix.m4]) +m4_include([m4/ksba.m4]) +m4_include([m4/lcmessage.m4]) +m4_include([m4/ldap.m4]) +m4_include([m4/lib-ld.m4]) +m4_include([m4/lib-link.m4]) +m4_include([m4/lib-prefix.m4]) +m4_include([m4/libassuan.m4]) +m4_include([m4/libgcrypt.m4]) +m4_include([m4/nls.m4]) +m4_include([m4/npth.m4]) +m4_include([m4/ntbtls.m4]) +m4_include([m4/pkg.m4]) +m4_include([m4/po.m4]) +m4_include([m4/progtest.m4]) +m4_include([m4/readline.m4]) +m4_include([m4/socklen.m4]) +m4_include([m4/sys_socket_h.m4]) +m4_include([m4/tar-ustar.m4]) +m4_include([acinclude.m4]) diff --git a/agent/ChangeLog-2011 b/agent/ChangeLog-2011 new file mode 100644 index 0000000..d32d69c --- /dev/null +++ b/agent/ChangeLog-2011 @@ -0,0 +1,3107 @@ +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-11-28 Werner Koch + + * command-ssh.c (card_key_available): Change wording of no key + diagnostic. + (ssh_handler_request_identities): Do not call card_key_available + if the scdaemon is disabled. + +2011-09-12 Ben Kibbey + + * genkey.c (agent_ask_new_passphrase): Allow for an empty passphrase + (no protection) in PINENTRY_MODE_LOOPBACK. + +2011-09-10 Ben Kibbey + + * agent.h (pinentry_loopback): New prototype. + * command.c (pinentry_loopback): New function to inquire a passphrase + from the client. For use with pinentry-mode=loopback. + * call-pinentry.c (agent_askpin): Handle PINENTRY_MODE_LOOPBACK. + * call-pinentry.c (agent_get_passphrase): Ditto. + * genkey.c (agent_ask_new_passphrase): Ditto. + +2011-08-10 Werner Koch + + * genkey.c (check_passphrase_pattern): Use gpg_strerror instead of + strerror. + * command-ssh.c (ssh_receive_mpint_list): Remove unused var + ELEMS_PUBLIC_N. + * gpg-agent.c (main): Remove unused var MAY_COREDUMP. + +2011-08-09 Ben Kibbey + + * command.c (option_handler): Have option s2k-count match the + documentation. + +2011-07-27 Werner Koch + + * call-scd.c (struct inq_needpin_s): Add field ANY_INQ_SEEN. + (inq_needpin): Set it. + (agent_card_scd): Send the cancel only if an inquire was actually + used. + +2011-07-09 Ben Kibbey + + * call-scd.c (agent_card_scd): Send the CANCEL command back to SCD + when the SCD command is cancelled from the client. + +2011-07-22 Werner Koch + + * command-ssh.c (ssh_receive_key): Do not init comment to an empty + static string; in the error case it would be freed. + +2011-07-20 Werner Koch + + * command.c (do_one_keyinfo, cmd_keyinfo): Support option --ssh-fpr. + + * command-ssh.c (ssh_identity_register): Display the ssh + fingerprint in the prompt. + (add_control_entry): Add arg FMTFPR and use it as comment in + sshcontrol. + (confirm_flag_from_sshcontrol): New. + (data_sign): Ask for confirmaton if requested. + (search_control_file): Add new arg R_CONFIRM and enhance parser. + * findkey.c (agent_raw_key_from_file): New. + (modify_description): Add format letter %F. + * findkey.c (agent_key_from_file): Simplify comment extraction by + using gcry_sexp_nth_string. + +2011-06-28 Ben Kibbey + + * command.c (option_handler): Add option s2k-count. + * agent.h (server_control_s): Add member s2k_count. + * genkey.c (store_key): Add parameter s2k_count. + * protect.c (agent_protect): Add parameter s2k_count. + * protect.c (do_encryption): Add parameter s2k_count. + +2011-06-01 Marcus Brinkmann + + * cvt-openpgp.c (convert_to_openpgp): Change type of N to unsigned + int. + +2011-04-26 Werner Koch + + * cvt-openpgp.c (convert_to_openpgp): Use rfc4880 encoded S2K count. + * protect.c (get_standard_s2k_count_rfc4880): New. + (S2K_DECODE_COUNT): New. + (s2k_hash_passphrase): Use the new macro. + +2011-04-21 Werner Koch + + * agent.h (server_control_s): Add field cache_ttl_opt_preset. + * gpg-agent.c (agent_init_default_ctrl): Init this field. + * genkey.c (agent_genkey): Use this new variable. + * command.c (cmd_passwd): Ditto. + (option_handler): Add new option cache-ttl-opt-preset. + +2011-04-20 Marcus Brinkmann + + * command.c (cmd_import_key): Release key from failed import + before converting openpgp private key in the openpgp-private-key + case. + +2011-04-17 Ben Kibbey + + * command.c (cmd_passwd): Check for an error before presetting. + +2011-04-12 Ben Kibbey + + * command.c (cmd_passwd): Fixed --preset when not previously cached. + +2011-04-12 Werner Koch + + * agent.h (CACHE_TTL_NONCE, CACHE_TTL_OPT_PRESET): New. + * command.c (cmd_passwd, cmd_import_key): Use new macros. + * genkey.c (agent_genkey): Ditto. + +2011-04-10 Ben Kibbey + + * command.c (cmd_passwd): Add option --preset. + * command.c (cmd_genkey): Add option --preset. + * genkey.c (agent_genkey): Add parameter preset. + +2011-04-06 Ben Kibbey + + * command.c (do_one_keyinfo): Add protection type field. + +2011-03-10 Werner Koch + + * protect.c (hash_passphrase): Use the new gcry_kdf_derive. + +2011-03-08 Werner Koch + + * cvt-openpgp.c (GCRY_PK_ECDH) [!HAVE_GCRY_PK_ECDH]: Remove. + +2011-03-03 Ben Kibbey + + * command.c (cmd_preset_passphrase): Add option --inquire. + +2011-03-03 Werner Koch + + * gpg-agent.c: Add option --allow-loopback-pinentry. + * command.c (option_handler): Add option pinentry-mode. + * agent.h (pinentry_mode_t): New enum. + (struct server_local_s): Add PINENTRY_MODE. + (struct opt): Add ALLOW_LOOPBACK_PINENTRY. + * call-pinentry.c (agent_askpin): Implement ask, cancel and error + pinentry modes. + (agent_get_passphrase, agent_get_confirmation): Ditto. + (agent_show_message): Return cancel if pinentry mode is not "ask". + (agent_popup_message_start): Ditto. + +2011-03-02 Werner Koch + + * call-scd.c (hash_algo_option): New. + (agent_card_pksign): Use it with PKSIGN. + +2011-03-02 Ben Kibbey (wk) + + * command.c (cmd_clear_passphrase): Add option --mode=normal. + (cmd_keyinfo): Add option --data. + (do_one_keyinfo): Return CACHED status. Add arg DATA. + +2011-02-07 Werner Koch + + * pksign.c (do_encode_dsa): Enforce multipe of 8 bits only for DSA. + +2011-02-03 Werner Koch + + * protect.c (protect_info): Support ECC algos. + + * pksign.c (do_encode_dsa): Map public key algo number. Extend + DSA size check for ECDSA. + + * gpg-agent.c: Include cipher.h. + (map_pk_openpgp_to_gcry): New. + + * findkey.c (key_parms_from_sexp): Support ECDH. + + * cvt-openpgp.c (get_keygrip): Support ECC algorithms. + (convert_secret_key): Ditto. + (do_unprotect): Ditto. + +2011-02-02 Werner Koch + + * cvt-openpgp.c (convert_secret_key): Remove algo mapping. + +2011-01-31 Werner Koch + + * cvt-openpgp.c (convert_to_openpgp): Adjust to reverted Libgcrypt + ABI. + + * protect.c (protect_info): Adjust ECDSA and ECDH parameter names. + Add "ecc". + * findkey.c (key_parms_from_sexp): Ditto. + +2011-01-19 Werner Koch + + * trustlist.c (read_one_trustfile): Also chop an CR. + +2011-01-21 Werner Koch + + * pksign.c (do_encode_dsa): Compare MDLEN to bytes. + + * cvt-openpgp.c (GCRY_PK_ECDH) [!HAVE_GCRY_PK_ECDH]: New. + +2010-12-02 Werner Koch + + * gpg-agent.c (CHECK_OWN_SOCKET_INTERVAL) [W32CE]: Set to 60 + seconds. + +2010-11-29 Werner Koch + + * cache.c (initialize_module_cache): Factor code out to ... + (init_encryption): new. + (new_data, agent_get_cache): Init encryption on on the fly. + +2010-11-26 Werner Koch + + * gpg-agent.c (CHECK_OWN_SOCKET_INTERVAL): New. + (handle_tick) [W32CE]: Don't check own socket. + +2010-11-23 Werner Koch + + * Makefile.am (gpg_agent_LDFLAGS): Add extra_bin_ldflags. + +2010-11-11 Werner Koch + + * agent.h (opt): Add field SIGUSR2_ENABLED. + * gpg-agent.c (handle_connections): Set that flag. + * call-scd.c (start_scd): Enable events depending on this flag. + +2010-10-27 Werner Koch + + * gpg-agent.c (create_socket_name): Use TMPDIR. Change callers. + +2010-10-26 Werner Koch + + * cache.c (agent_put_cache): Allow deletion even if TTL is passwd + as 0. + + * genkey.c (agent_protect_and_store): Add arg PASSPHRASE_ADDR. + * command.c (cmd_passwd): Add option --passwd-nonce. + (struct server_local_s): Add LAST_CACHE_NONCE and LAST_PASSWD_NONCE. + (clear_nonce_cache): New. + (reset_notify): Clear the nonce cache. + (start_command_handler): Ditto. + +2010-10-25 Werner Koch + + * command.c (cmd_export_key): Free CACHE_NONCE. + (cmd_passwd): Add option --cache-nonce. + +2010-10-18 Werner Koch + + * call-pinentry.c (start_pinentry): Print name of pinentry on + connect error. + + * call-scd.c (agent_card_pksign): Make sure to return an unsigned + number. + +2010-10-14 Werner Koch + + * command.c (cmd_genkey): Add option --no-protection. + * genkey.c (agent_genkey): Add arg NO_PROTECTION. + +2010-10-13 Werner Koch + + * call-pinentry.c (agent_get_passphrase): Support the close_button. + + * gpg-agent.c (create_server_socket): Switch back to stderr + logging if we are not starting a agent. + + * command.c (cmd_passwd, cmd_export_key): Move mapping of + GPG_ERR_FULLY_CANCELED to .. + (leave_cmd): .. here. + (option_handler): Add option agent-awareness. + * protect-tool.c (get_passphrase): Take care of + GPG_ERR_FULLY_CANCELED. + * findkey.c (try_unprotect_cb): Ditto. + (unprotect): Remove the fully_canceled hack. + * call-pinentry.c (start_pinentry): Ditto. + (agent_askpin): Ditto. + * pkdecrypt.c (agent_pkdecrypt): Ditto + * pksign.c (agent_pksign_do): Ditto. + * genkey.c (agent_ask_new_passphrase): Remove arg CANCEL_ALL. + +2010-10-06 Werner Koch + + * cvt-openpgp.c (convert_secret_key): Add missing break. + +2010-10-05 Werner Koch + + * gpg-agent.c (main): Don't set SSH_AGENT_PID so that ssh-agent -k + won't kill out gpg-agent. + +2010-09-30 Werner Koch + + * gpg-agent.c (agent_exit): Run cleanup. + (cleanup): Run only once. + + * call-pinentry.c (close_button_status_cb): New. + (agent_askpin): Add arg R_CANCEL_ALL. Change all callers. + * genkey.c (agent_ask_new_passphrase): Ditto. + * findkey.c (unprotect): Return GPG_ERR_FULLY_CANCELED if needed. + + * command.c (cmd_export_key): Add support for OpenPGP keys. + * findkey.c (unprotect): Add optional arg R_PASSPHRASE. + (agent_key_from_file): Ditto. Change all callers. + + * findkey.c (unprotect): Do not put the passphrase into the cache + if it has been changed. + + * cvt-openpgp.c (convert_to_openpgp, apply_protection) + (key_from_sexp): New. + +2010-09-29 Werner Koch + + * cvt-openpgp.c (convert_openpgp): Rename to convert_from_openpgp. + + * command.c (has_option): Stop at "--". + (has_option_name, option_value): Ditto. + (skip_options): Skip initial spaces. + +2010-09-24 Werner Koch + + * gpg-agent.c (main, reread_configuration): Always test whether + the default configuration file has been created in the meantime. + Fixes bug#1285. + +2010-09-17 Werner Koch + + * command.c (cmd_havekey): Allow testing of several keygrips. + +2010-09-15 Werner Koch + + * protect.c (calculate_mic): Take care of shared secret format. + + * agent.h (PROTECTED_SHARED_SECRET): New. + +2010-09-02 Werner Koch + + * cache.c (new_data): Change arg and callers to use a string and + explicity return an error code. We never used raw binary data and + thus it is easier to use a string. Adjust callers. + (initialize_module_cache, deinitialize_module_cache): New. + (new_data): Encrypt the cached data. + (struct cache_item_s): Remove field LOCKCOUNT. Change all users + accordingly. + (agent_unlock_cache_entry): Remove. + (agent_get_cache): Return an allocated string and remove CACHE_ID. + * genkey.c (agent_genkey): Remove cache marker stuff. + * findkey.c (unprotect): Ditto. + * cvt-openpgp.c (convert_openpgp): Ditto. + * command.c (cmd_get_passphrase): Ditto. + * gpg-agent.c (main, cleanup): Initialize and deinitialize the + cache module. + +2010-09-01 Werner Koch + + * call-pinentry.c (start_pinentry): Disable pinentry logging. + + * command.c (cmd_import_key, cmd_genkey, cmd_pksign): Add CACHE + handling. + * cvt-openpgp.c (convert_openpgp): Add arg CACHE_NONCE and try the + cached nonce first. + * genkey.c (agent_genkey): Add arg CACHE_NONCE. + * cache.c (agent_get_cache): Require user and nonce cache modes + to match the requested mode. + (agent_put_cache): Ditto. + * agent.h (CACHE_MODE_NONCE): New. + * pksign.c (agent_pksign_do, agent_pksign): Add arg CACHE_NONCE. + * findkey.c (agent_key_from_file): Ditto. + (unprotect): Implement it. + +2010-08-31 Werner Koch + + * pksign.c (do_encode_dsa): Fix sign problem. + * findkey.c (agent_is_dsa_key): Adjust to actual usage. + +2010-08-30 Werner Koch + + * protect.c (s2k_hash_passphrase): New public function. + +2010-08-27 Werner Koch + + * command.c (cmd_import_key): Support OpenPGP keys. + * cvt-openpgp.h, cvt-openpgp.c: New. Some of the code is based on + code taken from g10/seckey-cert.c. + +2010-08-26 Werner Koch + + * command-ssh.c (open_control_file): Use estream to create the file. + + * findkey.c (agent_write_private_key): Explicitly create file with + mode 600. + * gpg-agent.c (main): Ditto. + * trustlist.c (agent_marktrusted): Explicitly create file with + mode 640. + +2010-08-16 Werner Koch + + * gpg-agent.c: Replace remaining printf by es_printf. + +2010-08-11 Werner Koch + + * call-pinentry.c (agent_get_passphrase, agent_askpin): Fix + setting of confidential flag. + + * call-scd.c (agent_card_scd): Pass assuan comment lines to the + caller. + (ASSUAN_CONVEY_COMMENTS): Provide replacement if needed. + +2010-08-09 Werner Koch + + * Makefile.am (t_common_ldadd): Add NETLIBS for sake of the TCP + logging. + +2010-06-24 Werner Koch + + * genkey.c (check_passphrase_pattern): Use HANG option for + gnupg_wait_progress. Fixes regression from 2010-06-09. + +2010-06-21 Werner Koch + + * protect-tool.c (export_p12_file, import_p12_cert_cb) + (import_p12_file, sexp_to_kparms, store_private_key): Remove + unused code. + +2010-06-18 Werner Koch + + * protect-tool.c (store_private_key, rsa_key_check): Remove. + + * command.c (cmd_export_key): New. + +2010-06-15 Werner Koch + + * command.c (cmd_keywrap_key, cmd_import_key): New. + + * genkey.c (agent_genkey, agent_protect_and_store): Factor common + code out to... + (agent_ask_new_passphrase): .. new. + + * findkey.c (agent_write_private_key): Return GPG_ERR_EEXIST + instead of GPG_ERR_GENERAL. + +2010-06-14 Werner Koch + + * protect-tool.c: Remove commands --p12-import and --p12-export. + * minip12.c, minip12.h: Move to ../sm. + * Makefile.am (gpg_protect_tool_SOURCES): Remove them. + * preset-passphrase.c: Remove unneeded minip12.h. + + * command.c (cmd_keywrap_key): New. + + * command.c (leave_cmd): New. + (cmd_istrusted, cmd_listtrusted, cmd_marktrusted, cmd_pksign) + (cmd_pkdecrypt, cmd_genkey, cmd_readkey, cmd_keyinfo) + (cmd_get_passphrase, cmd_get_confirmation, cmd_learn) + (cmd_passwd, cmd_preset_passphrase, cmd_getval, cmd_putval): Use it. + +2010-05-12 Werner Koch + + * preset-passphrase.c (forget_passphrase): Actually implement + this. Fixes bug#1198. + +2010-05-11 Werner Koch + + * agent.h (opt): Add field USE_STANDARD_SOCKET. + * gpg-agent.c (use_standard_socket): Remove. Use new option instead. + + * command.c (cmd_killagent, cmd_reloadagent): Provide command also + for non-W32 platforms. + (cmd_getinfo): New subcommands std_session_env and std_startup_env. + +2010-05-03 Werner Koch + + * gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME + too early. + +2010-04-30 Werner Koch + + * gpg-agent.c (main): Add command --use-standard-socket-p. + +2010-04-26 Werner Koch + + * gpg-agent.c (create_server_socket) [W32]: Also check for EEXIST. + +2010-04-19 Werner Koch + + * pksign.c (get_dsa_qbits, do_encode_dsa): New. + (agent_pksign_do): Detect DSA keys and use do_encode_dsa. + * findkey.c (agent_public_key_from_file): Factor some code out to .. + (key_parms_from_sexp): New. + (agent_is_dsa_key): New. + + * command.c (cmd_sethash): Clear digeest.RAW_VALUE. + +2010-04-14 Werner Koch + + * Makefile.am (libexec_PROGRAMS) [W32CE]: Do not build + gpg-preset-passphrase for now. + (pwquery_libs) [W32CE]: Set to empty. + + * trustlist.c (read_one_trustfile): Use estream. + +2010-04-13 Werner Koch + + * findkey.c (read_key_file): Use estream. + (agent_write_private_key): Ditto. + +2010-04-07 Werner Koch + + * gpg-agent.c (handle_connections) [W32]: Assume that PTh support + the handle event. Use a dummy event for W32CE. + (get_agent_scd_notify_event) [W32CE]: Do not build. + + * call-pinentry.c: Remove setenv.h. Include sysutils.h. + (atfork_cb): s/setenv/gnupg_setenv/. + + * gpg-agent.c: Do not include setenv.h. + (main): s/unsetenv/gnupg_unsetenv/. + + * protect.c (calibrate_get_time) [W32CE]: Use GetThreadTimes. + +2010-04-06 Werner Koch + + * call-scd.c [!HAVE_SIGNAL_H]: Do not include signal.h. + + * findkey.c (agent_write_private_key): s/remove/gnupg_remove/. + + * command-ssh.c (search_control_file): Replace rewind by fseek and + clearerr. + * genkey.c (check_passphrase_pattern): Ditto. + + * gpg-agent.c [!HAVE_SIGNAL_H]: Do not include signal.h. + (remove_socket): s/remove/gnupg_remove/. + (create_private_keys_directory): Use gnupg_mkdir. + +2010-03-11 Werner Koch + + * gpg-agent.c: Include "asshelp.h". + (main): Remove assuan_set_assuan_log_prefix. Add + assuan_set_log_cb. + (handle_signal): Disable pth ctrl dumping. + (parse_rereadable_options, main): Remove assuan_set_assuan_log_stream. + * call-scd.c (start_scd): Remove assuan_set_log_stream. + +2010-03-10 Werner Koch + + * Makefile.am (common_libs): Remove libjnlib.a. + + * trustlist.c, protect-tool.c, command-ssh.c: Remove estream.h. + +2010-02-17 Werner Koch + + * call-pinentry.c (start_pinentry): Always free OPTSTR. Send + default-xxx strings. + +2010-01-26 Werner Koch + + * protect.c (do_encryption): Encode the s2kcount and no not use a + static value of 96. + +2009-12-21 Werner Koch + + * command.c (cmd_getinfo): Add sub-command s2k_count. + +2009-12-14 Werner Koch + + * protect.c (agent_unprotect): Decode the S2K count here and take + care of the new unencoded values. Add a lower limit sanity check. + (hash_passphrase): Do not decode here. + (get_standard_s2k_count, calibrate_s2k_count): New. + (calibrate_get_time, calibrate_elapsed_time): New. + (do_encryption): Use get_standard_s2k_count. + +2009-12-08 Werner Koch + + * protect.c (agent_unprotect): Avoid compiler warning. + +2009-12-08 Marcus Brinkmann + + * call-pinentry.c (start_pinentry): Convert posix fd to assuan fd. + * call-scd.c (start_scd): Likewise. + +2009-12-03 Werner Koch + + * gpg-agent.c (set_debug): Allow for numerical debug leveles. Print + active debug flags. + +2009-12-02 Werner Koch + + * trustlist.c (read_trustfiles): Store the pointer returned from + shrinking the memory and not the orginal one. Fixes bug#1163. + Reported by TAKAHASHI Tamotsu. Also return correct error after + memory failure. + +2009-11-27 Marcus Brinkmann + + * command.c (start_command_handler): Do not call + assuan_set_log_stream anymore. + * gpg-agent.c (main): But call assuan_set_assuan_log_stream here. + +2009-11-25 Marcus Brinkmann + + * command.c (start_command_handler): Use assuan_fd_t and + assuan_fdopen on fds. + +2009-11-05 Marcus Brinkmann + + * call-pinentry.c (start_pinentry): Call assuan_pipe_connect, not + assuan_pipe_connect_ext. + * command.c (start_command_handler): Change + assuan_init_socket_server_ext into assuan_init_socket_server. + * call-scd.c (start_scd): Update use of assuan_socket_connect and + assuan_pipe_connect. + * gpg-agent.c (check_own_socket_thread, check_for_running_agent): + Update use of assuan_socket_connect. + +2009-11-04 Werner Koch + + * command.c (register_commands): Add help arg to + assuan_register_command. Convert all command comments to help + strings. + +2009-11-02 Marcus Brinkmann + + * command.c (reset_notify): Take LINE arg and return error. + (register_commands): Use assuan_handler_t type. + +2009-10-16 Marcus Brinkmann + + * gpg_agent_CFLAGS, gpg_agent_LDADD: Use libassuan instead of + libassuan-pth. + * gpg-agent.c: Invoke ASSUAN_SYSTEM_PTH_IMPL. + (main): Call assuan_set_system_hooks and assuan_sock_init. + Fix invocation of assuan_socket_connect. + +2009-09-23 Werner Koch + + * command.c (register_commands) [HAVE_ASSUAN_SET_IO_MONITOR]: + Remove cpp condition. + (start_command_handler) [HAVE_ASSUAN_SET_IO_MONITOR]: Ditto. + +2009-09-23 Marcus Brinkmann + + * gpg-agent.c (parse_rereadable_options): Don't set global assuan + log file (there ain't one anymore). + (main): Update to new API. + (check_own_socket_pid_cb): Return gpg_error_t instead of int. + (check_own_socket_thread, check_for_running_agent): Create assuan + context before connecting to server. + * command.c: Include "scdaemon.h" before because of + GPG_ERR_SOURCE_DEFAULT check. + (write_and_clear_outbuf): Use gpg_error_t instead of + assuan_error_t. + (cmd_geteventcounter, cmd_istrusted, cmd_listtrusted) + (cmd_marktrusted, cmd_havekey, cmd_sigkey, cmd_setkeydesc) + (cmd_sethash, cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_readkey) + (cmd_keyinfo, cmd_get_passphrase, cmd_clear_passphrase) + (cmd_get_confirmation, cmd_learn, cmd_passwd) + (cmd_preset_passphrase, cmd_scd, cmd_getval, cmd_putval) + (cmd_updatestartuptty, cmd_killagent, cmd_reloadagent) + (cmd_getinfo, option_handler): Return gpg_error_t instead of int. + (post_cmd_notify): Change type of ERR to gpg_error_t from int. + (io_monitor): Add hook argument. Use symbols for constants. + (register_commands): Change return type of HANDLER to gpg_error_t. + (start_command_handler): Allocate assuan context before starting + server. + * call-pinentry.c: Include "scdaemon.h" before because + of GPG_ERR_SOURCE_DEFAULT check. + (unlock_pinentry): Call assuan_release instead of + assuan_disconnect. + (getinfo_pid_cb, getpin_cb): Return gpg_error_t instead of int. + (start_pinentry): Allocate assuan context before connecting to + server. + * call-scd.c (membuf_data_cb, learn_status_cb, get_serialno_cb) + (membuf_data_cb, inq_needpin, card_getattr_cb, pass_status_thru) + (pass_data_thru): Change return type to gpg_error_t. + (start_scd): Allocate assuan context before connecting to server. + +2009-09-04 Marcus Brinkmann + + * command.c (start_command_handler): Add comment about gap in + implementation (in dead code), for future reference. + +2009-08-11 Werner Koch + + * divert-scd.c (ask_for_card): I18n a prompt string. + +2009-07-06 Werner Koch + + * agent.h: Include session-env.h. + (opt): Replace most of the startup_xxx fields by a session_env_t. + (struct server_control_s): Likewise. + * gpg-agent.c (main): Rewrite setting of the startup fields. + (handle_connections, main): Allocate SESSION_ENV. + (agent_init_default_ctrl, agent_deinit_default_ctrl): Change + accordingly. + * command.c (option_handler): Ditto. + (cmd_updatestartuptty): Change accordingly. Protect old values + from out of core failures. + * command-ssh.c (start_command_handler_ssh): Ditto. + (start_command_handler_ssh): Replace strdup by xtrystrdup. + * call-pinentry.c (atfork_cb): Pass new envrinmnet variables. + (start_pinentry): Use session_env stuff. + * protect-tool.c (main): Adjust call to gnupg_prepare_get_passphrase. + +2009-06-24 Werner Koch + + * genkey.c (agent_protect_and_store): Return RC and not 0. + * protect.c (do_encryption): Fix ignored error code from malloc. + Reported by Fabian Keil. + +2009-06-17 Werner Koch + + * call-pinentry.c (agent_get_confirmation): Add arg WITH_CANCEL. + Change all callers. + * trustlist.c (agent_marktrusted): Use WITH_CANCEL + +2009-06-09 Werner Koch + + * learncard.c (send_cert_back): Ignore certain error codes. + +2009-06-05 Werner Koch + + * protect-tool.c (store_private_key): Fix last change by appending + a ".key". + +2009-06-03 Werner Koch + + * protect-tool.c: Include estream.h. + (store_private_key): Replace stdio streams by estream functions + for a portable use of the "x" mode. + * trustlist.c: Include estream.h. + (agent_marktrusted): Replace stdio stream by estream functions. + + * protect-tool.c (store_private_key): Use bin2hex. + +2009-06-02 Werner Koch + + * gpg-agent.c (main): Run pth_kill after fork. Fixes bug#1066. + +2009-05-19 Werner Koch + + * gpg-agent.c (JNLIB_NEED_AFLOCAL): Define. + (create_server_socket): Use SUN_LEN macro. + +2009-05-15 Werner Koch + + Fix bug #1053. + + * agent.h (lookup_ttl_t): New. + * findkey.c (unprotect): Add arg LOOKUP_TTL. + (agent_key_from_file): Ditto. + * pksign.c (agent_pksign_do): Ditto. + * command-ssh.c (ttl_from_sshcontrol): New. + (data_sign): Pass new function to agent_pksign_do. + (search_control_file): Add new arg R_TTL. + +2009-05-14 Werner Koch + + * command.c (cmd_get_passphrase): Add option --qualitybar. + * call-pinentry.c (agent_askpin): Factor some code out to ... + (setup_qualitybar): .. new. + (agent_get_passphrase): Add arg WITH_QUALITYBAR and implement it. + +2009-04-14 Marcus Brinkmann + + * call-pinentry.c (agent_get_confirmation): Try SETNOTOK command + with pinentry. + +2009-04-01 Werner Koch + + * protect-tool.c (pe_opt): New. + (opts): Add option --agent-program. Use ARGPARSE macros. + (get_new_passphrase): Remove. + (get_passphrase): Use gpg-agent directly. Remove arg OPT_CHECK and + change all callers. + * Makefile.am (gpg_protect_tool_LDADD): Replace pwquery_libs by + LIBASSUAN_LIBS. + (gpg_protect_tool_CFLAGS): New. + + * command.c (percent_plus_unescape): Remove. + (cmd_putval): Use percent_plus_unescape_inplace. + * call-scd.c (unescape_status_string): Remove. + (card_getattr_cb): Use percent_plus_unescape. + * protect-tool.c (main): Use percent_plus_unescape from common/. + (percent_plus_unescape, percent_plus_unescape_string): Remove. + +2009-03-27 Werner Koch + + * learncard.c (agent_handle_learn): Add new certtype 111. + +2009-03-26 Werner Koch + + * agent.h (MAX_DIGEST_LEN): Change to 64. + * command.c (cmd_sethash): Allow digest length of 48 and 64. + (cmd_sethash): Allow more hash algos. + + * trustlist.c (reformat_name): New. + (agent_marktrusted): Use a reformatted name. Reload the table + before the update and always reload it at the end. + (agent_istrusted): Check early for the disabled flag. + +2009-03-25 Werner Koch + + * pkdecrypt.c (agent_pkdecrypt): Return a specific error message + if the key is not available. + + * gpg-agent.c (main): Print a started message to show the real pid. + +2009-03-20 Werner Koch + + * learncard.c (struct kpinfo_cp_parm_s): Add field CTRL. + (struct certinfo_cb_parm_s): Ditto. + (agent_handle_learn): Set CTRL field. + (kpinfo_cb, certinfo_cb): Send progress status. + + * agent.h (agent_write_status): Flag with GNUPG_GCC_A_SENTINEL. + +2009-03-19 Werner Koch + + * trustlist.c (struct trustitem_s): Add field DISABLED. + (read_one_trustfile): Parse the '!' flag. + (agent_istrusted, agent_listtrusted): Check flag. + (agent_istrusted): Add arg R_DISABLED. Change all callers. + (agent_marktrusted): Do not ask if flagged as disabled. Reverse + the order of the questions. Store the disabled flag. + + * gpg-agent.c (main): Save signal mask and open fds. Restore mask + and close all fds prior to the exec. Fixes bug#1013. + +2009-03-17 Werner Koch + + * command.c (cmd_get_passphrase): Break repeat loop on error. + Show error message. + (cmd_getinfo): Add subcommand "cmd_has_option". + (command_has_option): New. + +2009-03-17 Daiki Ueno + + * command.c (option_value): New function. + (cmd_get_passphrase): Accept new option --repeat, which makes + gpg-agent to ask passphrase several times. + +2009-03-06 Werner Koch + + * command.c (cmd_keyinfo): New command. + (register_commands): Register it. + (agent_write_status): Make sure not to print LR or CR. + * divert-scd.c (ask_for_card): Factor shadow info parsing out to ... + * protect.c (parse_shadow_info): New. + * findkey.c (agent_key_from_file): Use make_canon_sexp. + (agent_write_private_key, unprotect, read_key_file) + (agent_key_available): Use bin2hex. + (agent_key_info_from_file): New. + (read_key_file): Log no error message for ENOENT. + +2009-03-05 Werner Koch + + * divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits + from 8 to 16. Append a message about keypads. + * findkey.c (unprotect): Change max digits to 16. + +2009-03-02 Werner Koch + + * command.c (cmd_getinfo): Add subcommand "scd_running". + + * call-scd.c (agent_scd_check_running): New. + + * gpg-agent.c: Add missing option strings for "--batch" and + "--homedir". Reported by Petr Uzel. + + * protect-tool.c (import_p12_file): Take care of canceled + passphrase entry. Fixes bug#1003. + (export_p12_file): Ditto. + +2008-12-17 Werner Koch + + * gpg-agent.c (handle_connections): Set action of all pth event + handled signals to SIG_IGN. Use a different pth_sigmask strategy. + +2008-12-10 Werner Koch + + * command.c (cmd_get_passphrase): Implement option --no-ask. + +2008-12-09 Werner Koch + + * gpg-agent.c (main): Call i18n_init before init_common_subsystems. + * preset-passphrase.c (main): Ditto. + * protect-tool.c (main): Ditto. + + * command.c (cmd_preset_passphrase): Allow an arbitrary string for + the cache id. + +2008-12-08 Werner Koch + + * gpg-agent.c (handle_connections): Sync the ticker to the next + full second. This is bug#871. + +2008-12-05 Werner Koch + + * minip12.c (decrypt_block): Fix const modified of CHARSETS. + * learncard.c (sinfo_cb_parm_s): Remove superflous semicolon. + Reported by Stoyan Angelov. + +2008-11-18 Werner Koch + + * gpg-agent.c (make_libversion): New. + (my_strusage): Print libgcrypt version + +2008-11-11 Werner Koch + + * call-scd.c (membuf_data_cb): Change return type to + assuan_error_t to avoid warnings with newer libassuan versions. + +2008-11-04 Werner Koch + + * command.c (cmd_killagent): Stop the agent immediately. + (start_command_handler): Take care of GPG_ERR_EOF. + +2008-10-29 Werner Koch + + * gpg-agent.c (main): Move USE_STANDARD_SOCKET to the outer scope. + (create_socket_name): Remove arg USE_STANDARD_SOCKET. Change all + callers. + (create_server_socket): Remove IS_STANDARD_NAME and replace it by + USE_STANDARD_SOCKET. Change all callers. + (check_own_socket_running): New. + (check_own_socket, check_own_socket_thread): New. + (handle_tick): Check server socket once a minute. + (handle_connections): Remove the extra pth_wait in the shutdown + case. + +2008-10-20 Werner Koch + + * command.c (cmd_geteventcounter): Mark unused arg. + (cmd_listtrusted, cmd_pksign, cmd_pkdecrypt, cmd_genkey): Ditto. + (cmd_updatestartuptty, post_cmd_notify): Ditto. + * command-ssh.c (add_control_entry) + (ssh_handler_request_identities, ssh_handler_remove_identity) + (ssh_handler_remove_all_identities, ssh_handler_lock) + (ssh_handler_unlock): Ditto. + * call-pinentry.c (pinentry_active_p, popup_message_thread) + (agent_popup_message_stop): Ditto. + * findkey.c (agent_public_key_from_file): Ditto. + * genkey.c (check_passphrase_pattern): Ditto. + * call-scd.c (atfork_cb): Ditto. + * protect-tool.c (import_p12_cert_cb): Ditto. + * t-protect.c (main): Ditto. + +2008-10-17 Werner Koch + + * call-scd.c (start_scd) [W32]: Use snprintf again because we now + always use the estream variant. + +2008-10-15 Werner Koch + + * call-scd.c (start_scd): Enable assuan loggging if requested. + (agent_scd_check_aliveness) [W32]: Fix use of GetExitCodeProcess. + +2008-10-14 Werner Koch + + * gpg-agent.c (get_agent_scd_notify_event): Need to use a manual + reset event. + +2008-09-29 Werner Koch + + * agent.h (GCRY_MD_USER): Rename to GCRY_MODULE_ID_USER. + (GCRY_MD_USER_TLS_MD5SHA1): Rename to MD_USER_TLS_MD5SHA1 and + change all users. + +2008-09-25 Werner Koch + + * divert-scd.c (getpin_cb): Support a Reset Code style PINs.. + +2008-09-03 Werner Koch + + * command.c (parse_keygrip): Use hex2bin. + (cmd_preset_passphrase): Decode the passphrase. Reported by Kiss + Gabor. Fixes #679 again. + * preset-passphrase.c (make_hexstring): Remove. + (preset_passphrase): Use bin2hex. + +2008-05-27 Werner Koch + + * trustlist.c (insert_colons): Fix stupidly wrong allocation size + computation. + +2008-05-26 Werner Koch + + * gpg-agent.c (main): Re-initialize default assuan log stream if a + log file is used. + + * trustlist.c (agent_marktrusted): Use xtryasprintf and xfree. + + * gpg-agent.c (main, agent_deinit_default_ctrl): Always use xfree + because our asprintf is mapped to an xmalloc style function in + util.h. Replace xstrdup by xtrystrdup. + * w32main.c (build_argv): Ditto. + * preset-passphrase.c (preset_passphrase): Ditto. + * divert-scd.c (ask_for_card): Ditto. + * command.c (option_handler): Ditto. + * command-ssh.c (ssh_handler_request_identities): Ditto. + * call-pinentry.c (start_pinentry): Ditto. + + * gpg-agent.c (start_connection_thread) + (start_connection_thread_ssh): Use pth_thread_id for useful output + under W32. + (pth_thread_id) [!PTH_HAVE_PTH_THREAD_ID]: New. + +2008-03-17 Werner Koch + + * agent.h (agent_inq_pinentry_launched): New prototype. + + * call-pinentry.c: Include sys/types.h and signal.h. + +2008-02-14 Werner Koch + + * command.c (agent_inq_pinentry_launched): New. + (option_handler): Add option allow-pinentry-notify. + * call-pinentry.c (getinfo_pid_cb): New. + (start_pinentry): Ask for the PID and notify the client. + +2008-01-15 Marcus Brinkmann + + * call-pinentry.c (start_pinentry): Start pinentry in detached + mode. + +2007-12-04 Werner Koch + + * call-pinentry.c (agent_askpin): Use gnupg_get_help_string. + +2007-12-03 Werner Koch + + * gpg-agent.c (main): s/standard_socket/use_standard_socket/ for + clarity. + (create_server_socket): New arg IS_SSH to avoid testing with + assuan commands. + +2007-11-20 Werner Koch + + * gpg-agent.c (get_agent_scd_notify_event): New. + (handle_signal): Factor SIGUSR2 code out to: + (agent_sigusr2_action): .. New. + (agent_sighup_action): Print info message here and not in + handle_signal. + (handle_connections) [PTH_EVENT_HANDLE]: Call agent_sigusr2_action. + + * call-scd.c (agent_scd_check_aliveness) [W32]: Implemented. + (start_scd) [W32]: Send event-signal option. + +2007-11-19 Werner Koch + + * call-pinentry.c (agent_askpin): Set the tooltip for the quality + bar. + +2007-11-15 Werner Koch + + * agent.h (struct server_control_s): Add XAUTHORITY and + PINENTRY_USER_DATA. + * gpg-agent.c: New option --xauthority. + (main, agent_init_default_ctrl) + (agent_deinit_default_ctrl): Implemented + * command.c (cmd_updatestartuptty): Ditto. + * command-ssh.c (start_command_handler_ssh): Ditto. + * call-pinentry.c (atfork_cb): Set the environment. + (start_pinentry): Pass CTRL as arg to atfork_cb. + +2007-11-14 Werner Koch + + * call-scd.c (start_scd) [W32]: Take care of fflush peculiarities. + +2007-11-07 Werner Koch + + * agent.h: Remove errors.h. + +2007-10-24 Werner Koch + + * genkey.c (check_passphrase_constraints): Changed the wording of + the warning messages. + +2007-10-19 Werner Koch + + * protect-tool.c (get_passphrase): Use new utf8 switch fucntions. + +2007-10-15 Daiki Ueno (wk) + + * command-ssh.c (reenter_compare_cb): New function; imported from + genkey.c. + (ssh_identity_register): Ask initial passphrase twice. + +2007-10-02 Werner Koch + + * command.c (cmd_getinfo): Add "pid" subcommand. + +2007-10-01 Werner Koch + + * agent.h (struct server_control_s): Remove unused CONNECTION_FD. + + * gpg-agent.c: Remove w32-afunix.h. Include mkdtemp.h. + (socket_nonce, socket_nonce_ssh): New. + (create_server_socket): Use assuan socket wrappers. Remove W32 + specific stuff. Save the server nonce. + (check_nonce): New. + (start_connection_thread, start_connection_thread_ssh): Call it. + (handle_connections): Change args to gnupg_fd_t. + * command.c (start_command_handler): Change LISTEN_FD to gnupg_fd_t. + * command-ssh.c (start_command_handler_ssh): Ditto. + +2007-09-18 Werner Koch + + * agent.h (struct pin_entry_info_s): Add element WITH_QUALITYBAR. + * genkey.c (check_passphrase_constraints): New arg SILENT. + Changed all callers. + (agent_protect_and_store, agent_genkey): Enable qualitybar. + * call-pinentry.c (agent_askpin): Send that option. + (unescape_passphrase_string): New. + (inq_quality): New. + (estimate_passphrase_quality): New. + +2007-09-14 Marcus Brinkmann + + * call-pinentry.c (agent_popup_message_stop): Implement kill for + Windows. + +2007-08-28 Werner Koch + + * gpg-agent.c (main): Add option --faked-system-time. + + * protect-tool.c (read_and_unprotect): Print the protected-at date. + + * agent.h (struct server_control_s): Add member IN_PASSWD. + * command.c (cmd_passwd): Set it. + * findkey.c (try_unprotect_cb): Use it. + + * protect.c (do_encryption): Replace asprintf by xtryasprint. + (agent_protect): Create the protected-at item. + (agent_unprotect): Add optional arg PROTECTED_AT. + (merge_lists): Add args CUTOFF and CUTLEN. + (agent_unprotect): Use them. + * findkey.c (try_unprotect_cb): Add code to test for expired keys. + (unprotect): Allow changing the passphrase. + +2007-08-27 Werner Koch + + * gpg-agent.c: Add options --min-passphrase-nonalpha, + --check-passphrase-pattern and --enforce-passphrase-constraints. + (MIN_PASSPHRASE_NONALPHA): Init nonalpha option to 1. + (main): Declare options for gpgconf. + * agent.h (struct): Add members MIN_PASSPHRASE_NONALPHA, + ENFORCE_PASSPHRASE_CONSTRAINTS and CHECK_PASSPHRASE_PATTERN. + * genkey.c (nonalpha_charcount): New. + (check_passphrase_pattern): New. + (check_passphrase_constraints): Implement. Factor some code out... + (take_this_one_anyway, take_this_one_anyway2): .. New. + + * call-pinentry.c (agent_show_message): New. + (agent_askpin): We better reset the pin buffer before asking. + + * trustlist.c (insert_colons): New. + (agent_marktrusted): Pretty print the fpr. + +2007-08-22 Werner Koch + + * findkey.c (O_BINARY): Make sure it is defined. + (agent_write_private_key): Use O_BINARY + + * protect-tool.c (import_p12_file): Add hack to allow importing of + gnupg 2.0.4 generated files. + +2007-08-06 Werner Koch + + * trustlist.c (read_one_trustfile): Add flag "cm". + (agent_istrusted): Ditto. + +2007-08-02 Werner Koch + + * gpg-agent.c: Include gc-opt-flags.h and remove their definition + here. + +2007-07-13 Werner Koch + + * genkey.c (check_passphrase_constraints): Require a confirmation + for an empty passphrase. + (agent_genkey, agent_protect_and_store): No need to repeat an + empty passphrase. + +2007-07-05 Werner Koch + + * call-scd.c (struct inq_needpin_s): New. + (inq_needpin): Pass unknown inquiries up. + +2007-07-04 Werner Koch + + * gpg-agent.c (TIMERTICK_INTERVAL): New. + (fixed_gcry_pth_init, main): Kludge to fix Pth initialization. + +2007-07-03 Werner Koch + + * gpg-agent.c (handle_connections): Do not use FD_SETSIZE for + select but compute the correct number. + +2007-07-02 Werner Koch + + * command.c (cmd_reloadagent) [W32]: New. + (register_commands) [W32]: New command RELOADAGENT. + + * Makefile.am (gpg_agent_SOURCES): Remove w32main.c and w32main.h. + (gpg_agent_res_ldflags): Remove icon file as we don't have a + proper icon yet. + * gpg-agent.c (main): do not include w32main.h. Remove all calls + to w32main.c. + (agent_sighup_action): New. + (handle_signal): Use it. + +2007-06-26 Werner Koch + + * gpg-agent.c (create_directories) [W32]: Made it work. + +2007-06-21 Werner Koch + + * agent.h (ctrl_t): Remove. It is now declared in ../common/util.h. + + * gpg-agent.c (check_for_running_agent): New arg SILENT. Changed + all callers. + (create_server_socket): If the standard socket is in use check + whether a agent is running and avoid starting another one. + +2007-06-18 Marcus Brinkmann + + * gpg-agent.c (main): Percent escape pathname in --gpgconf-list + output. + +2007-06-18 Werner Koch + + * w32main.c (build_argv): New. + (WinMain): Use it. + + * command.c (cmd_killagent) [W32]: New. + (cmd_getinfo): New. + * gpg-agent.c (get_agent_ssh_socket_name): New. + (no_force_standard_socket) New. + (create_server_socket): Use it. + * Makefile.am (gpg_agent_res_ldflags): Pass windows option to ld. + +2007-06-14 Werner Koch + + * protect-tool.c (main): Setup default socket name for + simple-pwquery. + (MAP_SPWQ_ERROR_IMPL): New. Use map_spwq_error for spqw related + error codes. + * preset-passphrase.c (main): Setup default socket name for + simple-pwquery. + (map_spwq_error): Remove. + (MAP_SPWQ_ERROR_IMPL): New. + + * call-pinentry.c (start_pinentry): Use gnupg_module_name. + * call-scd.c (start_scd): Ditto. + +2007-06-12 Werner Koch + + * taskbar.c: New. + + * trustlist.c (read_one_trustfile): Replace GNUPG_SYSCONFDIR by a + function call. + (read_trustfiles): Ditto. + + * gpg-agent.c (main): Replace some calls by init_common_subsystems. + * preset-passphrase.c (main): Ditto. + * protect-tool.c (main): Ditto. + +2007-06-11 Werner Koch + + * Makefile.am (common_libs): Use libcommonstd macro. + (commonpth_libs): Use libcommonpth macro. + + * protect-tool.c (main) [W32]: Call pth_init. + + * preset-passphrase.c (main) [W32]: Replace the explicit Winsocket + init by a call to pth_init. + + * trustlist.c (initialize_module_trustlist): New. + * gpg-agent.c (main): Call it. + + * call-pinentry.c (initialize_module_query): Rename to + initialize_module_call_pinentry. + + * minip12.c: Remove iconv.h. Add utf8conf.h. Changed all iconv + calss to use these jnlib wrappers. + +2007-06-06 Werner Koch + + * minip12.c (enum): Rename CONTEXT to ASNCONTEXT as winnt.h + defines such a symbol to access the process context. + + * call-pinentry.c (dump_mutex_state) [W32]: Handle the W32Pth case. + * call-scd.c (dump_mutex_state): Ditto. + + * protect-tool.c (i18n_init): Remove. + * preset-passphrase.c (i18n_init): Remove. + * gpg-agent.c (i18n_init): Remove. + +2007-05-19 Marcus Brinkmann + + * protect-tool.c (get_passphrase): Free ORIG_CODESET on error. + +2007-05-14 Werner Koch + + * protect.c (make_shadow_info): Replace sprintf by smklen. + +2007-04-20 Werner Koch + + * gpg-agent.c (my_gcry_logger, my_gcry_outofcore_handler): Removed. + (main): Call the setup_libgcrypt_logging helper. + * protect-tool.c (my_gcry_logger): Removed. + (main): Call the setup_libgcrypt_logging helper. + +2007-04-03 Werner Koch + + * trustlist.c (read_trustfiles): Take a missing trustlist as an + empty one. + +2007-03-20 Werner Koch + + * protect-tool.c: New option --p12-charset. + * minip12.c (p12_build): Implement it. + +2007-03-19 Werner Koch + + * minip12.c: Include iconv.h. + (decrypt_block): New. + (parse_bag_encrypted_data, parse_bag_data): Use it here. + (bag_data_p, bag_decrypted_data_p): New helpers. + +2007-03-06 Werner Koch + + * gpg-agent.c (main) : Add entries for all ttl options. + +2007-02-20 Werner Koch + + * call-pinentry.c (start_pinentry): Fix for OS X to allow loading + of the bundle. Tested by Benjamin Donnachie. + +2007-02-14 Werner Koch + + * gpg-agent.c: New option --pinentry-touch-file. + (get_agent_socket_name): New. + * agent.h (opt): Add pinentry_touch_file. + * call-pinentry.c (start_pinentry): Send new option to the + pinentry. + +2007-01-31 Moritz Schulte (wk) + + * command-ssh.c (stream_read_string): Initialize LENGTH to zero. + (start_command_handler_ssh): Use es_fgetc/es_ungetc to check if + EOF has been reached before trying to process another request. + +2007-01-31 Werner Koch + + * command-ssh.c (start_command_handler_ssh): + + * Makefile.am (t_common_ldadd): Add LIBICONV. + +2007-01-25 Werner Koch + + * genkey.c (check_passphrase_constraints): Get ngettext call right + and use UTF-8 aware strlen. + + * protect-tool.c (get_passphrase): New arg OPT_CHECK. + (get_new_passphrase): Enable OPT_CHECK on the first call. + * command.c (cmd_get_passphrase): Implement option --check. + +2007-01-24 Werner Koch + + * gpg-agent.c (MIN_PASSPHRASE_LEN): New + (parse_rereadable_options): New option --min-passphrase-len. + * genkey.c (check_passphrase_constraints): New. + (agent_genkey, agent_protect_and_store): Call new function. Fix + memory leak. + + * call-pinentry.c (agent_askpin): Allow translation of the displayed + error message. + (agent_popup_message_start): Remove arg CANCEL_BTN. + (popup_message_thread): Use --one-button option. + + * command.c (cmd_passwd): Now that we don't distinguish between + assuan and regular error codes we can jump to the end on error. + +2006-12-07 David Shaw + + * Makefile.am: Link to iconv for jnlib dependency. + +2006-11-20 Werner Koch + + * call-pinentry.c (agent_popup_message_stop): Use SIGKILL. + * call-scd.c (inq_needpin): Implement POPUPKEYPADPROMPT and + DISMISSKEYPADPROMPT. + +2006-11-15 Werner Koch + + * protect.c (make_shadow_info): Cast printf arg to unsigned int. + * minip12.c (parse_bag_encrypted_data): Ditto. + (parse_bag_data, p12_parse): Ditto. + * command-ssh.c (ssh_identity_register): Changed buffer_n to + size_t. + + * agent.h (struct server_control_s): New field thread_startup. + * command.c (start_command_handler): Moved CTRL init code to .. + * gpg-agent.c (start_connection_thread): .. here. + (agent_deinit_default_ctrl): New. + (agent_init_default_ctrl): Made static. + (handle_connections): Allocate CTRL and pass it pth_spawn. + * command-ssh.c (start_command_handler_ssh): Moved CTRL init code + to .. + * gpg-agent.c (start_connection_thread_ssh): .. here. + +2006-11-14 Werner Koch + + * command.c (bump_key_eventcounter): New. + (bump_card_eventcounter): New. + (cmd_geteventcounter): New command. + * gpg-agent.c (handle_signal): Call bump_card_eventcounter. + * findkey.c (agent_write_private_key): Call bump_key_eventcounter. + * trustlist.c (agent_reload_trustlist): Ditto. + + * command.c (post_cmd_notify, io_monitor): New. + (register_commands, start_command_handler): Register them. + +2006-11-09 Werner Koch + + * gpg-agent.c (main): In detached mode connect standard + descriptors to /dev/null. + + * trustlist.c (read_trustfiles): Make sure not to pass a zero size + to realloc as the C standards says that this behaves like free. + +2006-11-06 Werner Koch + + * protect-tool.c (my_strusage): Fixed typo. + +2006-10-23 Werner Koch + + * gpg-agent.c (main): New command --gpgconf-test. + + * minip12.c (parse_bag_encrypted_data, parse_bag_data): Allow for + a salt of 20 bytes. + +2006-10-20 Werner Koch + + * Makefile.am (t_common_ldadd): Use GPG_ERROR_LIBS instead -o just -l + +2006-10-19 Werner Koch + + * findkey.c (unprotect): Use it to avoid unnecessary calls to + agent_askpin. + * call-pinentry.c (pinentry_active_p): New. + +2006-10-17 Werner Koch + + * Makefile.am (gpg_agent_LDADD): Link to libcommonpth. + (gpg_agent_CFLAGS): New. This allows to only link this with Pth. + +2006-10-16 Werner Koch + + * call-pinentry.c (agent_get_confirmation): Map Cancel code here too. + * trustlist.c (agent_marktrusted): Return Cancel instead of + Not_Confirmed for the first question. + +2006-10-12 Werner Koch + + * protect-tool.c (get_passphrase): Fix if !HAVE_LANGINFO_CODESET. + +2006-10-06 Werner Koch + + * Makefile.am (AM_CFLAGS): Use PTH version of libassuan. + (gpg_agent_LDADD): Ditto. + + * divert-scd.c (divert_pksign): Use PKAUTH for the TLS algo. + +2006-10-05 Werner Koch + + * command.c (has_option_name): New. + (cmd_sethash): New --hash option. + * pksign.c (do_encode_raw_pkcs1): New. + (agent_pksign_do): Use it here for the TLS algo. + * agent.h (GCRY_MD_USER_TLS_MD5SHA1): New. + * divert-scd.c (pksign): Add case for tls-md5sha1. + + * divert-scd.c (encode_md_for_card): Check that the algo is valid. + +2006-10-04 Werner Koch + + * call-pinentry.c (agent_get_passphrase): Changed to return the + unencoded passphrase. + (agent_askpin, agent_get_passphrase, agent_get_confirmation): Need + to map the cancel error. + * command.c (send_back_passphrase): New. + (cmd_get_passphrase): Use it here. Also implement --data option. + (skip_options): New. + +2006-09-26 Werner Koch + + * learncard.c (agent_handle_learn): Send back the keypair + information. + +2006-09-25 Werner Koch + + * trustlist.c (read_one_trustfile): Allow extra flags. + (struct trustitem_s): Replaced KEYFLAGS by a FLAGS struct. + Changed all code to use this. + (agent_istrusted): New arg CTRL. Changed all callers. Send back + flags. + * command.c (agent_write_status): New. + +2006-09-20 Werner Koch + + * Makefile.am: Changes to allow parallel make runs. + +2006-09-15 Werner Koch + + * trustlist.c: Entirely rewritten. + (agent_trustlist_housekeeping): Removed and removed all calls. + +2006-09-14 Werner Koch + + Replaced all call gpg_error_from_errno(errno) by + gpg_error_from_syserror(). + + * call-pinentry.c (start_pinentry): Replaced pipe_connect2 by + pipe_connect_ext. + * call-scd.c (start_scd): Ditto. + * command.c (start_command_handler): Replaced + init_connected_socket_server by init_socket_server_ext. + +2006-09-13 Werner Koch + + * preset-passphrase.c (main) [W32]: Check for WSAStartup error. + +2006-09-08 Werner Koch + + * call-scd.c: Add signal.h as we are referencing SIGUSR2. + +2006-09-06 Marcus Brinkmann + + * Makefile.am (AM_CFLAGS): Add $(GPG_ERR_CFLAGS). + (gpg_agent_LDADD): Replace -lgpg-error with $(GPG_ERROR_LIBS). + +2006-09-06 Werner Koch + + * query.c: Renamed to .. + * call-pinentry.c: .. this. + + * agent.h (out_of_core): Removed. + (CTRL): Removed and changed everywhere to ctrl_t. + + Replaced all Assuan error codes by libgpg-error codes. Removed + all map_to_assuan_status and map_assuan_err. + + * gpg-agent.c (main): Call assuan_set_assuan_err_source to have Assuan + switch to gpg-error codes. + * command.c (set_error): Adjusted. + +2006-09-04 Werner Koch + + * command.c (percent_plus_unescape): New. + (cmd_get_val, cmd_putval): New. + +2006-08-29 Werner Koch + + * command-ssh.c (stream_read_mpi): Sanity check for early + detecting of too large keys. + * gpg-agent.c (my_gcry_outofcore_handler): New. + (main): Register it. + (main): No allocate 32k secure memory (was 16k). + +2006-07-31 Werner Koch + + * preset-passphrase.c (make_hexstring): For consistency use + xtrymalloc and changed caller to use xfree. Fixed function + comment. + +2006-07-29 Marcus Brinkmann + + * preset-passphrase.c (preset_passphrase): Do not strip off last + character of passphrase. + (make_hexstring): New function. + * command.c (cmd_preset_passphrase): Use parse_hexstring to syntax + check passphrase argument. Truncate passphrase at delimiter. + +2006-07-24 Werner Koch + + * minip12.c (build_key_bag): New args SHA1HASH and + KEYIDSTR. Append bag Attributes if these args are given. + (build_cert_sequence): ditto. + (p12_build): Calculate certificate hash and pass to build + functions. + +2006-07-21 Werner Koch + + * minip12.c (oid_pkcs_12_keyBag): New. + (parse_bag_encrypted_data): New arg R_RESULT. Support keybags and + return the key object. + (p12_parse): Take new arg into account. Free RESULT on error. + +2006-06-26 Werner Koch + + * gpg-agent.c (handle_signal): Print info for SIGUSR2 only in + verbose mode. + +2006-06-22 Werner Koch + + * command-ssh.c (make_cstring): Use memcpy instead of strncpy. + (ssh_receive_mpint_list, sexp_key_extract, data_sign): Use + xtrycalloc instead of xtrymalloc followed by memset. + +2006-06-20 Werner Koch + + * minip12.c (create_final): New arg PW. Add code to calculate the + MAC. + +2006-06-09 Marcus Brinkmann + + * Makefile.am (gpg_agent_LDADD): Add $(NETLIBS). + (gpg_protect_tool_LDADD): Likewise. + (gpg_preset_passphrase_LDADD): Likewise. + +2006-04-09 Moritz Schulte + + * command-ssh.c (ssh_request_process): Removed FIXME mentioning a + possible DoS attack. + +2006-04-01 Moritz Schulte + + * command-ssh.c (ssh_identity_register): Make KEY_GRIP_RAW be 20 + instead of 21 bytes long; do not fill KEY_GRIP_RAW[20] with NUL + byte - KEY_GRIP_RAW is a raw binary string anyway. + +2006-02-09 Werner Koch + + * call-scd.c (struct scd_local_s): New field next_local. + (scd_local_list): New. + (start_scd): Put new local into list. + (agent_reset_scd): Remove it from the list. + (agent_scd_check_aliveness): Here is the actual reason why we need + all this stuff. + (agent_reset_scd): Send the new command RESTART instead of RESET. + +2005-12-16 Werner Koch + + * minip12.c (cram_octet_string): New + (p12_parse): Use it for NDEFed bags. + (parse_bag_data): Ditto. + (string_to_key, set_key_iv, crypt_block): New arg SALTLEN. + (p12_build): Use old value 8 for new arg. + (parse_bag_encrypted_data, parse_bag_data): Allow for salts of 8 + to 16 bytes. Add new arg R_CONSUMED. + +2005-11-24 Werner Koch + + * minip12.c (p12_parse): Fixed for case that the key object comes + prior to the certificate. + +2005-10-19 Werner Koch + + * divert-scd.c (getpin_cb): Hack to use it for a keypad message. + + * call-scd.c (inq_needpin): Reworked to support the new KEYPADINFO. + + * query.c (start_pinentry): Keep track of the owner. + (popup_message_thread, agent_popup_message_start) + (agent_popup_message_stop, agent_reset_query): New. + * command.c (start_command_handler): Make sure a popup window gets + closed. + +2005-10-08 Marcus Brinkmann + + * Makefile.am (gpg_protect_tool_LDADD): Add ../gl/libgnu.a. + (gpg_preset_passphrase_LDADD, t_common_ldadd): Likewise. + (gpg_agent_LDADD): Add ../gl/libgnu.a after ../common/libcommon.a. + +2005-09-16 Werner Koch + + * minip12.c (build_key_sequence, build_cert_sequence): Fixed + padding. + +2005-09-15 Moritz Schulte + + * t-protect.c (test_agent_protect): Implemented. + (main): Disable use of secure memory. + +2005-09-09 Werner Koch + + * minip12.c (p12_build): Oops, array needs to be larger for the + certificate. + (build_cert_bag): Fixed yesterdays change. + + * command-ssh.c (card_key_available): Let the card handler decide + whether the card is supported here. Also get a short serial + number to return from the card handler. + +2005-09-08 Werner Koch + + * minip12.c (build_cert_bag): Use a non constructed object. + i.e. 0x80 and not 0xa0. + +2005-08-16 Werner Koch + + * gpg-agent.c (main): Use a default file name for --write-env-file. + +2005-07-25 Werner Koch + + * findkey.c (agent_public_key_from_file): Fixed array assignment. + This was the cause for random segvs. + +2005-06-29 Werner Koch + + * command-ssh.c (data_sign): Removed empty statement. + +2005-06-21 Werner Koch + + * minip12.c (create_final): Cast size_t to ulong for printf. + (build_key_bag, build_cert_bag, build_cert_sequence): Ditto. + +2005-06-16 Werner Koch + + * protect-tool.c (make_advanced): Makde RESULT a plain char. + * call-scd.c (unescape_status_string): Need to cast unsigned char* + for strcpy. + (agent_card_pksign): Made arg R_BUF an unsigned char**. + * divert-scd.c (divert_pksign): Made SIGVAL unsigned char*. + (encode_md_for_card): Initialize R_VAL and R_LEN. + * genkey.c (store_key): Made BUF unsigned. + * protect.c (do_encryption): Ditto. + (do_encryption): Made arg PROTBEGIN unsigned. Initialize RESULT + and RESULTLEN even on error. + (merge_lists): Need to cast unsigned char * for strcpy. Initialize + RESULTand RESULTLEN even on error. + (agent_unprotect): Likewise for strtoul. + (make_shadow_info): Made P and INFO plain char. + (agent_shadow_key): Made P plain char. + +2005-06-15 Werner Koch + + * query.c (agent_get_passphrase): Made HEXSTRING a char*. + * command-ssh.c (ssh_key_grip): Made arg BUFFER unsigned. + (ssh_key_grip): Simplified. + (data_sign): Initialize variables with the definition. + (ssh_convert_key_to_blob): Make sure that BLOB and BLOB_SIZE + are set to NULL on error. Cool, gcc-4 detects uninitialized stuff + beyond function boundaries; well it can't know that we do error + proper error handling so that this was not a real error. + (file_to_buffer): Likewise for BUFFER and BUFFER_N. + (data_sign): Likewise for SIG and SIG_N. + (stream_read_byte): Set B to a value even on error. + * command.c (cmd_genkey): Changed VALUE to char. + (cmd_readkey): Cast arg for gcry_sexp_sprint. + * agent.h (struct server_control_s): Made KEYGRIP unsigned. + +2005-06-13 Werner Koch + + * command-ssh.c (start_command_handler_ssh): Reset the SCD. + +2005-06-09 Werner Koch + + * gpg-agent.c (create_socket_name): New option --max-cache-ttl-ssh. + * cache.c (housekeeping): Use it. + (agent_put_cache): Use a switch to get the default ttl so that it + is easier to add more cases. + +2005-06-06 Werner Koch + + * gpg-agent.c: New option --default-cache-ttl-ssh. + * agent.h (cache_mode_t): New. + * pksign.c (agent_pksign_do): New arg CACHE_MODE to replace the + ARG IGNORE_CACHE. Changed all callers. + (agent_pksign): Ditto. + * findkey.c (agent_key_from_file): Ditto. Canged all callers. + (unprotect): Ditto. + * command-ssh.c (data_sign): Use CACHE_MODE_SSH. + * cache.c (agent_get_cache): New arg CACHE_MODE. + (agent_put_cache): Ditto. Store it in the cache. + + * query.c (agent_query_dump_state, dump_mutex_state): New. + (unlock_pinentry): Reset the global context before releasing the + mutex. + * gpg-agent.c (handle_signal): Dump query.c info on SIGUSR1. + + * call-scd.c (agent_scd_check_aliveness): Always do a waitpid and + add a timeout to the locking. + +2005-06-03 Werner Koch + + * command.c (cmd_updatestartuptty): New. + + * gpg-agent.c: New option --write-env-file. + + * gpg-agent.c (handle_connections): Make sure that the signals we + are handling are not blocked.Block signals while creating new + threads. + +2005-06-02 Werner Koch + + * call-scd.c (agent_scd_dump_state, dump_mutex_state): New. + * gpg-agent.c (handle_signal): Print it on SIGUSR1. + (handle_connections): Include the file descriptor into the + threadnames. + +2005-06-01 Werner Koch + + * gpg-agent.c: Include setenv.h. + +2005-05-31 Werner Koch + + * agent.h (out_of_core): s/__inline__/inine. Noted by Ray Link. + +2005-05-25 Werner Koch + + * gpg-agent.c (main): Do not unset the DISPLAY when we are + continuing as child. + +2005-05-24 Werner Koch + + * call-scd.c (inq_needpin): Skip leading spaces in of PIN + description. + * divert-scd.c (getpin_cb): Enhanced to cope with description + flags. + * query.c (agent_askpin): Add arg PROMPT_TEXT. Changed all + callers. + +2005-05-21 Werner Koch + + * call-scd.c (start_scd): Don't test for an alive scdaemon here. + (agent_scd_check_aliveness): New. + * gpg-agent.c (handle_tick): Test for an alive scdaemon. + (handle_signal): Print thread info on SIGUSR1. + +2005-05-20 Werner Koch + + * protect-tool.c: New option --canonical. + (show_file): Implement it. + + * keyformat.txt: Define the created-at attribute for keys. + +2005-05-18 Werner Koch + + * divert-scd.c (ask_for_card): Removed the card reset kludge. + +2005-05-17 Werner Koch + + * call-scd.c (unlock_scd): Add new arg CTRL. Changed all callers. + (start_scd): Reoworked to allow for additional connections. + * agent.h (ctrl_t): Add local data for the SCdaemon. + * command.c (start_command_handler): Release SERVER_LOCAL. + + * gpg-agent.c (create_server_socket): Use xmalloc. + (main): Removed option --disable-pth a dummy. Removed non-pth + code path. + (cleanup_sh): Removed. Not needed anymore. + +2005-05-05 Moritz Schulte + + * command-ssh.c (ssh_key_to_buffer): Rename to ... + (ssh_key_to_protected_buffer): ... this; change callers. + Improved documentation. + Use ssh_key_grip(), where gcry_pk_get_keygrip() has been used + before. + (ssh_handler_sign_request): Removed unusued variable P. + +2005-04-20 Moritz Schulte + + * command-ssh.c (ssh_handler_request_identities): Removed + debugging code (sleep call), which was commited unintenionally. + +2005-04-20 Werner Koch + + * minip12.c (parse_bag_encrypted_data): Fix the unpadding hack. + + * gpg-agent.c: New option --disable-scdaemon. + (handle_connections): Add time event to drive ... + (handle_tick): New function. + (main): Record the parent PID. Fixed segv when using ssh and a + command. + + * call-scd.c (start_scd): Take care of this option. + +2005-04-03 Moritz Schulte + + * command-ssh.c (ssh_request_spec): New member: secret_input. + (REQUEST_SPEC_DEFINE): New argument: secret_input. + (request_specs): Add secret_input flag. + (request_spec_lookup): New function ... + (ssh_request_process): ... use it here; depending on secret_input + flag allocate secure or non-secure memory. + +2005-03-02 Moritz Schulte + + * command-ssh.c (sexp_key_extract): Removed FIXME, since + xtrymallos does set errno correctly by now. + (sexp_extract_identifier): Remove const attribute from identifier. + (ssh_handler_request_identities): Remove const attribute from + key_type; removes ugly casts and FIXME. + (sexp_key_extract): Remove const attribute from comment. + (ssh_send_key_public): Remove const attribute from + key_type/comment; removes ugly cast. + (data_sign): Remove const attribute from identifier; removes ugly + cast. + (key_secret_to_public): Remove const attribute from comment; + removes ugly cast. + (ssh_handler_sign_request): Remove const attribute from p. + (sexp_key_extract): Use make_cstring(). + (ssh_key_extract_comment): Likewise. + (ssh_key_to_buffer): Use secure memory for memory area to hold the + key S-Expression. + Added more comments. + +2005-02-25 Werner Koch + + * findkey.c (modify_description): Keep invalid % escapes, so that + %0A may pass through. + + * agent.h (server_control_s): New field USE_AUTH_CALL. + * call-scd.c (agent_card_pksign): Make use of it. + * command-ssh.c (data_sign): Set the flag. + (ssh_send_key_public): New arg OVERRIDE_COMMENT. + (card_key_available): Add new arg CARDSN. + (ssh_handler_request_identities): Use the card s/n as comment. + (sexp_key_extract): Use GCRYMPI_FMT_STD. + (data_sign): Ditto. + + * learncard.c (make_shadow_info): Moved to .. + * protect.c (make_shadow_info): .. here. Return NULL on malloc + failure. Made global. + * agent.h: Add prototype. + +2005-02-24 Werner Koch + + * call-scd.c (unescape_status_string): New. Actual a copy of + ../g10/call-agent.c + (card_getattr_cb, agent_card_getattr): New. + + * command-ssh.c (card_key_available): New. + (ssh_handler_request_identities): First see whether a card key is + available. + + * gpg-agent.c (handle_connections): Need to check for events if + select returns with -1. + +2005-02-23 Werner Koch + + * command-ssh.c (get_passphrase): Removed. + (ssh_identity_register): Partly rewritten. + (open_control_file, search_control_file, add_control_entry): New. + (ssh_handler_request_identities): Return only files listed in our + control file. + + * findkey.c (unprotect): Check for allocation error. + + * agent.h (opt): Add fields to record the startup terminal + settings. + * gpg-agent.c (main): Record them and do not force keep display + with --enable-ssh-support. + * command-ssh.c (start_command_handler_ssh): Use them here. + + * gpg-agent.c: Renamed option --ssh-support to + --enable-ssh-support. + + * command.c (cmd_readkey): New. + (register_commands): Register new command "READKEY". + + * command-ssh.c (ssh_request_process): Improved logging. + + * findkey.c (agent_write_private_key): Always use plain open. + Don't depend on an umask for permissions. + (agent_key_from_file): Factored file reading code out to .. + (read_key_file): .. new function. + (agent_public_key_from_file): New. + +2005-02-22 Werner Koch + + * command-ssh.c (stream_read_string): Removed call to abort on + memory error because the CVS version of libgcrypt makes sure + that ERRNO gets always set on error even with a faulty user + supplied function. + +2005-02-19 Moritz Schulte + + * command-ssh.c (ssh_receive_mpint_list): Slightly rewritten, do + not use elems_secret member of key_spec. + (ssh_key_type_spec): Removed member: elems_secret. + (ssh_key_types): Removed elems_secret data. + (ssh_sexp_construct): Renamed to ... + (sexp_key_construct): ... this; changed callers. + (ssh_sexp_extract): Renamed to ... + (sexp_key_extract): ... this; changed callers. + (ssh_sexp_extract_key_type): Renamed to ... + (sexp_extract_identifier): ... this; changed callers; use + make_cstring(). + Added more comments. + +2005-02-18 Moritz Schulte + + * command-ssh.c (ssh_sexp_construct): Rewritten generation of sexp + template, clarified. + (ssh_sexp_extract): Support shadowed-private-key-sexp; treat + protected-private key and shadowed-private-key as public keys. + (key_secret_to_public): Rewritten: simply use ssh_sexp_extract() + and ssh_sexp_construct(). + +2005-02-15 Werner Koch + + * findkey.c (modify_description): Don't increment OUT_LEN during + the second pass. + +2005-02-14 Moritz Schulte + + * command-ssh.c (es_read_byte): Renamed to ... + (stream_es_read_byte): ... this; changed callers. + (es_write_byte): Renamed to ... + (stream_write_byte): ... this; changed callers. + (es_read_uint32): Renamed to ... + (stream_read_uint32): ... this; changed callers. + (es_write_uint32): Renamed to ... + (stream_write_uint32): ... this; changed callers. + (es_read_data): Renamed to ... + (stream_read_data): ... this; changed callers. + (es_write_data): Renamed to ... + (stream_write_data): ... this; changed callers. + (es_read_string): Renamed to ... + (stream_read_string): ... this; changed callers. + (es_read_cstring): Renamed to ... + (stream_read_cstring): ... this; changed callers. + (es_write_string): Renamed to ... + (stream_write_string): ... this; changed callers. + (es_write_cstring): Renamed to ... + (stream_write_cstring): ... this; changed callers. + (es_read_mpi): Renamed to ... + (stream_read_mpi): ... this; changed callers. + (es_write_mpi): Renamed to ... + (stream_write_mpi): ... this; changed callers. + (es_copy): Renamed to ... + (stream_copy): ... this; changed callers. + (es_read_file): Renamed to ... + (file_to_buffer): ... this; changed callers. + (ssh_identity_register): Removed variable description_length; + changed code to use asprintf for description. + (stream_write_uint32): Do not filter out the last byte of shift + expression. + (uint32_construct): New macro ... + (stream_read_uint32): ... use it; removed unnecessary cast. + +2005-02-03 Werner Koch + + * agent.h (agent_exit): Add JNLIB_GCC_A_NR to indicate that this + function won't return. + + * gpg-agent.c (check_for_running_agent): Initialize pid to a + default value if not needed. + + * command-ssh.c: Removed stdint.h. s/byte_t/unsigned char/, + s/uint32/u32/ becuase that is what we have always used in GnuPG. + (ssh_request_specs): Moved to top of file. + (ssh_key_types): Ditto. + (make_cstring): Ditto. + (data_sign): Don't use a variable for the passphrase prompt, make + it translatable. + (ssh_request_process): + + + * findkey.c (modify_description): Renamed arguments for clarity, + polished documentation. Make comment a C-string. Fixed case of + DESCRIPTION being just "%". + (agent_key_from_file): Make sure comment string to a C-string. + + * gpg-agent.c (create_socket_name): Cleanup the implemntation, use + DIMof, agent_exit, removed superflous args and return the + allocated string as value. Documented. Changed callers. + (create_server_socket): Cleanups similar to above. Changed callers. + (cleanup_do): Renamed to .. + (remove_socket): .. this. Changed caller. + (handle_connections): The signals are to be handled in the select + and not in the accept. Test all FDs after returning from a + select. Remove the event tests from the accept calls. The select + already assured that the accept won't block. + +2005-01-29 Moritz Schulte + + * command-ssh.c (ssh_handler_request_identities) + (ssh_handler_sign_request, ssh_handler_add_identity) + (ssh_handler_remove_identity, ssh_handler_remove_all_identities) + (ssh_handler_lock, ssh_handler_unlock): Changed to return an error + code instead of a boolean. + (ssh_request_process): Changed to return a boolean instead of an + error; adjust caller. + (ssh_request_handle_t): Adjusted type. + (ssh_request_spec): New member: identifier. + (REQUEST_SPEC_DEFINE): New macro; use it for initialization of + request_specs[]. + (ssh_request_process): In debugging mode, log identifier of + handler to execute. + (start_command_handler_ssh): Moved most of the stream handling + code ... + (ssh_request_process): ... here. + +2005-01-28 Moritz Schulte + + * command-ssh.c (ssh_handler_add_identity): Pass ctrl to + ssh_identity_register(). + (ssh_identity_register): New argument: ctrl; pass ctrl to + get_passphrase(). + (get_passphrase): Pass ctrl instead of NULL to agent_askpin(). + (start_command_handler_ssh): Use agent_init_default_ctrl(); + deallocate structure members, which might be dynamically + allocated. + (lifetime_default): Removed variable. + (ssh_handler_add_identity): Fix ttl handling; renamed variable + `death' to `ttl'. + (ssh_identity_register): Fix key grip handling. + +2005-01-26 Moritz Schulte + + * command-ssh.c (ssh_handler_sign_request): Confirm to agent + protocol in case of failure. + + * command-ssh.c: New file. + + * Makefile.am (gpg_agent_SOURCES): New source file: command-ssh.c. + + * findkey.c (modify_description): New function. + (agent_key_from_file): Support comment field in key s-expressions. + + * gpg-agent.c (enum cmd_and_opt_values): New item: oSSHSupport. + (opts) New entry for oSSHSupport. + New variable: socket_name_ssh. + (cleanup_do): New function based on cleanup(). + (cleanup): Use cleanup_do() for socket_name and socket_name_ssh. + (main): New switch case for oSSHSupport. + (main): Move socket name creation code to ... + (create_socket_name): ... this new function. + (main): Use create_socket_name() for creating socket names for + socket_name and for socket_name_ssh in case ssh support is + enabled. + Move socket creation code to ... + (create_server_socket): ... this new function. + (main): Use create_server_socket() for creating sockets. + In case standard_socket is set, do not only store a socket name in + socket_name, but also in socket_name_ssh. + Generate additional environment info strings for ssh support. + Pass additional ssh socket argument to handle_connections. + (start_connection_thread_ssh): New function. + (handle_connections): Use select to multiplex between gpg-agent + and ssh-agent protocol. + + * agent.h (struct opt): New member: ssh_support. + (start_command_handler_ssh): Add prototype. + +2005-01-04 Werner Koch + + * trustlist.c (agent_marktrusted): Use "Cancel" for the first + confirmation and made the strings translatable. + + * cache.c (agent_put_cache): Fix the test for using the default + TTL. + +2004-12-21 Werner Koch + + * preset-passphrase.c (preset_passphrase): Handle --passphrase. + + * Makefile.am (gpg_preset_passphrase_LDADD): Reorder libs so that + pwquery may use stuff from jnlib. Conditionally add -lwsock2 + (gpg_protect_tool_LDADD): Ditto. + + * preset-passphrase.c (main): Use default_homedir(). + (main) [W32]: Initialize sockets. + +2004-12-21 Marcus Brinkmann + + * Makefile.am (libexec_PROGRAMS): Add gpg-preset-passphrase. + (gpg_preset_passphrase_SOURCES, gpg_preset_passphrase_LDADD): New + targets. + * agent.h (opt): New member allow_cache_passphrase. + * cache.c (housekeeping): Check if R->ttl is not negative. + (agent_put_cache): Allow ttl to be negative. + * command.c (parse_hexstring): Allow something to follow the + hexstring. + (cmd_cache_passphrase): New function. + (register_commands): Add it. + * gpg-agent.c: Handle --allow-preset-passphrase. + * preset-passphrase.c: New file. + +2004-12-21 Werner Koch + + * gpg-agent.c (main): Use default_homedir(). + * protect-tool.c (main): Ditto. + +2004-12-20 Werner Koch + + * gpg-agent.c (main) [W32]: Now that Mutexes work we can remove + the pth_init kludge. + (main): Add new options --[no-]use-standard-socket. + (check_for_running_agent): Check whether it is running on the + standard socket. + + * call-scd.c (init_membuf, put_membuf, get_membuf): Removed. We + now use the identical implementation from ../common/membuf.c. + + * pksign.c (agent_pksign): Changed arg OUTFP to OUTBUF and use + membuf functions to return the value. + * pkdecrypt.c (agent_pkdecrypt): Ditto. + * genkey.c (agent_genkey): Ditto. + * command.c (cmd_pksign, cmd_pkdecrypt, cmd_genkey): Replaced + assuan_get_data_fp() by a the membuf scheme. + (clear_outbuf, write_and_clear_outbuf): New. + +2004-12-19 Werner Koch + + * query.c (initialize_module_query): New. + * call-scd.c (initialize_module_call_scd): New. + * gpg-agent.c (main): Call them. + +2004-12-18 Werner Koch + + * gpg-agent.c (main): Remove special Pth initialize. + + * agent.h (map_assuan_err): Define in terms of + map_assuan_err_with_source. + +2004-12-17 Moritz Schulte + + * query.c: Undo change from 2004-12-05. + +2004-12-15 Werner Koch + + * gpg-agent.c [W32]: Various hacks to make it work. + + * findkey.c (agent_write_private_key) [W32]: Adjust open call. + + * call-scd.c (start_scd) [W32]: Don't check whether the daemon + didn't died. To hard to do under Windows. + (start_scd) [W32]: Disable sending of the event signal option. + + * protect-tool.c (read_file, export_p12_file) [W32]: Use setmode + to get stdout and stin into binary mode. + +2004-12-05 Moritz Schulte + + * query.c (start_pinentry): Allow CTRL be NULL. + +2004-10-22 Werner Koch + + * gpg-agent.c (parse_rereadable_options): Return "not handled" + when the log file has not beend hadled. This is will let the main + option processing continue. Fixed a bug introduced on 2004-09-4 + resulting in logging to stderr until a HUP has been given. + (main): Don't close the listen FD. + +2004-09-30 Werner Koch + + * Makefile.am: Adjusted from gettext 1.14. + +2004-09-29 Werner Koch + + * minip12.c (parse_bag_encrypted_data): Print error if a bad + passphrase has been given. + +2004-09-28 Werner Koch + + * protect.c (agent_unprotect): Fixed wiping of CLEARTEXT. Thanks + to Moritz for pointing this out. + +2004-09-25 Moritz Schulte + + * agent.h: Declare: agent_pksign_do. + (struct server_control_s): New member: raw_value. + + * pksign.c (do_encode_md): New argument: raw_value; support + generation of raw (non-pkcs1) data objects; adjust callers. + (agent_pksign_do): New function, based on code ripped + out from agent_pksign. + (agent_pksign): Use agent_pksign_do. + + * command.c (start_command_handler): Set ctrl.digest.raw_value. + +2004-09-09 Werner Koch + + * gpg-agent.c (check_for_running_agent): New. + (main): The default action is now to check for an already running + agent. + (parse_rereadable_options): Set logfile only on reread. + (main): Do not print the "is development version" note. + +2004-08-20 Werner Koch + + * gpg-agent.c: New option --max-cache-ttl. Suggested by Alexander + Belopolsky. + * cache.c (housekeeping): Use it here instead of the hardwired + default of 1 hour. + + * query.c (start_pinentry): Use a timeout for the pinentry lock. + +2004-08-18 Werner Koch + + * protect-tool.c (get_passphrase): Make sure that the default + prompts passed to gpg-agent are utf-8 encoded. Add new prompt values. + (import_p12_file, import_p12_file, export_p12_file): Changed calls + to get_passphrase so that better prompts are displayed. + (get_new_passphrase): New. + +2004-07-22 Werner Koch + + * trustlist.c (read_list): Allow colons in the fingerprint. + (headerblurb): Rephrased. + + * gpg-agent.c (handle_connections): Increase the stack size ot 256k. + +2004-06-20 Moritz Schulte + + * gpg-agent.c: Include (build fix for BSD). + +2004-05-11 Werner Koch + + * gpg-agent.c (handle_signal): Reload the trustlist on SIGHUP. + (start_connection_thread): Hack to simulate a ticker. + * trustlist.c (agent_trustlist_housekeeping) + (agent_reload_trustlist): New. Protected all global functions + here with a simple counter which is sufficient for Pth. + +2004-05-03 Werner Koch + + * gpg-agent.c: Remove help texts for options lile --lc-ctype. + (main): New option --allow-mark-trusted. + * trustlist.c (agent_marktrusted): Use it here. + +2004-04-30 Werner Koch + + * protect-tool.c: New option --enable-status-msg. + (store_private_key): Print status messages for imported keys. + (read_and_unprotect): Ditto for bad passphrase. + + * gpg-agent.c (parse_rereadable_options): New arg REREAD. Allow + changing oLogFile. + (current_logfile): New. + +2004-04-26 Werner Koch + + * call-scd.c (start_scd): Do not register an event signal if we + are running as a pipe server. + +2004-04-21 Werner Koch + + * call-scd.c (start_scd): Send event-signal option. Always check + that the scdaemon is still running. + + * gpg-agent.c (handle_signal): Do not use SIGUSR{1,2} anymore for + changing the verbosity. + +2004-04-16 Werner Koch + + * gpg-agent.c (main): Tell the logging code that we are running + detached. + +2004-04-06 Werner Koch + + * gpg-agent.c (main): Use new libgcrypt thread library register + scheme. + +2004-03-23 Marcus Brinkmann + + * gpg-agent.c (main): For now, always print the default config + file name for --gpgconf-list. + +2004-03-17 Werner Koch + + * gpg-agent.c (main) : Fixed default value quoting. + +2004-03-16 Werner Koch + + * gpg-agent.c (parse_rereadable_options): Use the new + DEFAULT_CACHE_TTL macro. + (main): Updated --gpgconf-list output. + +2004-02-21 Werner Koch + + * command.c (cmd_passwd): Take acount of a key description. + + * genkey.c (reenter_compare_cb): Do not set the error text. + (agent_protect_and_store, agent_genkey): Force a re-enter after a + non-matching passphrase. + * query.c (agent_askpin): Add new arg INITIAL_ERRTEXT; changed + all callers. + +2004-02-19 Werner Koch + + * protect-tool.c: New options --have-cert and --prompt. + (export_p12_file): Read a certificate from STDIN and pass it to + p12_build. Detect a keygrip and construct the filename in that + case. Unprotcet a key if needed. Print error messages for key + formats we can't handle. + (release_passphrase): New. + (get_passphrase): New arg PROMPTNO. Return the allocated + string. Changed all callers. + + * minip12.c: Revamped the build part. + (p12_build): New args CERT and CERTLEN. + +2004-02-18 Werner Koch + + * protect-tool.c (main): Setup the used character set. + * gpg-agent.c (main): Ditto. + + * gpg-agent.c (set_debug): New. New option --debug-level. + (main): New option --gpgconf-list. + +2004-02-17 Werner Koch + + * pksign.c (do_encode_md): Cleaned up by using gcry_sexp_build. + + * Makefile.am (gpg_protect_tool_SOURCES): Removed + simple-pwquery.[ch], as we once moved it to ../common. + +2004-02-13 Werner Koch + + * command.c (cmd_setkeydesc): New. + (register_commands): Add command SETKEYDESC. + (cmd_pksign, cmd_pkdecrypt): Use the key description. + (reset_notify): Reset the description. + * findkey.c (unprotect): Add arg DESC_TEXT. + (agent_key_from_file): Ditto. + * pksign.c (agent_pksign): Ditto. + * pkdecrypt.c (agent_pkdecrypt): Ditto. Made CIPHERTEXT an + unsigned char*. + + * protect-tool.c (main): New options --no-fail-on-exist, --homedir. + (store_private_key): Use them here. + +2004-02-12 Werner Koch + + * protect-tool.c (read_file, main): Allow reading from stdin. + + * Makefile.am: Include cmacros.am for common flags. + (libexec_PROGRAMS): Put gpg-protect-tool there. + +2004-02-10 Werner Koch + + * minip12.c (parse_bag_encrypted_data): Finished implementation. + (p12_parse): Add callback args. + * protect-tool.c (import_p12_cert_cb): New. + (import_p12_file): Use it. + +2004-02-06 Werner Koch + + * minip12.c (crypt_block): Add arg CIPHER_ALGO; changed all callers. + (set_key_iv): Add arg KEYBYTES; changed caller. + +2004-02-03 Werner Koch + + * findkey.c (agent_key_from_file): Extra paranoid wipe. + * protect.c (agent_unprotect): Ditto. + (merge_lists): Ditto. Add arg RESULTLEN. + * pkdecrypt.c (agent_pkdecrypt): Don't show the secret key even in + debug mode. + + * protect.c: Add DSA and Elgamal description. + +2004-01-29 Werner Koch + + * agent.h (server_control_s): Add connection_fd field. + * command.c (start_command_handler): Init it here. + * gpg-agent.c (agent_init_default_ctrl): and here. + * call-scd.c: Add the CTRL arg to all functions calling start_scd + and pass it to start_scd. Changed all callers + (start_scd): Keep track of the current active connection. + (agent_reset_scd): New. + * command.c (start_command_handler): Call it here. + * learncard.c (agent_handle_learn): Add arg CTRL; changed caller. + (send_cert_back): Ditto. + +2004-01-28 Werner Koch + + * trustlist.c (agent_marktrusted): Check whether the trustlist is + writable. + +2004-01-27 Werner Koch + + * sexp-parse.h: Moved to ../common. + +2004-01-24 Werner Koch + + * call-scd.c (atfork_cb): New. + (start_scd): Make sure secmem gets cleared. + * query.c (atfork_cb): New. + (start_pinentry): Make sure secmem gets cleared. + +2004-01-16 Werner Koch + + * findkey.c (agent_key_from_file): Now return an error code so + that we have more detailed error messages in the upper layers. + This fixes the handling of pinentry's cancel button. + * pksign.c (agent_pksign): Changed accordingly. + * pkdecrypt.c (agent_pkdecrypt): Ditto. + * command.c (cmd_passwd): Ditto. + +2003-12-16 Werner Koch + + * gpg-agent.c (main): Set the prefixes for assuan logging. + +2003-12-15 Werner Koch + + * protect.c (do_encryption): Use gcry_create_nonce instad of the + obsolete WEAK_RANDOM. + +2003-11-20 Werner Koch + + * sexp-parse.h (snext): Don't use atoi_1 and digitp macros, so + that this file is useful by other applications too. + +2003-10-27 Werner Koch + + * command.c (cmd_get_confirmation): New command. + +2003-08-20 Timo Schulz + + * pksign.c (do_encode_md): Allocate enough space. Cast md + byte to unsigned char to prevent sign extension. + +2003-08-14 Timo Schulz + + * pksign.c (do_encode_md): Due to the fact pkcs#1 padding + is now in Libgcrypt, use the new interface. + +2003-07-31 Werner Koch + + * Makefile.am (gpg_agent_LDADD): Added INTLLIBS. + (gpg_protect_tool_SOURCES): Added simple-pwquery.[ch] + +2003-07-27 Werner Koch + + Adjusted for gcry_mpi_print and gcry_mpi_scan API change. + +2003-07-15 Werner Koch + + * simple-pwquery.c, simple-pwquery.h: Moved to ../common. + * Makefile.am (gpg_protect_tool_LDADD): Add simple-pwquery.o. + Removed it from xx_SOURCES. + +2003-07-04 Werner Koch + + * gpg-agent.c (handle_connections): Kludge to allow use of Pth 1 + and 2. + +2003-06-30 Werner Koch + + * call-scd.c (learn_status_cb): Store the serialno in PARM. + +2003-06-26 Werner Koch + + * call-scd.c (agent_card_serialno): Don't do a RESET anymore. + +2003-06-25 Werner Koch + + * command.c (cmd_scd): New. + * call-scd.c (agent_card_scd): New. + * divert-scd.c (divert_generic_cmd): New + + * call-scd.c (agent_card_learn): New callback args SINFO. + (learn_status_cb): Pass all other status lines to the sinfo + callback. + * learncard.c (release_sinfo, sinfo_cb): New. + (agent_handle_learn): Pass the new cb to the learn function and + pass the collected information back to the client's assuan + connection. + + * gpg-agent.c (main): Moved pth_init before gcry_check_version. + +2003-06-24 Werner Koch + + * gpg-agent.c (handle_connections): Adjusted for Pth 2.0 + + Adjusted for changes in the libgcrypt API. Some more fixes for the + libgpg-error stuff. + +2003-06-04 Werner Koch + + Renamed error codes from INVALID to INV and removed _ERROR suffixes. + +2003-06-03 Werner Koch + + Changed all error codes in all files to the new libgpg-error scheme. + + * agent.h: Include gpg-error.h and errno.h + * Makefile.am: Link with libgpg-error + + * query.c: assuan.h is now a system header. + * genkey.c (agent_genkey): Fixed silly use of xmalloc by + xtrymalloc. + +2003-04-29 Werner Koch + + * command.c (register_commands): Adjusted for new Assuan semantics. + + * Makefile.am: Don't override LDFLAGS. + +2002-12-04 Werner Koch + + * gpg-agent.c: New variable config_filename. + (parse_rereadable_options): New. + (main): Use it here. Add setting of default values, set + config_filename. + (reread_configuration): Filled with actual code. + +2002-12-03 Werner Koch + + * protect-tool.c (read_key): Don't run make_canonical on a NULL + buffer. + + * command.c (parse_hexstring): New. + (cmd_sethash): Use it. + (parse_keygrip): New. + (cmd_havekey, cmd_sigkey): Use it. + (cmd_passwd): New. + * genkey.c (agent_protect_and_store): New. + (store_key): Add arg FORCE. + (agent_genkey): Pass false to this force of store_key. + +2002-11-13 Werner Koch + + * gpg-agent.c (main): Switch all messages to utf-8. + + * simple-pwquery.c (agent_send_all_options): Use $GPG_TTY and + stdin with ttyname. + + * cache.c (new_data): Uiih - /sizeof d/sizeof *d/. + +2002-11-10 Werner Koch + + * command.c (option_handler): Fix keep_tty check. + +2002-11-06 Werner Koch + + * gpg-agent.c (main): Make sure we have a default ttyname. + * command.c (option_handler): Check opt.keep_tty here + * query.c (start_pinentry): but not anymore here. + +2002-11-05 Werner Koch + + * agent.h (opt,server_control_s): Move display and lc_ variables + to the control struct so that they are per connection. + * gpg-agent.c (agent_init_default_ctrl): New. + (main): Assign those command line options to new default_* variables. + Reset DISPLAY in server mode so that there is no implicit default. + * command.c (start_command_handler): Initialize and deinitialize + the control values. + (option_handler): Work on the ctrl values and not on the opt. + * query.c (start_pinentry): New argument CTRL to set the display + connection specific. Changed all callers to pass this value. + (agent_askpin,agent_get_passphrase,agent_get_confirmation): Add + CTRL arg and pass it ot start_pinentry. + * command.c (cmd_get_passphrase): Pass CTRL argument. + * trustlist.c (agent_marktrusted): Add CTRL argument + * command.c (cmd_marktrusted): Pass CTRL argument + * divert-scd.c (ask_for_card): Add CTRL arg. + (divert_pksign,divert_pkdecrypt): Ditto. Changed caller. + (getpin_cb): Use OPAQUE to pass the CTRL variable. Changed both + users. + * findkey.c (unprotect): Add CTRL arg. + (agent_key_from_file): Ditto. + + * query.c (unlock_pinentry): Disconnect the pinentry so that we + start a new one for each request. This is required to support + clients with different environments (e.g. X magic cookies). + +2002-09-05 Neal H. Walfield + + * gpg-agent.c (main) [USE_GNU_PTH]: No need to call + assuan_set_io_func as assuan is smart. + +2002-09-25 Werner Koch + + * gpg-agent.c (handle_signal): Flush cache on SIGHUP. + * cache.c (agent_flush_cache): New. + + * gpg-agent.c, agent.h: Add --keep-display and --keep-tty. + * query.c (start_pinentry): Implement them. The option passing + needs more thoughts. + +2002-09-09 Werner Koch + + * gpg-agent.c (create_private_keys_directory) + (create_directories): New. + (main): Try to create a home directory. + +2002-09-04 Neal H. Walfield + + * gpg-agent.c (main): Use sigaction, not signal. + +2002-09-03 Neal H. Walfield + + * findkey.c: Include . + (agent_write_private_key): Prefer POSIX compatibity, open and + fdopen, over the simplicity of GNU extensions, fopen(file, "x"). + +2002-08-22 Werner Koch + + * query.c (agent_askpin): Provide the default desc text depending + on the pininfo. Do the basic PIN verification only when + min_digits is set. + +2002-08-21 Werner Koch + + * query.c (agent_askpin): Hack to show the right default prompt. + (agent_get_passphrase): Ditto. + + * trans.c: Removed and replaced all usages with standard _() + + * divert-scd.c (getpin_cb): Pass a more descritive text to the + pinentry. + + * Makefile.am: Renamed the binary protect-tool to gpg-protect-tool. + * protect-tool.c: Removed the note about internal use only. + + * gpg-agent.c (main): New option --daemon so that the program is + not accidently started in the background. + +2002-08-16 Werner Koch + + * call-scd.c (learn_status_cb): Handle CERTINFO status. + (agent_card_learn): Add args for certinfo cb. + * learncard.c (release_certinfo,certinfo_cb): New. + (send_cert_back): New. With factored out code from .. + (agent_handle_learn): here. Return certinfo stuff. + +2002-07-26 Werner Koch + + * gpg-agent.c (main): New option --ignore-cache-for-signing. + * command.c (option_handler): New server option + use-cache-for-signing defaulting to true. + (cmd_pksign): handle global and per session option. + * findkey.c (agent_key_from_file, unprotect): New arg + ignore_cache. Changed all callers. + * pksign.c (agent_pksign): Likewise. + +2002-06-29 Werner Koch + + * query.c (start_pinentry): Use GNUPG_DERAULT_PINENTRY. + * call-scd.c (start_scd): Use GNUPG_DEFAULT_SCDAEMON. + +2002-06-28 Werner Koch + + * protect-tool.c (export_p12_file): New. + (main): New command --p12-export. + * minip12.c (create_final,p12_build,compute_tag_length): New. + (store_tag_length): New. + +2002-06-27 Werner Koch + + * minip12.c (crypt_block): Renamed from decrypt_block, add arg to + allow encryption. + + * Makefile.am (pkglib_PROGRAMS): Put protect-tool there. + + * findkey.c (agent_write_private_key,agent_key_from_file) + (agent_key_available): Use GNUPG_PRIVATE_KEYS_DIR constant. + * gpg-agent.c (main): Use GNUPG_DEFAULT_HOMEDIR constant. + + * protect-tool.c (store_private_key): New. + (import_p12_file): Store the new file if requested. + (main): New options --force and --store. + + * gpg-agent.c (main): Set a global flag when running detached. + * query.c (start_pinentry): Pass the list of FD to keep in the + child when not running detached. + * call-scd.c (start_scd): Ditto. + +2002-06-26 Werner Koch + + * command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted) + (cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_get_passphrase) + (cmd_learn): Print an error message for a failed operation. + + * simple-pwquery.c, simple-pwquery.h: New. + * protect-tool. (get_passphrase): New, used to get a passphrase + from the agent if none was given on the command line. + +2002-06-25 Werner Koch + + * protect-tool.c (rsa_key_check): New. + (import_p12_file): New. + (main): New command --p12-import. + * minip12.c, minip12.h: New. + +2002-06-24 Werner Koch + + * protect-tool.c (read_file): New. + (read_key): Factored most code out to read_file. + +2002-06-17 Werner Koch + + * agent.h: Add a callback function to the pin_entry_info structure. + * query.c (agent_askpin): Use the callback to check for a correct + PIN. Removed the start_err_text argument because it is not + anymore needed; changed callers. + * findkey.c (unprotect): Replace our own check loop by a callback. + (try_unprotect_cb): New. + * genkey.c (reenter_compare_cb): New. + (agent_genkey): Use this callback here. Fixed setting of the pi2 + variable and a segv in case of an empty PIN. + + * divert-scd.c (getpin_cb): Removed some unused stuff and + explained what we still have to change. + +2002-06-12 Werner Koch + + * gpg-agent.c (main): New option --disable-pth. + +2002-06-11 Werner Koch + + * protect-tool.c: Add command --show-keygrip + (show_keygrip): New. + +2002-05-23 Werner Koch + + * call-scd.c: Seirialized all scdaeom access when using Pth. + + * cache.c: Made the cache Pth-thread-safe. + (agent_unlock_cache_entry): New. + * findkey.c (unprotect): Unlock the returned cache value. + * command.c (cmd_get_passphrase): Ditto. + + * gpg-agent.c (main): Register pth_read/write with Assuan. + +2002-05-22 Werner Koch + + * query.c: Serialized all pinentry access when using Pth. + + * gpg-agent.c (handle_signal,start_connection_thread) + (handle_connections): New + (main): Use the new Pth stuff to allow concurrent connections. + * command.c (start_command_handler): Add new arg FD so that the + fucntion can also be used for an already connected socket. + * Makefile.am: Link with Pth. + +2002-05-14 Werner Koch + + * cache.c (housekeeping, agent_put_cache): Use our time() wrapper. + +2002-04-26 Werner Koch + + * cache.c (agent_put_cache): Reinitialize the creation time and + the ttl when reusing a slot. + + * call-scd.c (start_scd): Print debug messages only with debug + flags set. + * query.c (start_pinentry): Ditto. + +2002-04-25 Marcus Brinkmann + + * agent.h (agent_get_confirmation): Replace paramter prompt with + two parameters ok and cancel. + * query.c (agent_get_confirmation): Likewise. Implement this. + * trustlist.c (agent_marktrusted): Fix invocation of + agent_get_confirmation. + * divert-scd.c (ask_for_card): Likewise. + +2002-04-24 Marcus Brinkmann + + * agent.h (struct opt): Add members display, ttyname, ttytype, + lc_ctype, and lc_messages. + * gpg-agent.c (enum cmd_and_opt_values): Add oDisplay, oTTYname, + oTTYtype, oLCctype, and LCmessages. + (main): Handle these options. + * command.c (option_handler): New function. + (register_commands): Register option handler. + * query.c (start_pinentry): Pass the various display and tty + options to the pinentry. + +2002-04-05 Werner Koch + + * protect-tool.c (show_file): New. Used as default action. + +2002-03-28 Werner Koch + + * divert-scd.c (encode_md_for_card): Don't do the pkcs-1 padding, + the scdaemon should take care of it. + (ask_for_card): Hack to not display the trailing zero. + +2002-03-11 Werner Koch + + * learncard.c (kpinfo_cb): Remove the content restrictions from + the keyID. + +2002-03-06 Werner Koch + + * learncard.c: New. + * divert-scd.c (ask_for_card): The serial number is binary so + convert it to hex here. + * findkey.c (agent_write_private_key): New. + * genkey.c (store_key): And use it here. + + * pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done. + * divert-scd.c (divert_pkdecrypt): Changed interface and + implemented it. + +2002-03-05 Werner Koch + + * call-scd.c (inq_needpin): New. + (agent_card_pksign): Add getpin_cb args. + (agent_card_pkdecrypt): New. + +2002-03-04 Werner Koch + + * pksign.c (agent_pksign): Changed how the diversion is done. + * divert-scd.c (divert_pksign): Changed interface and implemented it. + (encode_md_for_card): New. + * call-scd.c (agent_card_pksign): New. + +2002-02-28 Werner Koch + + * pksign.c (agent_pksign): Detect whether a Smartcard is to be + used and divert the operation in this case. + * pkdecrypt.c (agent_pkdecrypt): Likewise + * findkey.c (agent_key_from_file): Add optional arg shadow_info + and have it return information about a shadowed key. + * protect.c (agent_get_shadow_info): New. + + * protect.c (snext,sskip,smatch): Moved to + * sexp-parse.h: New file. + * divert-scd.c: New. + +2002-02-27 Werner Koch + + * protect.c (agent_shadow_key): New. + + * command.c (cmd_learn): New command LEARN. + * gpg-agent.c: New option --scdaemon-program. + * call-scd.c (start_scd): New. Based on query.c + * query.c: Add 2 more arguments to all uses of assuan_transact. + +2002-02-18 Werner Koch + + * findkey.c (unprotect): Show an error message for a bad passphrase. + + * command.c (cmd_marktrusted): Implemented. + * trustlist.c (agent_marktrusted): New. + (open_list): Add APPEND arg. + + * query.c (agent_get_confirmation): New. + +2002-02-06 Werner Koch + + * cache.c (housekeeping): Fixed linking in the remove case. + +2002-02-01 Werner Koch + + * gpg-agent.c: New option --default-cache-ttl. + * cache.c (agent_put_cache): Use it. + + * cache.c: Add a few debug outputs. + + * protect.c (agent_private_key_type): New. + * agent.h: Add PRIVATE_KEY_ enums. + * findkey.c (agent_key_from_file): Use it to decide whether we + have to unprotect a key. + (unprotect): Cache the passphrase. + + * findkey.c (agent_key_from_file,agent_key_available): The key + files do now require a ".key" suffix to make a script's life + easier. + * genkey.c (store_key): Ditto. + +2002-01-31 Werner Koch + + * genkey.c (store_key): Protect the key. + (agent_genkey): Ask for the passphrase. + * findkey.c (unprotect): Actually unprotect the key. + * query.c (agent_askpin): Add an optional start_err_text. + +2002-01-30 Werner Koch + + * protect.c: New. + (hash_passphrase): Based on the GnuPG 1.0.6 version. + * protect-tool.c: New + +2002-01-29 Werner Koch + + * findkey.c (agent_key_available): New. + * command.c (cmd_havekey): New. + (register_commands): And register new command. + +2002-01-20 Werner Koch + + * command.c (cmd_get_passphrase): Remove the plus signs. + + * query.c (start_pinentry): Send no-grab option to pinentry + * gpg-agent.c (main): Move variable grab as no_grab to agent.h. + +2002-01-19 Werner Koch + + * gpg-agent.c (main): Disable core dumps. + + * cache.c: New. + * command.c (cmd_get_passphrase): Use the cache. + (cmd_clear_passphrase): Ditto. + + * gpg-agent.c: Removed unused cruft and implement the socket + based server. + (my_strusage): Take bug report address from configure.ac. + * command.c (start_command_handler): Add an argument to start as + regular server. + (start_command_handler): Enable Assuan logging. + +2002-01-15 Werner Koch + + * trustlist.c: New. + * command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted): New. + +2002-01-07 Werner Koch + + * genkey.c: Store the secret part and return the public part. + +2002-01-03 Werner Koch + + * command.c (cmd_get_passphrase): New. + (cmd_clear_passphrase): New. + * query.c (agent_get_passphrase): New. + +2002-01-02 Werner Koch + + * genkey.c: New. + * command.c (cmd_genkey): New. + + * command.c (rc_to_assuan_status): Removed and changed all callers + to use map_to_assuan_status. + +2001-12-19 Werner Koch + + * keyformat.txt: New. + +2001-12-19 Marcus Brinkmann + + * query.c (start_pinentry): Add new argument to assuan_pipe_connect. + +2001-12-18 Werner Koch + + * Makefile.am: Use LIBGCRYPT macros + +2001-12-14 Werner Koch + + * gpg-agent.c (main): New option --batch. New option --debug-wait + n, so that it is possible to attach gdb when used in server mode. + * query.c (agent_askpin): Don't ask in batch mode. + + * command.c: Removed the conversion macros as they are now in + ../common/util.h. + +2001-12-14 Marcus Brinkmann + + * query.c (LINELENGTH): Removed. + (agent_askpin): Use ASSUAN_LINELENGTH, not LINELENGTH. + +2001-11-19 Werner Koch + + * gpg-agent.c: Removed all GUI code, removed code for old + protocol. New code to use the Assuan protocol as a server and + also to communicate with a new ask-passphrase utility. + +2000-11-22 Werner Koch + + * gpg-agent.c (main): csh support by Dan Winship, new options --sh + and --csh and set default by consulting $SHELL. + +Mon Aug 21 17:59:17 CEST 2000 Werner Koch + + * gpg-agent.c (passphrase_dialog): Cleanup the window and added the + user supplied text to the window. + (main): Fixed segv in gtk_init when used without a command to start. + + * gpg-agent.c: --flush option. + (req_flush): New. + (req_clear_passphrase): Implemented. + +Fri Aug 18 14:27:14 CEST 2000 Werner Koch + + * gpg-agent.c: New. + * Makefile.am: New. + + + Copyright 2001, 2002, 2003, 2004, 2005, + 2007, 2008, 2009, 2010, 2011 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/agent/Makefile.am b/agent/Makefile.am new file mode 100644 index 0000000..045566e --- /dev/null +++ b/agent/Makefile.am @@ -0,0 +1,109 @@ +# Copyright (C) 2001, 2003, 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 3 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, see . + +## Process this file with automake to produce Makefile.in + +bin_PROGRAMS = gpg-agent +libexec_PROGRAMS = gpg-protect-tool +if !HAVE_W32CE_SYSTEM +# fixme: Do no use simple-pwquery for preset-passphrase. +libexec_PROGRAMS += gpg-preset-passphrase +endif +noinst_PROGRAMS = $(TESTS) + +EXTRA_DIST = ChangeLog-2011 gpg-agent-w32info.rc + + +AM_CPPFLAGS = -I$(top_srcdir)/common + +include $(top_srcdir)/am/cmacros.am + +if HAVE_W32_SYSTEM +resource_objs += gpg-agent-w32info.o +endif + +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) + +gpg_agent_SOURCES = \ + gpg-agent.c agent.h \ + command.c command-ssh.c \ + call-pinentry.c \ + cache.c \ + trans.c \ + findkey.c \ + pksign.c \ + pkdecrypt.c \ + genkey.c \ + protect.c \ + trustlist.c \ + divert-scd.c \ + cvt-openpgp.c cvt-openpgp.h \ + call-scd.c \ + learncard.c + +common_libs = $(libcommon) +commonpth_libs = $(libcommonpth) +if HAVE_W32CE_SYSTEM +pwquery_libs = +else +pwquery_libs = ../common/libsimple-pwquery.a +endif + + +gpg_agent_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \ + $(INCICONV) +gpg_agent_LDADD = $(commonpth_libs) \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ + $(resource_objs) +gpg_agent_LDFLAGS = $(extra_bin_ldflags) +gpg_agent_DEPENDENCIES = $(resource_objs) + +gpg_protect_tool_SOURCES = \ + protect-tool.c \ + protect.c cvt-openpgp.c + +gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) \ + $(INCICONV) +gpg_protect_tool_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) + +gpg_preset_passphrase_SOURCES = \ + preset-passphrase.c + +# Needs $(NETLIBS) for libsimple-pwquery.la. +gpg_preset_passphrase_LDADD = \ + $(pwquery_libs) $(common_libs) $(LIBASSUAN_LIBS) \ + $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) + + +# Make sure that all libs are build before we use them. This is +# important for things like make -j2. +$(PROGRAMS): $(common_libs) $(commonpth_libs) $(pwquery_libs) + + + +# +# Module tests +# +TESTS = t-protect + +t_common_ldadd = $(common_libs) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) $(NETLIBS) + +t_protect_SOURCES = t-protect.c protect.c +t_protect_LDADD = $(t_common_ldadd) diff --git a/agent/Makefile.in b/agent/Makefile.in new file mode 100644 index 0000000..3e255f6 --- /dev/null +++ b/agent/Makefile.in @@ -0,0 +1,1310 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# 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. + +@SET_MAKE@ + +# Copyright (C) 2001, 2003, 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 3 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, see . + +# cmacros.am - C macro definitions +# 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 3 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, see . + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = gpg-agent$(EXEEXT) +libexec_PROGRAMS = gpg-protect-tool$(EXEEXT) $(am__EXEEXT_1) +# fixme: Do no use simple-pwquery for preset-passphrase. +@HAVE_W32CE_SYSTEM_FALSE@am__append_1 = gpg-preset-passphrase +noinst_PROGRAMS = $(am__EXEEXT_2) +DIST_COMMON = $(top_srcdir)/am/cmacros.am $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/build-aux/mkinstalldirs \ + $(top_srcdir)/build-aux/depcomp +@HAVE_DOSISH_SYSTEM_FALSE@am__append_2 = -DGNUPG_BINDIR="\"$(bindir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\"" + + +# If a specific protect tool program has been defined, pass its name +# to cc. Note that these macros should not be used directly but via +# the gnupg_module_name function. +@GNUPG_AGENT_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\"" +@GNUPG_PINENTRY_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\"" +@GNUPG_SCDAEMON_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\"" +@GNUPG_DIRMNGR_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\"" +@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\"" +@GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_8 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\"" +@HAVE_W32_SYSTEM_TRUE@am__append_9 = gpg-agent-w32info.o +TESTS = t-protect$(EXEEXT) +subdir = agent +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ + $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" +@HAVE_W32CE_SYSTEM_FALSE@am__EXEEXT_1 = \ +@HAVE_W32CE_SYSTEM_FALSE@ gpg-preset-passphrase$(EXEEXT) +am__EXEEXT_2 = t-protect$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(noinst_PROGRAMS) +am_gpg_agent_OBJECTS = gpg_agent-gpg-agent.$(OBJEXT) \ + gpg_agent-command.$(OBJEXT) gpg_agent-command-ssh.$(OBJEXT) \ + gpg_agent-call-pinentry.$(OBJEXT) gpg_agent-cache.$(OBJEXT) \ + gpg_agent-trans.$(OBJEXT) gpg_agent-findkey.$(OBJEXT) \ + gpg_agent-pksign.$(OBJEXT) gpg_agent-pkdecrypt.$(OBJEXT) \ + gpg_agent-genkey.$(OBJEXT) gpg_agent-protect.$(OBJEXT) \ + gpg_agent-trustlist.$(OBJEXT) gpg_agent-divert-scd.$(OBJEXT) \ + gpg_agent-cvt-openpgp.$(OBJEXT) gpg_agent-call-scd.$(OBJEXT) \ + gpg_agent-learncard.$(OBJEXT) +gpg_agent_OBJECTS = $(am_gpg_agent_OBJECTS) +am__DEPENDENCIES_1 = +gpg_agent_LINK = $(CCLD) $(gpg_agent_CFLAGS) $(CFLAGS) \ + $(gpg_agent_LDFLAGS) $(LDFLAGS) -o $@ +am_gpg_preset_passphrase_OBJECTS = preset-passphrase.$(OBJEXT) +gpg_preset_passphrase_OBJECTS = $(am_gpg_preset_passphrase_OBJECTS) +@HAVE_W32CE_SYSTEM_FALSE@am__DEPENDENCIES_2 = \ +@HAVE_W32CE_SYSTEM_FALSE@ ../common/libsimple-pwquery.a +gpg_preset_passphrase_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(common_libs) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_gpg_protect_tool_OBJECTS = gpg_protect_tool-protect-tool.$(OBJEXT) \ + gpg_protect_tool-protect.$(OBJEXT) \ + gpg_protect_tool-cvt-openpgp.$(OBJEXT) +gpg_protect_tool_OBJECTS = $(am_gpg_protect_tool_OBJECTS) +gpg_protect_tool_DEPENDENCIES = $(common_libs) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +gpg_protect_tool_LINK = $(CCLD) $(gpg_protect_tool_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am_t_protect_OBJECTS = t-protect.$(OBJEXT) protect.$(OBJEXT) +t_protect_OBJECTS = $(am_t_protect_OBJECTS) +am__DEPENDENCIES_3 = $(common_libs) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +t_protect_DEPENDENCIES = $(am__DEPENDENCIES_3) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(gpg_agent_SOURCES) $(gpg_preset_passphrase_SOURCES) \ + $(gpg_protect_tool_SOURCES) $(t_protect_SOURCES) +DIST_SOURCES = $(gpg_agent_SOURCES) $(gpg_preset_passphrase_SOURCES) \ + $(gpg_protect_tool_SOURCES) $(t_protect_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_FILEVERSION = @BUILD_FILEVERSION@ +BUILD_HOSTNAME = @BUILD_HOSTNAME@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BUILD_REVISION = @BUILD_REVISION@ +BUILD_TIMESTAMP = @BUILD_TIMESTAMP@ +BUILD_VERSION = @BUILD_VERSION@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DL_LIBS = @DL_LIBS@ +DNSLIBS = @DNSLIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENCFS = @ENCFS@ +EXEEXT = @EXEEXT@ +FUSERMOUNT = @FUSERMOUNT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@ +GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@ +GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@ +GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ +GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ +GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ +GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ +GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ +GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ +GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@ +GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KSBA_CFLAGS = @KSBA_CFLAGS@ +KSBA_CONFIG = @KSBA_CONFIG@ +KSBA_LIBS = @KSBA_LIBS@ +LBER_LIBS = @LBER_LIBS@ +LDAPLIBS = @LDAPLIBS@ +LDAP_CPPFLAGS = @LDAP_CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@ +LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@ +LIBASSUAN_LIBS = @LIBASSUAN_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ +LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBREADLINE = @LIBREADLINE@ +LIBS = @LIBS@ +LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBUTIL_LIBS = @LIBUTIL_LIBS@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NETLIBS = @NETLIBS@ +NPTH_CFLAGS = @NPTH_CFLAGS@ +NPTH_CONFIG = @NPTH_CONFIG@ +NPTH_LIBS = @NPTH_LIBS@ +NTBTLS_CFLAGS = @NTBTLS_CFLAGS@ +NTBTLS_CONFIG = @NTBTLS_CONFIG@ +NTBTLS_LIBS = @NTBTLS_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_GT = @PACKAGE_GT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHRED = @SHRED@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ +STRIP = @STRIP@ +SYSROOT = @SYSROOT@ +SYS_SOCKET_H = @SYS_SOCKET_H@ +TAR = @TAR@ +USE_C99_CFLAGS = @USE_C99_CFLAGS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +W32SOCKLIBS = @W32SOCKLIBS@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZLIBS = @ZLIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = $(datadir)/locale +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = ChangeLog-2011 gpg-agent-w32info.rc + +# NB: AM_CFLAGS may also be used by tools running on the build +# platform to create source files. +AM_CPPFLAGS = -I$(top_srcdir)/common -DLOCALEDIR=\"$(localedir)\" \ + $(am__append_2) $(am__append_3) $(am__append_4) \ + $(am__append_5) $(am__append_6) $(am__append_7) \ + $(am__append_8) +@HAVE_W32CE_SYSTEM_FALSE@extra_sys_libs = + +# Under Windows we use LockFileEx. WindowsCE provides this only on +# the WindowsMobile 6 platform and thus we need to use the coredll6 +# import library. We also want to use a stacksize of 256k instead of +# the 2MB which is the default with cegcc. 256k is the largest stack +# we use with pth. +@HAVE_W32CE_SYSTEM_TRUE@extra_sys_libs = -lcoredll6 +@HAVE_W32CE_SYSTEM_FALSE@extra_bin_ldflags = +@HAVE_W32CE_SYSTEM_TRUE@extra_bin_ldflags = -Wl,--stack=0x40000 +resource_objs = $(am__append_9) + +# Convenience macros +libcommon = ../common/libcommon.a +libcommonpth = ../common/libcommonpth.a +libcommontls = ../common/libcommontls.a +libcommontlsnpth = ../common/libcommontlsnpth.a +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) +gpg_agent_SOURCES = \ + gpg-agent.c agent.h \ + command.c command-ssh.c \ + call-pinentry.c \ + cache.c \ + trans.c \ + findkey.c \ + pksign.c \ + pkdecrypt.c \ + genkey.c \ + protect.c \ + trustlist.c \ + divert-scd.c \ + cvt-openpgp.c cvt-openpgp.h \ + call-scd.c \ + learncard.c + +common_libs = $(libcommon) +commonpth_libs = $(libcommonpth) +@HAVE_W32CE_SYSTEM_FALSE@pwquery_libs = ../common/libsimple-pwquery.a +@HAVE_W32CE_SYSTEM_TRUE@pwquery_libs = +gpg_agent_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \ + $(INCICONV) + +gpg_agent_LDADD = $(commonpth_libs) \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ + $(resource_objs) + +gpg_agent_LDFLAGS = $(extra_bin_ldflags) +gpg_agent_DEPENDENCIES = $(resource_objs) +gpg_protect_tool_SOURCES = \ + protect-tool.c \ + protect.c cvt-openpgp.c + +gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) \ + $(INCICONV) + +gpg_protect_tool_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) + +gpg_preset_passphrase_SOURCES = \ + preset-passphrase.c + + +# Needs $(NETLIBS) for libsimple-pwquery.la. +gpg_preset_passphrase_LDADD = \ + $(pwquery_libs) $(common_libs) $(LIBASSUAN_LIBS) \ + $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) + +t_common_ldadd = $(common_libs) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) $(NETLIBS) + +t_protect_SOURCES = t-protect.c protect.c +t_protect_LDADD = $(t_common_ldadd) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj .rc +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu agent/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu agent/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(top_srcdir)/am/cmacros.am: + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + -test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS) + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + +gpg-agent$(EXEEXT): $(gpg_agent_OBJECTS) $(gpg_agent_DEPENDENCIES) $(EXTRA_gpg_agent_DEPENDENCIES) + @rm -f gpg-agent$(EXEEXT) + $(AM_V_CCLD)$(gpg_agent_LINK) $(gpg_agent_OBJECTS) $(gpg_agent_LDADD) $(LIBS) + +gpg-preset-passphrase$(EXEEXT): $(gpg_preset_passphrase_OBJECTS) $(gpg_preset_passphrase_DEPENDENCIES) $(EXTRA_gpg_preset_passphrase_DEPENDENCIES) + @rm -f gpg-preset-passphrase$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gpg_preset_passphrase_OBJECTS) $(gpg_preset_passphrase_LDADD) $(LIBS) + +gpg-protect-tool$(EXEEXT): $(gpg_protect_tool_OBJECTS) $(gpg_protect_tool_DEPENDENCIES) $(EXTRA_gpg_protect_tool_DEPENDENCIES) + @rm -f gpg-protect-tool$(EXEEXT) + $(AM_V_CCLD)$(gpg_protect_tool_LINK) $(gpg_protect_tool_OBJECTS) $(gpg_protect_tool_LDADD) $(LIBS) + +t-protect$(EXEEXT): $(t_protect_OBJECTS) $(t_protect_DEPENDENCIES) $(EXTRA_t_protect_DEPENDENCIES) + @rm -f t-protect$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_protect_OBJECTS) $(t_protect_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-call-pinentry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-call-scd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-command-ssh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-command.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-cvt-openpgp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-divert-scd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-findkey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-genkey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-gpg-agent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-learncard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-pkdecrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-pksign.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-protect.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-trans.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_agent-trustlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_protect_tool-cvt-openpgp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_protect_tool-protect-tool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg_protect_tool-protect.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preset-passphrase.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protect.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-protect.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +gpg_agent-gpg-agent.o: gpg-agent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-gpg-agent.o -MD -MP -MF $(DEPDIR)/gpg_agent-gpg-agent.Tpo -c -o gpg_agent-gpg-agent.o `test -f 'gpg-agent.c' || echo '$(srcdir)/'`gpg-agent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-gpg-agent.Tpo $(DEPDIR)/gpg_agent-gpg-agent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gpg-agent.c' object='gpg_agent-gpg-agent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-gpg-agent.o `test -f 'gpg-agent.c' || echo '$(srcdir)/'`gpg-agent.c + +gpg_agent-gpg-agent.obj: gpg-agent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-gpg-agent.obj -MD -MP -MF $(DEPDIR)/gpg_agent-gpg-agent.Tpo -c -o gpg_agent-gpg-agent.obj `if test -f 'gpg-agent.c'; then $(CYGPATH_W) 'gpg-agent.c'; else $(CYGPATH_W) '$(srcdir)/gpg-agent.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-gpg-agent.Tpo $(DEPDIR)/gpg_agent-gpg-agent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gpg-agent.c' object='gpg_agent-gpg-agent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-gpg-agent.obj `if test -f 'gpg-agent.c'; then $(CYGPATH_W) 'gpg-agent.c'; else $(CYGPATH_W) '$(srcdir)/gpg-agent.c'; fi` + +gpg_agent-command.o: command.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-command.o -MD -MP -MF $(DEPDIR)/gpg_agent-command.Tpo -c -o gpg_agent-command.o `test -f 'command.c' || echo '$(srcdir)/'`command.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-command.Tpo $(DEPDIR)/gpg_agent-command.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='command.c' object='gpg_agent-command.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-command.o `test -f 'command.c' || echo '$(srcdir)/'`command.c + +gpg_agent-command.obj: command.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-command.obj -MD -MP -MF $(DEPDIR)/gpg_agent-command.Tpo -c -o gpg_agent-command.obj `if test -f 'command.c'; then $(CYGPATH_W) 'command.c'; else $(CYGPATH_W) '$(srcdir)/command.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-command.Tpo $(DEPDIR)/gpg_agent-command.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='command.c' object='gpg_agent-command.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-command.obj `if test -f 'command.c'; then $(CYGPATH_W) 'command.c'; else $(CYGPATH_W) '$(srcdir)/command.c'; fi` + +gpg_agent-command-ssh.o: command-ssh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-command-ssh.o -MD -MP -MF $(DEPDIR)/gpg_agent-command-ssh.Tpo -c -o gpg_agent-command-ssh.o `test -f 'command-ssh.c' || echo '$(srcdir)/'`command-ssh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-command-ssh.Tpo $(DEPDIR)/gpg_agent-command-ssh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='command-ssh.c' object='gpg_agent-command-ssh.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-command-ssh.o `test -f 'command-ssh.c' || echo '$(srcdir)/'`command-ssh.c + +gpg_agent-command-ssh.obj: command-ssh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-command-ssh.obj -MD -MP -MF $(DEPDIR)/gpg_agent-command-ssh.Tpo -c -o gpg_agent-command-ssh.obj `if test -f 'command-ssh.c'; then $(CYGPATH_W) 'command-ssh.c'; else $(CYGPATH_W) '$(srcdir)/command-ssh.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-command-ssh.Tpo $(DEPDIR)/gpg_agent-command-ssh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='command-ssh.c' object='gpg_agent-command-ssh.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-command-ssh.obj `if test -f 'command-ssh.c'; then $(CYGPATH_W) 'command-ssh.c'; else $(CYGPATH_W) '$(srcdir)/command-ssh.c'; fi` + +gpg_agent-call-pinentry.o: call-pinentry.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-call-pinentry.o -MD -MP -MF $(DEPDIR)/gpg_agent-call-pinentry.Tpo -c -o gpg_agent-call-pinentry.o `test -f 'call-pinentry.c' || echo '$(srcdir)/'`call-pinentry.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-call-pinentry.Tpo $(DEPDIR)/gpg_agent-call-pinentry.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-pinentry.c' object='gpg_agent-call-pinentry.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-call-pinentry.o `test -f 'call-pinentry.c' || echo '$(srcdir)/'`call-pinentry.c + +gpg_agent-call-pinentry.obj: call-pinentry.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-call-pinentry.obj -MD -MP -MF $(DEPDIR)/gpg_agent-call-pinentry.Tpo -c -o gpg_agent-call-pinentry.obj `if test -f 'call-pinentry.c'; then $(CYGPATH_W) 'call-pinentry.c'; else $(CYGPATH_W) '$(srcdir)/call-pinentry.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-call-pinentry.Tpo $(DEPDIR)/gpg_agent-call-pinentry.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-pinentry.c' object='gpg_agent-call-pinentry.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-call-pinentry.obj `if test -f 'call-pinentry.c'; then $(CYGPATH_W) 'call-pinentry.c'; else $(CYGPATH_W) '$(srcdir)/call-pinentry.c'; fi` + +gpg_agent-cache.o: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-cache.o -MD -MP -MF $(DEPDIR)/gpg_agent-cache.Tpo -c -o gpg_agent-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-cache.Tpo $(DEPDIR)/gpg_agent-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='gpg_agent-cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c + +gpg_agent-cache.obj: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-cache.obj -MD -MP -MF $(DEPDIR)/gpg_agent-cache.Tpo -c -o gpg_agent-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-cache.Tpo $(DEPDIR)/gpg_agent-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='gpg_agent-cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` + +gpg_agent-trans.o: trans.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-trans.o -MD -MP -MF $(DEPDIR)/gpg_agent-trans.Tpo -c -o gpg_agent-trans.o `test -f 'trans.c' || echo '$(srcdir)/'`trans.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-trans.Tpo $(DEPDIR)/gpg_agent-trans.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='trans.c' object='gpg_agent-trans.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-trans.o `test -f 'trans.c' || echo '$(srcdir)/'`trans.c + +gpg_agent-trans.obj: trans.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-trans.obj -MD -MP -MF $(DEPDIR)/gpg_agent-trans.Tpo -c -o gpg_agent-trans.obj `if test -f 'trans.c'; then $(CYGPATH_W) 'trans.c'; else $(CYGPATH_W) '$(srcdir)/trans.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-trans.Tpo $(DEPDIR)/gpg_agent-trans.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='trans.c' object='gpg_agent-trans.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-trans.obj `if test -f 'trans.c'; then $(CYGPATH_W) 'trans.c'; else $(CYGPATH_W) '$(srcdir)/trans.c'; fi` + +gpg_agent-findkey.o: findkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-findkey.o -MD -MP -MF $(DEPDIR)/gpg_agent-findkey.Tpo -c -o gpg_agent-findkey.o `test -f 'findkey.c' || echo '$(srcdir)/'`findkey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-findkey.Tpo $(DEPDIR)/gpg_agent-findkey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='findkey.c' object='gpg_agent-findkey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-findkey.o `test -f 'findkey.c' || echo '$(srcdir)/'`findkey.c + +gpg_agent-findkey.obj: findkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-findkey.obj -MD -MP -MF $(DEPDIR)/gpg_agent-findkey.Tpo -c -o gpg_agent-findkey.obj `if test -f 'findkey.c'; then $(CYGPATH_W) 'findkey.c'; else $(CYGPATH_W) '$(srcdir)/findkey.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-findkey.Tpo $(DEPDIR)/gpg_agent-findkey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='findkey.c' object='gpg_agent-findkey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-findkey.obj `if test -f 'findkey.c'; then $(CYGPATH_W) 'findkey.c'; else $(CYGPATH_W) '$(srcdir)/findkey.c'; fi` + +gpg_agent-pksign.o: pksign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-pksign.o -MD -MP -MF $(DEPDIR)/gpg_agent-pksign.Tpo -c -o gpg_agent-pksign.o `test -f 'pksign.c' || echo '$(srcdir)/'`pksign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-pksign.Tpo $(DEPDIR)/gpg_agent-pksign.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pksign.c' object='gpg_agent-pksign.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-pksign.o `test -f 'pksign.c' || echo '$(srcdir)/'`pksign.c + +gpg_agent-pksign.obj: pksign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-pksign.obj -MD -MP -MF $(DEPDIR)/gpg_agent-pksign.Tpo -c -o gpg_agent-pksign.obj `if test -f 'pksign.c'; then $(CYGPATH_W) 'pksign.c'; else $(CYGPATH_W) '$(srcdir)/pksign.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-pksign.Tpo $(DEPDIR)/gpg_agent-pksign.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pksign.c' object='gpg_agent-pksign.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-pksign.obj `if test -f 'pksign.c'; then $(CYGPATH_W) 'pksign.c'; else $(CYGPATH_W) '$(srcdir)/pksign.c'; fi` + +gpg_agent-pkdecrypt.o: pkdecrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-pkdecrypt.o -MD -MP -MF $(DEPDIR)/gpg_agent-pkdecrypt.Tpo -c -o gpg_agent-pkdecrypt.o `test -f 'pkdecrypt.c' || echo '$(srcdir)/'`pkdecrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-pkdecrypt.Tpo $(DEPDIR)/gpg_agent-pkdecrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pkdecrypt.c' object='gpg_agent-pkdecrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-pkdecrypt.o `test -f 'pkdecrypt.c' || echo '$(srcdir)/'`pkdecrypt.c + +gpg_agent-pkdecrypt.obj: pkdecrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-pkdecrypt.obj -MD -MP -MF $(DEPDIR)/gpg_agent-pkdecrypt.Tpo -c -o gpg_agent-pkdecrypt.obj `if test -f 'pkdecrypt.c'; then $(CYGPATH_W) 'pkdecrypt.c'; else $(CYGPATH_W) '$(srcdir)/pkdecrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-pkdecrypt.Tpo $(DEPDIR)/gpg_agent-pkdecrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pkdecrypt.c' object='gpg_agent-pkdecrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-pkdecrypt.obj `if test -f 'pkdecrypt.c'; then $(CYGPATH_W) 'pkdecrypt.c'; else $(CYGPATH_W) '$(srcdir)/pkdecrypt.c'; fi` + +gpg_agent-genkey.o: genkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-genkey.o -MD -MP -MF $(DEPDIR)/gpg_agent-genkey.Tpo -c -o gpg_agent-genkey.o `test -f 'genkey.c' || echo '$(srcdir)/'`genkey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-genkey.Tpo $(DEPDIR)/gpg_agent-genkey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genkey.c' object='gpg_agent-genkey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-genkey.o `test -f 'genkey.c' || echo '$(srcdir)/'`genkey.c + +gpg_agent-genkey.obj: genkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-genkey.obj -MD -MP -MF $(DEPDIR)/gpg_agent-genkey.Tpo -c -o gpg_agent-genkey.obj `if test -f 'genkey.c'; then $(CYGPATH_W) 'genkey.c'; else $(CYGPATH_W) '$(srcdir)/genkey.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-genkey.Tpo $(DEPDIR)/gpg_agent-genkey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='genkey.c' object='gpg_agent-genkey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-genkey.obj `if test -f 'genkey.c'; then $(CYGPATH_W) 'genkey.c'; else $(CYGPATH_W) '$(srcdir)/genkey.c'; fi` + +gpg_agent-protect.o: protect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-protect.o -MD -MP -MF $(DEPDIR)/gpg_agent-protect.Tpo -c -o gpg_agent-protect.o `test -f 'protect.c' || echo '$(srcdir)/'`protect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-protect.Tpo $(DEPDIR)/gpg_agent-protect.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protect.c' object='gpg_agent-protect.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-protect.o `test -f 'protect.c' || echo '$(srcdir)/'`protect.c + +gpg_agent-protect.obj: protect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-protect.obj -MD -MP -MF $(DEPDIR)/gpg_agent-protect.Tpo -c -o gpg_agent-protect.obj `if test -f 'protect.c'; then $(CYGPATH_W) 'protect.c'; else $(CYGPATH_W) '$(srcdir)/protect.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-protect.Tpo $(DEPDIR)/gpg_agent-protect.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protect.c' object='gpg_agent-protect.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-protect.obj `if test -f 'protect.c'; then $(CYGPATH_W) 'protect.c'; else $(CYGPATH_W) '$(srcdir)/protect.c'; fi` + +gpg_agent-trustlist.o: trustlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-trustlist.o -MD -MP -MF $(DEPDIR)/gpg_agent-trustlist.Tpo -c -o gpg_agent-trustlist.o `test -f 'trustlist.c' || echo '$(srcdir)/'`trustlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-trustlist.Tpo $(DEPDIR)/gpg_agent-trustlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='trustlist.c' object='gpg_agent-trustlist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-trustlist.o `test -f 'trustlist.c' || echo '$(srcdir)/'`trustlist.c + +gpg_agent-trustlist.obj: trustlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-trustlist.obj -MD -MP -MF $(DEPDIR)/gpg_agent-trustlist.Tpo -c -o gpg_agent-trustlist.obj `if test -f 'trustlist.c'; then $(CYGPATH_W) 'trustlist.c'; else $(CYGPATH_W) '$(srcdir)/trustlist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-trustlist.Tpo $(DEPDIR)/gpg_agent-trustlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='trustlist.c' object='gpg_agent-trustlist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-trustlist.obj `if test -f 'trustlist.c'; then $(CYGPATH_W) 'trustlist.c'; else $(CYGPATH_W) '$(srcdir)/trustlist.c'; fi` + +gpg_agent-divert-scd.o: divert-scd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-divert-scd.o -MD -MP -MF $(DEPDIR)/gpg_agent-divert-scd.Tpo -c -o gpg_agent-divert-scd.o `test -f 'divert-scd.c' || echo '$(srcdir)/'`divert-scd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-divert-scd.Tpo $(DEPDIR)/gpg_agent-divert-scd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='divert-scd.c' object='gpg_agent-divert-scd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-divert-scd.o `test -f 'divert-scd.c' || echo '$(srcdir)/'`divert-scd.c + +gpg_agent-divert-scd.obj: divert-scd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-divert-scd.obj -MD -MP -MF $(DEPDIR)/gpg_agent-divert-scd.Tpo -c -o gpg_agent-divert-scd.obj `if test -f 'divert-scd.c'; then $(CYGPATH_W) 'divert-scd.c'; else $(CYGPATH_W) '$(srcdir)/divert-scd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-divert-scd.Tpo $(DEPDIR)/gpg_agent-divert-scd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='divert-scd.c' object='gpg_agent-divert-scd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-divert-scd.obj `if test -f 'divert-scd.c'; then $(CYGPATH_W) 'divert-scd.c'; else $(CYGPATH_W) '$(srcdir)/divert-scd.c'; fi` + +gpg_agent-cvt-openpgp.o: cvt-openpgp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-cvt-openpgp.o -MD -MP -MF $(DEPDIR)/gpg_agent-cvt-openpgp.Tpo -c -o gpg_agent-cvt-openpgp.o `test -f 'cvt-openpgp.c' || echo '$(srcdir)/'`cvt-openpgp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-cvt-openpgp.Tpo $(DEPDIR)/gpg_agent-cvt-openpgp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cvt-openpgp.c' object='gpg_agent-cvt-openpgp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-cvt-openpgp.o `test -f 'cvt-openpgp.c' || echo '$(srcdir)/'`cvt-openpgp.c + +gpg_agent-cvt-openpgp.obj: cvt-openpgp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-cvt-openpgp.obj -MD -MP -MF $(DEPDIR)/gpg_agent-cvt-openpgp.Tpo -c -o gpg_agent-cvt-openpgp.obj `if test -f 'cvt-openpgp.c'; then $(CYGPATH_W) 'cvt-openpgp.c'; else $(CYGPATH_W) '$(srcdir)/cvt-openpgp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-cvt-openpgp.Tpo $(DEPDIR)/gpg_agent-cvt-openpgp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cvt-openpgp.c' object='gpg_agent-cvt-openpgp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-cvt-openpgp.obj `if test -f 'cvt-openpgp.c'; then $(CYGPATH_W) 'cvt-openpgp.c'; else $(CYGPATH_W) '$(srcdir)/cvt-openpgp.c'; fi` + +gpg_agent-call-scd.o: call-scd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-call-scd.o -MD -MP -MF $(DEPDIR)/gpg_agent-call-scd.Tpo -c -o gpg_agent-call-scd.o `test -f 'call-scd.c' || echo '$(srcdir)/'`call-scd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-call-scd.Tpo $(DEPDIR)/gpg_agent-call-scd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-scd.c' object='gpg_agent-call-scd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-call-scd.o `test -f 'call-scd.c' || echo '$(srcdir)/'`call-scd.c + +gpg_agent-call-scd.obj: call-scd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-call-scd.obj -MD -MP -MF $(DEPDIR)/gpg_agent-call-scd.Tpo -c -o gpg_agent-call-scd.obj `if test -f 'call-scd.c'; then $(CYGPATH_W) 'call-scd.c'; else $(CYGPATH_W) '$(srcdir)/call-scd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-call-scd.Tpo $(DEPDIR)/gpg_agent-call-scd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-scd.c' object='gpg_agent-call-scd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-call-scd.obj `if test -f 'call-scd.c'; then $(CYGPATH_W) 'call-scd.c'; else $(CYGPATH_W) '$(srcdir)/call-scd.c'; fi` + +gpg_agent-learncard.o: learncard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-learncard.o -MD -MP -MF $(DEPDIR)/gpg_agent-learncard.Tpo -c -o gpg_agent-learncard.o `test -f 'learncard.c' || echo '$(srcdir)/'`learncard.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-learncard.Tpo $(DEPDIR)/gpg_agent-learncard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='learncard.c' object='gpg_agent-learncard.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-learncard.o `test -f 'learncard.c' || echo '$(srcdir)/'`learncard.c + +gpg_agent-learncard.obj: learncard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -MT gpg_agent-learncard.obj -MD -MP -MF $(DEPDIR)/gpg_agent-learncard.Tpo -c -o gpg_agent-learncard.obj `if test -f 'learncard.c'; then $(CYGPATH_W) 'learncard.c'; else $(CYGPATH_W) '$(srcdir)/learncard.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_agent-learncard.Tpo $(DEPDIR)/gpg_agent-learncard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='learncard.c' object='gpg_agent-learncard.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_agent_CFLAGS) $(CFLAGS) -c -o gpg_agent-learncard.obj `if test -f 'learncard.c'; then $(CYGPATH_W) 'learncard.c'; else $(CYGPATH_W) '$(srcdir)/learncard.c'; fi` + +gpg_protect_tool-protect-tool.o: protect-tool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -MT gpg_protect_tool-protect-tool.o -MD -MP -MF $(DEPDIR)/gpg_protect_tool-protect-tool.Tpo -c -o gpg_protect_tool-protect-tool.o `test -f 'protect-tool.c' || echo '$(srcdir)/'`protect-tool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_protect_tool-protect-tool.Tpo $(DEPDIR)/gpg_protect_tool-protect-tool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protect-tool.c' object='gpg_protect_tool-protect-tool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -c -o gpg_protect_tool-protect-tool.o `test -f 'protect-tool.c' || echo '$(srcdir)/'`protect-tool.c + +gpg_protect_tool-protect-tool.obj: protect-tool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -MT gpg_protect_tool-protect-tool.obj -MD -MP -MF $(DEPDIR)/gpg_protect_tool-protect-tool.Tpo -c -o gpg_protect_tool-protect-tool.obj `if test -f 'protect-tool.c'; then $(CYGPATH_W) 'protect-tool.c'; else $(CYGPATH_W) '$(srcdir)/protect-tool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_protect_tool-protect-tool.Tpo $(DEPDIR)/gpg_protect_tool-protect-tool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protect-tool.c' object='gpg_protect_tool-protect-tool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -c -o gpg_protect_tool-protect-tool.obj `if test -f 'protect-tool.c'; then $(CYGPATH_W) 'protect-tool.c'; else $(CYGPATH_W) '$(srcdir)/protect-tool.c'; fi` + +gpg_protect_tool-protect.o: protect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -MT gpg_protect_tool-protect.o -MD -MP -MF $(DEPDIR)/gpg_protect_tool-protect.Tpo -c -o gpg_protect_tool-protect.o `test -f 'protect.c' || echo '$(srcdir)/'`protect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_protect_tool-protect.Tpo $(DEPDIR)/gpg_protect_tool-protect.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protect.c' object='gpg_protect_tool-protect.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -c -o gpg_protect_tool-protect.o `test -f 'protect.c' || echo '$(srcdir)/'`protect.c + +gpg_protect_tool-protect.obj: protect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -MT gpg_protect_tool-protect.obj -MD -MP -MF $(DEPDIR)/gpg_protect_tool-protect.Tpo -c -o gpg_protect_tool-protect.obj `if test -f 'protect.c'; then $(CYGPATH_W) 'protect.c'; else $(CYGPATH_W) '$(srcdir)/protect.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_protect_tool-protect.Tpo $(DEPDIR)/gpg_protect_tool-protect.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='protect.c' object='gpg_protect_tool-protect.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -c -o gpg_protect_tool-protect.obj `if test -f 'protect.c'; then $(CYGPATH_W) 'protect.c'; else $(CYGPATH_W) '$(srcdir)/protect.c'; fi` + +gpg_protect_tool-cvt-openpgp.o: cvt-openpgp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -MT gpg_protect_tool-cvt-openpgp.o -MD -MP -MF $(DEPDIR)/gpg_protect_tool-cvt-openpgp.Tpo -c -o gpg_protect_tool-cvt-openpgp.o `test -f 'cvt-openpgp.c' || echo '$(srcdir)/'`cvt-openpgp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_protect_tool-cvt-openpgp.Tpo $(DEPDIR)/gpg_protect_tool-cvt-openpgp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cvt-openpgp.c' object='gpg_protect_tool-cvt-openpgp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -c -o gpg_protect_tool-cvt-openpgp.o `test -f 'cvt-openpgp.c' || echo '$(srcdir)/'`cvt-openpgp.c + +gpg_protect_tool-cvt-openpgp.obj: cvt-openpgp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -MT gpg_protect_tool-cvt-openpgp.obj -MD -MP -MF $(DEPDIR)/gpg_protect_tool-cvt-openpgp.Tpo -c -o gpg_protect_tool-cvt-openpgp.obj `if test -f 'cvt-openpgp.c'; then $(CYGPATH_W) 'cvt-openpgp.c'; else $(CYGPATH_W) '$(srcdir)/cvt-openpgp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gpg_protect_tool-cvt-openpgp.Tpo $(DEPDIR)/gpg_protect_tool-cvt-openpgp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cvt-openpgp.c' object='gpg_protect_tool-cvt-openpgp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gpg_protect_tool_CFLAGS) $(CFLAGS) -c -o gpg_protect_tool-cvt-openpgp.obj `if test -f 'cvt-openpgp.c'; then $(CYGPATH_W) 'cvt-openpgp.c'; else $(CYGPATH_W) '$(srcdir)/cvt-openpgp.c'; fi` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libexecPROGRAMS \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libexecPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-libexecPROGRAMS + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ + clean-binPROGRAMS clean-generic clean-libexecPROGRAMS \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libexecPROGRAMS install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-libexecPROGRAMS + + +@HAVE_W32_SYSTEM_TRUE@.rc.o: +@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" + +# Make sure that all libs are build before we use them. This is +# important for things like make -j2. +$(PROGRAMS): $(common_libs) $(commonpth_libs) $(pwquery_libs) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/agent/agent.h b/agent/agent.h new file mode 100644 index 0000000..89dc46d --- /dev/null +++ b/agent/agent.h @@ -0,0 +1,575 @@ +/* agent.h - Global definitions for the agent + * Copyright (C) 2001, 2002, 2003, 2005, 2011 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH. + * + * 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 3 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, see . + */ + +#ifndef AGENT_H +#define AGENT_H + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGAGENT +#include +#define map_assuan_err(a) \ + map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a)) +#include + +#include +#include "../common/util.h" +#include "../common/membuf.h" +#include "../common/sysutils.h" /* (gnupg_fd_t) */ +#include "../common/session-env.h" +#include "../common/shareddefs.h" + +/* To convey some special hash algorithms we use algorithm numbers + reserved for application use. */ +#ifndef GCRY_MODULE_ID_USER +#define GCRY_MODULE_ID_USER 1024 +#endif +#define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1) + +/* Maximum length of a digest. */ +#define MAX_DIGEST_LEN 64 + +/* The maximum length of a passphrase (in bytes). Note: this is + further contrained by the Assuan line length (and any other text on + the same line). However, the Assuan line length is 1k bytes so + this shouldn't be a problem in practice. */ +#define MAX_PASSPHRASE_LEN 255 + + +/* A large struct name "opt" to keep global flags */ +struct +{ + unsigned int debug; /* Debug flags (DBG_foo_VALUE) */ + int verbose; /* Verbosity level */ + int quiet; /* Be as quiet as possible */ + int dry_run; /* Don't change any persistent data */ + int batch; /* Batch mode */ + + /* True if we handle sigusr2. */ + int sigusr2_enabled; + + /* Environment settings gathered at program start or changed using the + Assuan command UPDATESTARTUPTTY. */ + session_env_t startup_env; + char *startup_lc_ctype; + char *startup_lc_messages; + + /* Enable pinentry debugging (--debug 1024 should also be used). */ + int debug_pinentry; + + /* Filename of the program to start as pinentry. */ + const char *pinentry_program; + + /* Filename of the program to handle smartcard tasks. */ + const char *scdaemon_program; + + int disable_scdaemon; /* Never use the SCdaemon. */ + + int no_grab; /* Don't let the pinentry grab the keyboard */ + + /* The name of the file pinentry shall touch before exiting. If + this is not set the file name of the standard socket is used. */ + const char *pinentry_touch_file; + + /* A string where the first character is used by the pinentry as a + custom invisible character. */ + char *pinentry_invisible_char; + + /* The timeout value for the Pinentry in seconds. This is passed to + the pinentry if it is not 0. It is up to the pinentry to act + upon this timeout value. */ + unsigned long pinentry_timeout; + + /* The default and maximum TTL of cache entries. */ + unsigned long def_cache_ttl; /* Default. */ + unsigned long def_cache_ttl_ssh; /* for SSH. */ + unsigned long max_cache_ttl; /* Default. */ + unsigned long max_cache_ttl_ssh; /* for SSH. */ + + /* Flag disallowing bypassing of the warning. */ + int enforce_passphrase_constraints; + + /* The require minmum length of a passphrase. */ + unsigned int min_passphrase_len; + + /* The minimum number of non-alpha characters in a passphrase. */ + unsigned int min_passphrase_nonalpha; + + /* File name with a patternfile or NULL if not enabled. */ + const char *check_passphrase_pattern; + + /* If not 0 the user is asked to change his passphrase after these + number of days. */ + unsigned int max_passphrase_days; + + /* If set, a passphrase history will be written and checked at each + passphrase change. */ + int enable_passphrase_history; + + int running_detached; /* We are running detached from the tty. */ + + /* If this global option is true, the passphrase cache is ignored + for signing operations. */ + int ignore_cache_for_signing; + + /* If this global option is true, the user is allowed to + interactively mark certificate in trustlist.txt as trusted. */ + int allow_mark_trusted; + + /* If this global option is true, the Assuan command + PRESET_PASSPHRASE is allowed. */ + int allow_preset_passphrase; + + /* If this global option is true, the Assuan option + pinentry-mode=loopback is allowed. */ + int allow_loopback_pinentry; + + /* Allow the use of an external password cache. If this option is + enabled (which is the default) we send an option to Pinentry + to allow it to enable such a cache. */ + int allow_external_cache; + + /* If this global option is true, the Assuan option of Pinentry + allow-emacs-prompt is allowed. */ + int allow_emacs_pinentry; + + int keep_tty; /* Don't switch the TTY (for pinentry) on request */ + int keep_display; /* Don't switch the DISPLAY (for pinentry) on request */ + + /* This global option indicates the use of an extra socket. Note + that we use a hack for cleanup handling in gpg-agent.c: If the + value is less than 2 the name has not yet been malloced. */ + int extra_socket; + + /* This global option indicates the use of an extra socket for web + browsers. Note that we use a hack for cleanup handling in + gpg-agent.c: If the value is less than 2 the name has not yet + been malloced. */ + int browser_socket; +} opt; + + +/* Bit values for the --debug option. */ +#define DBG_MPI_VALUE 2 /* debug mpi details */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ +#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ +#define DBG_CACHE_VALUE 64 /* debug the caching */ +#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ +#define DBG_IPC_VALUE 1024 /* Enable Assuan debugging. */ + +/* Test macros for the debug option. */ +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) +#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) +#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) +#define DBG_IPC (opt.debug & DBG_IPC_VALUE) + +/* Forward reference for local definitions in command.c. */ +struct server_local_s; + +/* Declaration of objects from command-ssh.c. */ +struct ssh_control_file_s; +typedef struct ssh_control_file_s *ssh_control_file_t; + +/* Forward reference for local definitions in call-scd.c. */ +struct scd_local_s; + +/* Collection of data per session (aka connection). */ +struct server_control_s +{ + /* Private data used to fire up the connection thread. We use this + structure do avoid an extra allocation for only a few bytes while + spawning a new connection thread. */ + struct { + gnupg_fd_t fd; + } thread_startup; + + /* Flag indicating the connection is run in restricted mode. + A value of 1 if used for --extra-socket, + a value of 2 is used for --browser-socket. */ + int restricted; + + /* Private data of the server (command.c). */ + struct server_local_s *server_local; + + /* Private data of the SCdaemon (call-scd.c). */ + struct scd_local_s *scd_local; + + /* Environment settings for the connection. */ + session_env_t session_env; + char *lc_ctype; + char *lc_messages; + + /* The current pinentry mode. */ + pinentry_mode_t pinentry_mode; + + /* The TTL used for the --preset option of certain commands. */ + int cache_ttl_opt_preset; + + /* Information on the currently used digest (for signing commands). */ + struct { + int algo; + unsigned char value[MAX_DIGEST_LEN]; + int valuelen; + int raw_value: 1; + } digest; + unsigned char keygrip[20]; + int have_keygrip; + + /* A flag to enable a hack to send the PKAUTH command instead of the + PKSIGN command to the scdaemon. */ + int use_auth_call; + + /* A flag to inhibit enforced passphrase change during an explicit + passwd command. */ + int in_passwd; + + /* The current S2K which might be different from the calibrated + count. */ + unsigned long s2k_count; +}; + + +/* Information pertaining to pinentry requests. */ +struct pin_entry_info_s +{ + int min_digits; /* min. number of digits required or 0 for freeform entry */ + int max_digits; /* max. number of allowed digits allowed*/ + int max_tries; /* max. number of allowed tries. */ + int failed_tries; /* Number of tries so far failed. */ + int with_qualitybar; /* Set if the quality bar should be displayed. */ + int with_repeat; /* Request repetition of the passphrase. */ + int repeat_okay; /* Repetition worked. */ + gpg_error_t (*check_cb)(struct pin_entry_info_s *); /* CB used to check + the PIN */ + void *check_cb_arg; /* optional argument which might be of use in the CB */ + const char *cb_errtext; /* used by the cb to display a specific error */ + size_t max_length; /* Allocated length of the buffer PIN. */ + char pin[1]; /* The buffer to hold the PIN or passphrase. + It's actual allocated length is given by + MAX_LENGTH (above). */ +}; + + +/* Types of the private keys. */ +enum + { + PRIVATE_KEY_UNKNOWN = 0, /* Type of key is not known. */ + PRIVATE_KEY_CLEAR = 1, /* The key is not protected. */ + PRIVATE_KEY_PROTECTED = 2, /* The key is protected. */ + PRIVATE_KEY_SHADOWED = 3, /* The key is a stub for a smartcard + based key. */ + PROTECTED_SHARED_SECRET = 4, /* RFU. */ + PRIVATE_KEY_OPENPGP_NONE = 5 /* openpgp-native with protection "none". */ + }; + + +/* Values for the cache_mode arguments. */ +typedef enum + { + CACHE_MODE_IGNORE = 0, /* Special mode to bypass the cache. */ + CACHE_MODE_ANY, /* Any mode except ignore matches. */ + CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */ + CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */ + CACHE_MODE_SSH, /* SSH related cache. */ + CACHE_MODE_NONCE /* This is a non-predictable nonce. */ + } +cache_mode_t; + +/* The TTL is seconds used for adding a new nonce mode cache item. */ +#define CACHE_TTL_NONCE 120 + +/* The TTL in seconds used by the --preset option of some commands. + This is the default value changeable by an OPTION command. */ +#define CACHE_TTL_OPT_PRESET 900 + + +/* The type of a function to lookup a TTL by a keygrip. */ +typedef int (*lookup_ttl_t)(const char *hexgrip); + + +/* This is a special version of the usual _() gettext macro. It + assumes a server connection control variable with the name "ctrl" + and uses that to translate a string according to the locale set for + the connection. The macro LunderscoreIMPL is used by i18n to + actually define the inline function when needed. */ +#ifdef ENABLE_NLS +#define L_(a) agent_Lunderscore (ctrl, (a)) +#define LunderscorePROTO \ + static inline const char *agent_Lunderscore (ctrl_t ctrl, \ + const char *string) \ + GNUPG_GCC_ATTR_FORMAT_ARG(2); +#define LunderscoreIMPL \ + static inline const char * \ + agent_Lunderscore (ctrl_t ctrl, const char *string) \ + { \ + return ctrl? i18n_localegettext (ctrl->lc_messages, string) \ + /* */: gettext (string); \ + } +#else +#define L_(a) (a) +#endif + + +/*-- gpg-agent.c --*/ +void agent_exit (int rc) + GPGRT_ATTR_NORETURN; /* Also implemented in other tools */ +void agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what, + int printchar, int current, int total), + ctrl_t ctrl); +gpg_error_t agent_copy_startup_env (ctrl_t ctrl); +const char *get_agent_socket_name (void); +const char *get_agent_ssh_socket_name (void); +int get_agent_active_connection_count (void); +#ifdef HAVE_W32_SYSTEM +void *get_agent_scd_notify_event (void); +#endif +void agent_sighup_action (void); +int map_pk_openpgp_to_gcry (int openpgp_algo); + +/*-- command.c --*/ +gpg_error_t agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid, + const char *extra); +gpg_error_t agent_write_status (ctrl_t ctrl, const char *keyword, ...) + GPGRT_ATTR_SENTINEL(0); +gpg_error_t agent_print_status (ctrl_t ctrl, const char *keyword, + const char *format, ...) + GPGRT_ATTR_PRINTF(3,4); +void bump_key_eventcounter (void); +void bump_card_eventcounter (void); +void start_command_handler (ctrl_t, gnupg_fd_t, gnupg_fd_t); +gpg_error_t pinentry_loopback (ctrl_t, const char *keyword, + unsigned char **buffer, size_t *size, + size_t max_length); + +#ifdef HAVE_W32_SYSTEM +int serve_mmapped_ssh_request (ctrl_t ctrl, + unsigned char *request, size_t maxreqlen); +#endif /*HAVE_W32_SYSTEM*/ + +/*-- command-ssh.c --*/ +ssh_control_file_t ssh_open_control_file (void); +void ssh_close_control_file (ssh_control_file_t cf); +gpg_error_t ssh_read_control_file (ssh_control_file_t cf, + char *r_hexgrip, int *r_disabled, + int *r_ttl, int *r_confirm); +gpg_error_t ssh_search_control_file (ssh_control_file_t cf, + const char *hexgrip, + int *r_disabled, + int *r_ttl, int *r_confirm); + +void start_command_handler_ssh (ctrl_t, gnupg_fd_t); + +/*-- findkey.c --*/ +int agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force); +gpg_error_t agent_key_from_file (ctrl_t ctrl, + const char *cache_nonce, + const char *desc_text, + const unsigned char *grip, + unsigned char **shadow_info, + cache_mode_t cache_mode, + lookup_ttl_t lookup_ttl, + gcry_sexp_t *result, + char **r_passphrase); +gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, + gcry_sexp_t *result); +gpg_error_t agent_public_key_from_file (ctrl_t ctrl, + const unsigned char *grip, + gcry_sexp_t *result); +int agent_is_dsa_key (gcry_sexp_t s_key); +int agent_is_eddsa_key (gcry_sexp_t s_key); +int agent_key_available (const unsigned char *grip); +gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, + int *r_keytype, + unsigned char **r_shadow_info); +gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, int force); + +/*-- call-pinentry.c --*/ +void initialize_module_call_pinentry (void); +void agent_query_dump_state (void); +void agent_reset_query (ctrl_t ctrl); +int pinentry_active_p (ctrl_t ctrl, int waitseconds); +gpg_error_t agent_askpin (ctrl_t ctrl, + const char *desc_text, const char *prompt_text, + const char *inital_errtext, + struct pin_entry_info_s *pininfo, + const char *keyinfo, cache_mode_t cache_mode); +int agent_get_passphrase (ctrl_t ctrl, char **retpass, + const char *desc, const char *prompt, + const char *errtext, int with_qualitybar, + const char *keyinfo, cache_mode_t cache_mode); +int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok, + const char *notokay, int with_cancel); +int agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn); +int agent_popup_message_start (ctrl_t ctrl, + const char *desc, const char *ok_btn); +void agent_popup_message_stop (ctrl_t ctrl); +int agent_clear_passphrase (ctrl_t ctrl, + const char *keyinfo, cache_mode_t cache_mode); + +/*-- cache.c --*/ +void initialize_module_cache (void); +void deinitialize_module_cache (void); +void agent_flush_cache (void); +int agent_put_cache (const char *key, cache_mode_t cache_mode, + const char *data, int ttl); +char *agent_get_cache (const char *key, cache_mode_t cache_mode); +void agent_store_cache_hit (const char *key); + + +/*-- pksign.c --*/ +int agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, + const char *desc_text, + gcry_sexp_t *signature_sexp, + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + const void *overridedata, size_t overridedatalen); +int agent_pksign (ctrl_t ctrl, const char *cache_nonce, + const char *desc_text, + membuf_t *outbuf, cache_mode_t cache_mode); + +/*-- pkdecrypt.c --*/ +int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, + const unsigned char *ciphertext, size_t ciphertextlen, + membuf_t *outbuf, int *r_padding); + +/*-- genkey.c --*/ +int check_passphrase_constraints (ctrl_t ctrl, const char *pw, + char **failed_constraint); +gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, + char **r_passphrase); +int agent_genkey (ctrl_t ctrl, const char *cache_nonce, + const char *keyparam, size_t keyparmlen, + int no_protection, const char *override_passphrase, + int preset, membuf_t *outbuf); +gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, + char **passphrase_addr); + +/*-- protect.c --*/ +unsigned long get_standard_s2k_count (void); +unsigned char get_standard_s2k_count_rfc4880 (void); +int agent_protect (const unsigned char *plainkey, const char *passphrase, + unsigned char **result, size_t *resultlen, + unsigned long s2k_count, int use_ocb); +int agent_unprotect (ctrl_t ctrl, + const unsigned char *protectedkey, const char *passphrase, + gnupg_isotime_t protected_at, + unsigned char **result, size_t *resultlen); +int agent_private_key_type (const unsigned char *privatekey); +unsigned char *make_shadow_info (const char *serialno, const char *idstring); +int agent_shadow_key (const unsigned char *pubkey, + const unsigned char *shadow_info, + unsigned char **result); +int agent_get_shadow_info (const unsigned char *shadowkey, + unsigned char const **shadow_info); +gpg_error_t parse_shadow_info (const unsigned char *shadow_info, + char **r_hexsn, char **r_idstr, int *r_pinlen); +gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, + unsigned int s2kcount, + unsigned char *key, size_t keylen); +gpg_error_t agent_write_shadow_key (const unsigned char *grip, + const char *serialno, const char *keyid, + const unsigned char *pkbuf, int force); + + +/*-- trustlist.c --*/ +void initialize_module_trustlist (void); +gpg_error_t agent_istrusted (ctrl_t ctrl, const char *fpr, int *r_disabled); +gpg_error_t agent_listtrusted (void *assuan_context); +gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name, + const char *fpr, int flag); +void agent_reload_trustlist (void); + + +/*-- divert-scd.c --*/ +int divert_pksign (ctrl_t ctrl, + const unsigned char *digest, size_t digestlen, int algo, + const unsigned char *shadow_info, unsigned char **r_sig, + size_t *r_siglen); +int divert_pkdecrypt (ctrl_t ctrl, + const unsigned char *cipher, + const unsigned char *shadow_info, + char **r_buf, size_t *r_len, int *r_padding); +int divert_generic_cmd (ctrl_t ctrl, + const char *cmdline, void *assuan_context); +int divert_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, size_t keydatalen); + + +/*-- call-scd.c --*/ +void initialize_module_call_scd (void); +void agent_scd_dump_state (void); +int agent_scd_check_running (void); +void agent_scd_check_aliveness (void); +int agent_reset_scd (ctrl_t ctrl); +int agent_card_learn (ctrl_t ctrl, + void (*kpinfo_cb)(void*, const char *), + void *kpinfo_cb_arg, + void (*certinfo_cb)(void*, const char *), + void *certinfo_cb_arg, + void (*sinfo_cb)(void*, const char *, + size_t, const char *), + void *sinfo_cb_arg); +int agent_card_serialno (ctrl_t ctrl, char **r_serialno); +int agent_card_pksign (ctrl_t ctrl, + const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + int mdalgo, + const unsigned char *indata, size_t indatalen, + unsigned char **r_buf, size_t *r_buflen); +int agent_card_pkdecrypt (ctrl_t ctrl, + const char *keyid, + int (*getpin_cb)(void *, const char *, char*,size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen, int *r_padding); +int agent_card_readcert (ctrl_t ctrl, + const char *id, char **r_buf, size_t *r_buflen); +int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf); +int agent_card_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, + size_t keydatalen, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg); +gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result); +int agent_card_scd (ctrl_t ctrl, const char *cmdline, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, void *assuan_context); + + +/*-- learncard.c --*/ +int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force); + + +/*-- cvt-openpgp.c --*/ +gpg_error_t +extract_private_key (gcry_sexp_t s_key, int req_private_key_data, + const char **r_algoname, int *r_npkey, int *r_nskey, + const char **r_format, + gcry_mpi_t *mpi_array, int arraysize, + gcry_sexp_t *r_curve, gcry_sexp_t *r_flags); + +#endif /*AGENT_H*/ diff --git a/agent/cache.c b/agent/cache.c new file mode 100644 index 0000000..f58eaea --- /dev/null +++ b/agent/cache.c @@ -0,0 +1,480 @@ +/* cache.c - keep a cache of passphrases + * Copyright (C) 2002, 2010 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "agent.h" + +/* The size of the encryption key in bytes. */ +#define ENCRYPTION_KEYSIZE (128/8) + +/* A mutex used to protect the encryption. This is required because + we use one context to do all encryption and decryption. */ +static npth_mutex_t encryption_lock; +/* The encryption context. This is the only place where the + encryption key for all cached entries is available. It would be nice + to keep this (or just the key) in some hardware device, for example + a TPM. Libgcrypt could be extended to provide such a service. + With the current scheme it is easy to retrieve the cached entries + if access to Libgcrypt's memory is available. The encryption + merely avoids grepping for clear texts in the memory. Nevertheless + the encryption provides the necessary infrastructure to make it + more secure. */ +static gcry_cipher_hd_t encryption_handle; + + +struct secret_data_s { + int totallen; /* This includes the padding and space for AESWRAP. */ + char data[1]; /* A string. */ +}; + +typedef struct cache_item_s *ITEM; +struct cache_item_s { + ITEM next; + time_t created; + time_t accessed; + int ttl; /* max. lifetime given in seconds, -1 one means infinite */ + struct secret_data_s *pw; + cache_mode_t cache_mode; + char key[1]; +}; + +/* The cache himself. */ +static ITEM thecache; + +/* NULL or the last cache key stored by agent_store_cache_hit. */ +static char *last_stored_cache_key; + + +/* This function must be called once to initialize this module. It + has to be done before a second thread is spawned. */ +void +initialize_module_cache (void) +{ + int err; + + err = npth_mutex_init (&encryption_lock, NULL); + + if (err) + log_fatal ("error initializing cache module: %s\n", strerror (err)); +} + + +void +deinitialize_module_cache (void) +{ + gcry_cipher_close (encryption_handle); + encryption_handle = NULL; +} + + +/* We do the encryption init on the fly. We can't do it in the module + init code because that is run before we listen for connections and + in case we are started on demand by gpg etc. it will only wait for + a few seconds to decide whether the agent may now accept + connections. Thus we should get into listen state as soon as + possible. */ +static gpg_error_t +init_encryption (void) +{ + gpg_error_t err; + void *key; + int res; + + if (encryption_handle) + return 0; /* Shortcut - Already initialized. */ + + res = npth_mutex_lock (&encryption_lock); + if (res) + log_fatal ("failed to acquire cache encryption mutex: %s\n", strerror (res)); + + err = gcry_cipher_open (&encryption_handle, GCRY_CIPHER_AES128, + GCRY_CIPHER_MODE_AESWRAP, GCRY_CIPHER_SECURE); + if (!err) + { + key = gcry_random_bytes (ENCRYPTION_KEYSIZE, GCRY_STRONG_RANDOM); + if (!key) + err = gpg_error_from_syserror (); + else + { + err = gcry_cipher_setkey (encryption_handle, key, ENCRYPTION_KEYSIZE); + xfree (key); + } + if (err) + { + gcry_cipher_close (encryption_handle); + encryption_handle = NULL; + } + } + if (err) + log_error ("error initializing cache encryption context: %s\n", + gpg_strerror (err)); + + res = npth_mutex_unlock (&encryption_lock); + if (res) + log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res)); + + return err? gpg_error (GPG_ERR_NOT_INITIALIZED) : 0; +} + + + +static void +release_data (struct secret_data_s *data) +{ + xfree (data); +} + +static gpg_error_t +new_data (const char *string, struct secret_data_s **r_data) +{ + gpg_error_t err; + struct secret_data_s *d, *d_enc; + size_t length; + int total; + int res; + + *r_data = NULL; + + err = init_encryption (); + if (err) + return err; + + length = strlen (string) + 1; + + /* We pad the data to 32 bytes so that it get more complicated + finding something out by watching allocation patterns. This is + usually not possible but we better assume nothing about our secure + storage provider. To support the AESWRAP mode we need to add 8 + extra bytes as well. */ + total = (length + 8) + 32 - ((length+8) % 32); + + d = xtrymalloc_secure (sizeof *d + total - 1); + if (!d) + return gpg_error_from_syserror (); + memcpy (d->data, string, length); + + d_enc = xtrymalloc (sizeof *d_enc + total - 1); + if (!d_enc) + { + err = gpg_error_from_syserror (); + xfree (d); + return err; + } + + d_enc->totallen = total; + res = npth_mutex_lock (&encryption_lock); + if (res) + log_fatal ("failed to acquire cache encryption mutex: %s\n", + strerror (res)); + + err = gcry_cipher_encrypt (encryption_handle, d_enc->data, total, + d->data, total - 8); + xfree (d); + res = npth_mutex_unlock (&encryption_lock); + if (res) + log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res)); + if (err) + { + xfree (d_enc); + return err; + } + *r_data = d_enc; + return 0; +} + + + +/* Check whether there are items to expire. */ +static void +housekeeping (void) +{ + ITEM r, rprev; + time_t current = gnupg_get_time (); + + /* First expire the actual data */ + for (r=thecache; r; r = r->next) + { + if (r->pw && r->ttl >= 0 && r->accessed + r->ttl < current) + { + if (DBG_CACHE) + log_debug (" expired '%s' (%ds after last access)\n", + r->key, r->ttl); + release_data (r->pw); + r->pw = NULL; + r->accessed = current; + } + } + + /* Second, make sure that we also remove them based on the created stamp so + that the user has to enter it from time to time. */ + for (r=thecache; r; r = r->next) + { + unsigned long maxttl; + + switch (r->cache_mode) + { + case CACHE_MODE_SSH: maxttl = opt.max_cache_ttl_ssh; break; + default: maxttl = opt.max_cache_ttl; break; + } + if (r->pw && r->created + maxttl < current) + { + if (DBG_CACHE) + log_debug (" expired '%s' (%lus after creation)\n", + r->key, opt.max_cache_ttl); + release_data (r->pw); + r->pw = NULL; + r->accessed = current; + } + } + + /* Third, make sure that we don't have too many items in the list. + Expire old and unused entries after 30 minutes */ + for (rprev=NULL, r=thecache; r; ) + { + if (!r->pw && r->ttl >= 0 && r->accessed + 60*30 < current) + { + ITEM r2 = r->next; + if (DBG_CACHE) + log_debug (" removed '%s' (mode %d) (slot not used for 30m)\n", + r->key, r->cache_mode); + xfree (r); + if (!rprev) + thecache = r2; + else + rprev->next = r2; + r = r2; + } + else + { + rprev = r; + r = r->next; + } + } +} + + +void +agent_flush_cache (void) +{ + ITEM r; + + if (DBG_CACHE) + log_debug ("agent_flush_cache\n"); + + for (r=thecache; r; r = r->next) + { + if (r->pw) + { + if (DBG_CACHE) + log_debug (" flushing '%s'\n", r->key); + release_data (r->pw); + r->pw = NULL; + r->accessed = 0; + } + } +} + + +/* Compare two cache modes. */ +static int +cache_mode_equal (cache_mode_t a, cache_mode_t b) +{ + /* CACHE_MODE_ANY matches any mode other than CACHE_MODE_IGNORE. */ + return ((a == CACHE_MODE_ANY && b != CACHE_MODE_IGNORE) + || (b == CACHE_MODE_ANY && a != CACHE_MODE_IGNORE) || a == b); +} + + +/* Store the string DATA in the cache under KEY and mark it with a + maximum lifetime of TTL seconds. If there is already data under + this key, it will be replaced. Using a DATA of NULL deletes the + entry. A TTL of 0 is replaced by the default TTL and a TTL of -1 + set infinite timeout. CACHE_MODE is stored with the cache entry + and used to select different timeouts. */ +int +agent_put_cache (const char *key, cache_mode_t cache_mode, + const char *data, int ttl) +{ + gpg_error_t err = 0; + ITEM r; + + if (DBG_CACHE) + log_debug ("agent_put_cache '%s' (mode %d) requested ttl=%d\n", + key, cache_mode, ttl); + housekeeping (); + + if (!ttl) + { + switch(cache_mode) + { + case CACHE_MODE_SSH: ttl = opt.def_cache_ttl_ssh; break; + default: ttl = opt.def_cache_ttl; break; + } + } + if ((!ttl && data) || cache_mode == CACHE_MODE_IGNORE) + return 0; + + for (r=thecache; r; r = r->next) + { + if (((cache_mode != CACHE_MODE_USER + && cache_mode != CACHE_MODE_NONCE) + || cache_mode_equal (r->cache_mode, cache_mode)) + && !strcmp (r->key, key)) + break; + } + if (r) /* Replace. */ + { + if (r->pw) + { + release_data (r->pw); + r->pw = NULL; + } + if (data) + { + r->created = r->accessed = gnupg_get_time (); + r->ttl = ttl; + r->cache_mode = cache_mode; + err = new_data (data, &r->pw); + if (err) + log_error ("error replacing cache item: %s\n", gpg_strerror (err)); + } + } + else if (data) /* Insert. */ + { + r = xtrycalloc (1, sizeof *r + strlen (key)); + if (!r) + err = gpg_error_from_syserror (); + else + { + strcpy (r->key, key); + r->created = r->accessed = gnupg_get_time (); + r->ttl = ttl; + r->cache_mode = cache_mode; + err = new_data (data, &r->pw); + if (err) + xfree (r); + else + { + r->next = thecache; + thecache = r; + } + } + if (err) + log_error ("error inserting cache item: %s\n", gpg_strerror (err)); + } + return err; +} + + +/* Try to find an item in the cache. Note that we currently don't + make use of CACHE_MODE except for CACHE_MODE_NONCE and + CACHE_MODE_USER. */ +char * +agent_get_cache (const char *key, cache_mode_t cache_mode) +{ + gpg_error_t err; + ITEM r; + char *value = NULL; + int res; + int last_stored = 0; + + if (cache_mode == CACHE_MODE_IGNORE) + return NULL; + + if (!key) + { + key = last_stored_cache_key; + if (!key) + return NULL; + last_stored = 1; + } + + + if (DBG_CACHE) + log_debug ("agent_get_cache '%s' (mode %d)%s ...\n", + key, cache_mode, + last_stored? " (stored cache key)":""); + housekeeping (); + + for (r=thecache; r; r = r->next) + { + if (r->pw + && ((cache_mode != CACHE_MODE_USER + && cache_mode != CACHE_MODE_NONCE) + || cache_mode_equal (r->cache_mode, cache_mode)) + && !strcmp (r->key, key)) + { + /* Note: To avoid races KEY may not be accessed anymore below. */ + r->accessed = gnupg_get_time (); + if (DBG_CACHE) + log_debug ("... hit\n"); + if (r->pw->totallen < 32) + err = gpg_error (GPG_ERR_INV_LENGTH); + else if ((err = init_encryption ())) + ; + else if (!(value = xtrymalloc_secure (r->pw->totallen - 8))) + err = gpg_error_from_syserror (); + else + { + res = npth_mutex_lock (&encryption_lock); + if (res) + log_fatal ("failed to acquire cache encryption mutex: %s\n", + strerror (res)); + err = gcry_cipher_decrypt (encryption_handle, + value, r->pw->totallen - 8, + r->pw->data, r->pw->totallen); + res = npth_mutex_unlock (&encryption_lock); + if (res) + log_fatal ("failed to release cache encryption mutex: %s\n", + strerror (res)); + } + if (err) + { + xfree (value); + value = NULL; + log_error ("retrieving cache entry '%s' failed: %s\n", + key, gpg_strerror (err)); + } + return value; + } + } + if (DBG_CACHE) + log_debug ("... miss\n"); + + return NULL; +} + + +/* Store the key for the last successful cache hit. That value is + used by agent_get_cache if the requested KEY is given as NULL. + NULL may be used to remove that key. */ +void +agent_store_cache_hit (const char *key) +{ + xfree (last_stored_cache_key); + last_stored_cache_key = key? xtrystrdup (key) : NULL; +} diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c new file mode 100644 index 0000000..fa00bf9 --- /dev/null +++ b/agent/call-pinentry.c @@ -0,0 +1,1493 @@ +/* call-pinentry.c - Spawn the pinentry to query stuff from the user + * Copyright (C) 2001, 2002, 2004, 2007, 2008, + * 2010 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef HAVE_W32_SYSTEM +# include +# include +# include +#endif +#include + +#include "agent.h" +#include +#include "sysutils.h" +#include "i18n.h" + +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + + +/* Because access to the pinentry must be serialized (it is and shall + be a global mutually exclusive dialog) we better timeout pending + requests after some time. 1 minute seem to be a reasonable + time. */ +#define LOCK_TIMEOUT (1*60) + +/* The assuan context of the current pinentry. */ +static assuan_context_t entry_ctx; + +/* The control variable of the connection owning the current pinentry. + This is only valid if ENTRY_CTX is not NULL. Note, that we care + only about the value of the pointer and that it should never be + dereferenced. */ +static ctrl_t entry_owner; + +/* A mutex used to serialize access to the pinentry. */ +static npth_mutex_t entry_lock; + +/* The thread ID of the popup working thread. */ +static npth_t popup_tid; + +/* A flag used in communication between the popup working thread and + its stop function. */ +static int popup_finished; + + + +/* Data to be passed to our callbacks, */ +struct entry_parm_s +{ + int lines; + size_t size; + unsigned char *buffer; +}; + + + + +/* This function must be called once to initialize this module. This + has to be done before a second thread is spawned. We can't do the + static initialization because Pth emulation code might not be able + to do a static init; in particular, it is not possible for W32. */ +void +initialize_module_call_pinentry (void) +{ + static int initialized; + + if (!initialized) + { + if (npth_mutex_init (&entry_lock, NULL)) + initialized = 1; + } +} + + + +/* This function may be called to print infromation pertaining to the + current state of this module to the log. */ +void +agent_query_dump_state (void) +{ + log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%p\n", + entry_ctx, (long)assuan_get_pid (entry_ctx), (void*)popup_tid); +} + +/* Called to make sure that a popup window owned by the current + connection gets closed. */ +void +agent_reset_query (ctrl_t ctrl) +{ + if (entry_ctx && popup_tid && entry_owner == ctrl) + { + agent_popup_message_stop (ctrl); + } +} + + +/* Unlock the pinentry so that another thread can start one and + disconnect that pinentry - we do this after the unlock so that a + stalled pinentry does not block other threads. Fixme: We should + have a timeout in Assuan for the disconnect operation. */ +static gpg_error_t +unlock_pinentry (gpg_error_t rc) +{ + assuan_context_t ctx = entry_ctx; + int err; + + if (rc) + { + if (DBG_IPC) + log_debug ("error calling pinentry: %s <%s>\n", + gpg_strerror (rc), gpg_strsource (rc)); + + /* Change the source of the error to pinentry so that the final + consumer of the error code knows that the problem is with + pinentry. For backward compatibility we do not do that for + some common error codes. */ + switch (gpg_err_code (rc)) + { + case GPG_ERR_NO_PIN_ENTRY: + case GPG_ERR_CANCELED: + case GPG_ERR_FULLY_CANCELED: + case GPG_ERR_ASS_UNKNOWN_INQUIRE: + case GPG_ERR_ASS_TOO_MUCH_DATA: + case GPG_ERR_NO_PASSPHRASE: + case GPG_ERR_BAD_PASSPHRASE: + case GPG_ERR_BAD_PIN: + break; + + default: + rc = gpg_err_make (GPG_ERR_SOURCE_PINENTRY, gpg_err_code (rc)); + break; + } + } + + entry_ctx = NULL; + err = npth_mutex_unlock (&entry_lock); + if (err) + { + log_error ("failed to release the entry lock: %s\n", strerror (err)); + if (!rc) + rc = gpg_error_from_errno (err); + } + assuan_release (ctx); + return rc; +} + + +/* To make sure we leave no secrets in our image after forking of the + pinentry, we use this callback. */ +static void +atfork_cb (void *opaque, int where) +{ + ctrl_t ctrl = opaque; + + if (!where) + { + int iterator = 0; + const char *name, *assname, *value; + + gcry_control (GCRYCTL_TERM_SECMEM); + + while ((name = session_env_list_stdenvnames (&iterator, &assname))) + { + /* For all new envvars (!ASSNAME) and the two medium old + ones which do have an assuan name but are conveyed using + environment variables, update the environment of the + forked process. */ + if (!assname + || !strcmp (name, "XAUTHORITY") + || !strcmp (name, "PINENTRY_USER_DATA")) + { + value = session_env_getenv (ctrl->session_env, name); + if (value) + gnupg_setenv (name, value, 1); + } + } + } +} + + +static gpg_error_t +getinfo_pid_cb (void *opaque, const void *buffer, size_t length) +{ + unsigned long *pid = opaque; + char pidbuf[50]; + + /* There is only the pid in the server's response. */ + if (length >= sizeof pidbuf) + length = sizeof pidbuf -1; + if (length) + { + strncpy (pidbuf, buffer, length); + pidbuf[length] = 0; + *pid = strtoul (pidbuf, NULL, 10); + } + return 0; +} + + +/* Fork off the pin entry if this has not already been done. Note, + that this function must always be used to acquire the lock for the + pinentry - we will serialize _all_ pinentry calls. + */ +static gpg_error_t +start_pinentry (ctrl_t ctrl) +{ + int rc = 0; + const char *full_pgmname; + const char *pgmname; + assuan_context_t ctx; + const char *argv[5]; + assuan_fd_t no_close_list[3]; + int i; + const char *tmpstr; + unsigned long pinentry_pid; + const char *value; + struct timespec abstime; + char *flavor_version; + int err; + + npth_clock_gettime (&abstime); + abstime.tv_sec += LOCK_TIMEOUT; + err = npth_mutex_timedlock (&entry_lock, &abstime); + if (err) + { + if (err == ETIMEDOUT) + rc = gpg_error (GPG_ERR_TIMEOUT); + else + rc = gpg_error_from_errno (rc); + log_error (_("failed to acquire the pinentry lock: %s\n"), + gpg_strerror (rc)); + return rc; + } + + entry_owner = ctrl; + + if (entry_ctx) + return 0; + + if (opt.verbose) + log_info ("starting a new PIN Entry\n"); + +#ifdef HAVE_W32_SYSTEM + fflush (stdout); + fflush (stderr); +#endif + if (fflush (NULL)) + { +#ifndef HAVE_W32_SYSTEM + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); +#endif + log_error ("error flushing pending output: %s\n", strerror (errno)); + /* At least Windows XP fails here with EBADF. According to docs + and Wine an fflush(NULL) is the same as _flushall. However + the Wine implementaion does not flush stdin,stdout and stderr + - see above. Let's try to ignore the error. */ +#ifndef HAVE_W32_SYSTEM + return unlock_pinentry (tmperr); +#endif + } + + full_pgmname = opt.pinentry_program; + if (!full_pgmname || !*full_pgmname) + full_pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PINENTRY); + if ( !(pgmname = strrchr (full_pgmname, '/'))) + pgmname = full_pgmname; + else + pgmname++; + + /* OS X needs the entire file name in argv[0], so that it can locate + the resource bundle. For other systems we stick to the usual + convention of supplying only the name of the program. */ +#ifdef __APPLE__ + argv[0] = full_pgmname; +#else /*!__APPLE__*/ + argv[0] = pgmname; +#endif /*__APPLE__*/ + + if (!opt.keep_display + && (value = session_env_getenv (ctrl->session_env, "DISPLAY"))) + { + argv[1] = "--display"; + argv[2] = value; + argv[3] = NULL; + } + else + argv[1] = NULL; + + i=0; + if (!opt.running_detached) + { + if (log_get_fd () != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); + no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr)); + } + no_close_list[i] = ASSUAN_INVALID_FD; + + rc = assuan_new (&ctx); + if (rc) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc)); + return rc; + } + /* We don't want to log the pinentry communication to make the logs + easier to read. We might want to add a new debug option to enable + pinentry logging. */ +#ifdef ASSUAN_NO_LOGGING + assuan_set_flag (ctx, ASSUAN_NO_LOGGING, !opt.debug_pinentry); +#endif + + /* Connect to the pinentry and perform initial handshaking. Note + that atfork is used to change the environment for pinentry. We + start the server in detached mode to suppress the console window + under Windows. */ + rc = assuan_pipe_connect (ctx, full_pgmname, argv, + no_close_list, atfork_cb, ctrl, + ASSUAN_PIPE_CONNECT_DETACHED); + if (rc) + { + log_error ("can't connect to the PIN entry module '%s': %s\n", + full_pgmname, gpg_strerror (rc)); + assuan_release (ctx); + return unlock_pinentry (gpg_error (GPG_ERR_NO_PIN_ENTRY)); + } + entry_ctx = ctx; + + if (DBG_IPC) + log_debug ("connection to PIN entry established\n"); + + value = session_env_getenv (ctrl->session_env, "PINENTRY_USER_DATA"); + if (value != NULL) + { + char *optstr; + if (asprintf (&optstr, "OPTION pinentry-user-data=%s", value) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION) + return unlock_pinentry (rc); + } + + rc = assuan_transact (entry_ctx, + opt.no_grab? "OPTION no-grab":"OPTION grab", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + + value = session_env_getenv (ctrl->session_env, "GPG_TTY"); + if (value) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttyname=%s", value) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + if (rc) + return unlock_pinentry (rc); + } + value = session_env_getenv (ctrl->session_env, "TERM"); + if (value) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttytype=%s", value) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + if (rc) + return unlock_pinentry (rc); + } + if (ctrl->lc_ctype) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + if (rc) + return unlock_pinentry (rc); + } + if (ctrl->lc_messages) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + if (rc) + return unlock_pinentry (rc); + } + + + if (opt.allow_external_cache) + { + /* Indicate to the pinentry that it may read from an external cache. + + It is essential that the pinentry respect this. If the + cached password is not up to date and retry == 1, then, using + a version of GPG Agent that doesn't support this, won't issue + another pin request and the user won't get a chance to + correct the password. */ + rc = assuan_transact (entry_ctx, "OPTION allow-external-password-cache", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION) + return unlock_pinentry (rc); + } + + if (opt.allow_emacs_pinentry) + { + /* Indicate to the pinentry that it may read passphrase through + Emacs minibuffer, if possible. */ + rc = assuan_transact (entry_ctx, "OPTION allow-emacs-prompt", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION) + return unlock_pinentry (rc); + } + + + { + /* Provide a few default strings for use by the pinentries. This + may help a pinentry to avoid implementing localization code. */ + static struct { const char *key, *value; int what; } tbl[] = { + /* TRANSLATORS: These are labels for buttons etc used in + Pinentries. An underscore indicates that the next letter + should be used as an accelerator. Double the underscore for + a literal one. The actual to be translated text starts after + the second vertical bar. Note that gpg-agent has been set to + utf-8 so that the strings are in the expected encoding. */ + { "ok", N_("|pinentry-label|_OK") }, + { "cancel", N_("|pinentry-label|_Cancel") }, + { "yes", N_("|pinentry-label|_Yes") }, + { "no", N_("|pinentry-label|_No") }, + { "prompt", N_("|pinentry-label|PIN:") }, + { "pwmngr", N_("|pinentry-label|_Save in password manager"), 1 }, + { "cf-visi",N_("Do you really want to make your " + "passphrase visible on the screen?") }, + { "tt-visi",N_("|pinentry-tt|Make passphrase visible") }, + { "tt-hide",N_("|pinentry-tt|Hide passphrase") }, + { NULL, NULL} + }; + char *optstr; + int idx; + const char *s, *s2; + + for (idx=0; tbl[idx].key; idx++) + { + if (!opt.allow_external_cache && tbl[idx].what == 1) + continue; /* No need for it. */ + s = L_(tbl[idx].value); + if (*s == '|' && (s2=strchr (s+1,'|'))) + s = s2+1; + if (asprintf (&optstr, "OPTION default-%s=%s", tbl[idx].key, s) < 0 ) + return unlock_pinentry (out_of_core ()); + assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + } + } + + /* Tell the pinentry that we would prefer that the given character + is used as the invisible character by the entry widget. */ + if (opt.pinentry_invisible_char) + { + char *optstr; + if ((optstr = xtryasprintf ("OPTION invisible-char=%s", + opt.pinentry_invisible_char))) + { + assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + /* We ignore errors because this is just a fancy thing and + older pinentries do not support this feature. */ + xfree (optstr); + } + } + + if (opt.pinentry_timeout) + { + char *optstr; + if ((optstr = xtryasprintf ("SETTIMEOUT %lu", opt.pinentry_timeout))) + { + assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + /* We ignore errors because this is just a fancy thing. */ + xfree (optstr); + } + } + + /* Tell the pinentry the name of a file it shall touch after having + messed with the tty. This is optional and only supported by + newer pinentries and thus we do no error checking. */ + tmpstr = opt.pinentry_touch_file; + if (tmpstr && !strcmp (tmpstr, "/dev/null")) + tmpstr = NULL; + else if (!tmpstr) + tmpstr = get_agent_socket_name (); + if (tmpstr) + { + char *optstr; + + if (asprintf (&optstr, "OPTION touch-file=%s", tmpstr ) < 0 ) + ; + else + { + assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + xfree (optstr); + } + } + + + /* Ask the pinentry for its version and flavor and streo that as a + * string in MB. This information is useful for helping users to + * figure out Pinentry problems. */ + { + membuf_t mb; + + init_membuf (&mb, 256); + if (assuan_transact (entry_ctx, "GETINFO flavor", + put_membuf_cb, &mb, NULL, NULL, NULL, NULL)) + put_membuf_str (&mb, "unknown"); + put_membuf_str (&mb, " "); + if (assuan_transact (entry_ctx, "GETINFO version", + put_membuf_cb, &mb, NULL, NULL, NULL, NULL)) + put_membuf_str (&mb, "unknown"); + put_membuf (&mb, "", 1); + flavor_version = get_membuf (&mb, NULL); + } + + + /* Now ask the Pinentry for its PID. If the Pinentry is new enough + it will send the pid back and we will use an inquire to notify + our client. The client may answer the inquiry either with END or + with CAN to cancel the pinentry. */ + rc = assuan_transact (entry_ctx, "GETINFO pid", + getinfo_pid_cb, &pinentry_pid, + NULL, NULL, NULL, NULL); + if (rc) + { + log_info ("You may want to update to a newer pinentry\n"); + rc = 0; + } + else if (!rc && (pid_t)pinentry_pid == (pid_t)(-1)) + log_error ("pinentry did not return a PID\n"); + else + { + rc = agent_inq_pinentry_launched (ctrl, pinentry_pid, flavor_version); + if (gpg_err_code (rc) == GPG_ERR_CANCELED + || gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) + return unlock_pinentry (gpg_err_make (GPG_ERR_SOURCE_DEFAULT, + gpg_err_code (rc))); + rc = 0; + } + + xfree (flavor_version); + + return 0; +} + + +/* Returns True if the pinentry is currently active. If WAITSECONDS is + greater than zero the function will wait for this many seconds + before returning. */ +int +pinentry_active_p (ctrl_t ctrl, int waitseconds) +{ + int err; + (void)ctrl; + + if (waitseconds > 0) + { + struct timespec abstime; + int rc; + + npth_clock_gettime (&abstime); + abstime.tv_sec += waitseconds; + err = npth_mutex_timedlock (&entry_lock, &abstime); + if (err) + { + if (err == ETIMEDOUT) + rc = gpg_error (GPG_ERR_TIMEOUT); + else + rc = gpg_error (GPG_ERR_INTERNAL); + return rc; + } + } + else + { + err = npth_mutex_trylock (&entry_lock); + if (err) + return gpg_error (GPG_ERR_LOCKED); + } + + err = npth_mutex_unlock (&entry_lock); + if (err) + log_error ("failed to release the entry lock at %d: %s\n", __LINE__, + strerror (errno)); + return 0; +} + + +static gpg_error_t +getpin_cb (void *opaque, const void *buffer, size_t length) +{ + struct entry_parm_s *parm = opaque; + + if (!buffer) + return 0; + + /* we expect the pin to fit on one line */ + if (parm->lines || length >= parm->size) + return gpg_error (GPG_ERR_ASS_TOO_MUCH_DATA); + + /* fixme: we should make sure that the assuan buffer is allocated in + secure memory or read the response byte by byte */ + memcpy (parm->buffer, buffer, length); + parm->buffer[length] = 0; + parm->lines++; + return 0; +} + + +static int +all_digitsp( const char *s) +{ + for (; *s && *s >= '0' && *s <= '9'; s++) + ; + return !*s; +} + + +/* Return a new malloced string by unescaping the string S. Escaping + is percent escaping and '+'/space mapping. A binary Nul will + silently be replaced by a 0xFF. Function returns NULL to indicate + an out of memory status. Parsing stops at the end of the string or + a white space character. */ +static char * +unescape_passphrase_string (const unsigned char *s) +{ + char *buffer, *d; + + buffer = d = xtrymalloc_secure (strlen ((const char*)s)+1); + if (!buffer) + return NULL; + while (*s && !spacep (s)) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d = xtoi_2 (s); + if (!*d) + *d = '\xff'; + d++; + s += 2; + } + else if (*s == '+') + { + *d++ = ' '; + s++; + } + else + *d++ = *s++; + } + *d = 0; + return buffer; +} + + +/* Estimate the quality of the passphrase PW and return a value in the + range 0..100. */ +static int +estimate_passphrase_quality (const char *pw) +{ + int goodlength = opt.min_passphrase_len + opt.min_passphrase_len/3; + int length; + const char *s; + + if (goodlength < 1) + return 0; + + for (length = 0, s = pw; *s; s++) + if (!spacep (s)) + length ++; + + if (length > goodlength) + return 100; + return ((length*10) / goodlength)*10; +} + + +/* Handle the QUALITY inquiry. */ +static gpg_error_t +inq_quality (void *opaque, const char *line) +{ + assuan_context_t ctx = opaque; + const char *s; + char *pin; + int rc; + int percent; + char numbuf[20]; + + if ((s = has_leading_keyword (line, "QUALITY"))) + { + pin = unescape_passphrase_string (s); + if (!pin) + rc = gpg_error_from_syserror (); + else + { + percent = estimate_passphrase_quality (pin); + if (check_passphrase_constraints (NULL, pin, NULL)) + percent = -percent; + snprintf (numbuf, sizeof numbuf, "%d", percent); + rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); + xfree (pin); + } + } + else + { + log_error ("unsupported inquiry '%s' from pinentry\n", line); + rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + } + + return rc; +} + + +/* Helper for agent_askpin and agent_get_passphrase. */ +static gpg_error_t +setup_qualitybar (ctrl_t ctrl) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + char *tmpstr, *tmpstr2; + const char *tooltip; + + (void)ctrl; + + /* TRANSLATORS: This string is displayed by Pinentry as the label + for the quality bar. */ + tmpstr = try_percent_escape (L_("Quality:"), "\t\r\n\f\v"); + snprintf (line, DIM(line), "SETQUALITYBAR %s", tmpstr? tmpstr:""); + xfree (tmpstr); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc == 103 /*(Old assuan error code)*/ + || gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) + ; /* Ignore Unknown Command from old Pinentry versions. */ + else if (rc) + return rc; + + tmpstr2 = gnupg_get_help_string ("pinentry.qualitybar.tooltip", 0); + if (tmpstr2) + tooltip = tmpstr2; + else + { + /* TRANSLATORS: This string is a tooltip, shown by pinentry when + hovering over the quality bar. Please use an appropriate + string to describe what this is about. The length of the + tooltip is limited to about 900 characters. If you do not + translate this entry, a default english text (see source) + will be used. */ + tooltip = L_("pinentry.qualitybar.tooltip"); + if (!strcmp ("pinentry.qualitybar.tooltip", tooltip)) + tooltip = ("The quality of the text entered above.\n" + "Please ask your administrator for " + "details about the criteria."); + } + tmpstr = try_percent_escape (tooltip, "\t\r\n\f\v"); + xfree (tmpstr2); + snprintf (line, DIM(line), "SETQUALITYBAR_TT %s", tmpstr? tmpstr:""); + xfree (tmpstr); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc == 103 /*(Old assuan error code)*/ + || gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) + ; /* Ignore Unknown Command from old pinentry versions. */ + else if (rc) + return rc; + + return 0; +} + +enum + { + PINENTRY_STATUS_CLOSE_BUTTON = 1 << 0, + PINENTRY_STATUS_PIN_REPEATED = 1 << 8, + PINENTRY_STATUS_PASSWORD_FROM_CACHE = 1 << 9 + }; + +/* Check the button_info line for a close action. Also check for the + PIN_REPEATED flag. */ +static gpg_error_t +pinentry_status_cb (void *opaque, const char *line) +{ + unsigned int *flag = opaque; + const char *args; + + if ((args = has_leading_keyword (line, "BUTTON_INFO"))) + { + if (!strcmp (args, "close")) + *flag |= PINENTRY_STATUS_CLOSE_BUTTON; + } + else if (has_leading_keyword (line, "PIN_REPEATED")) + { + *flag |= PINENTRY_STATUS_PIN_REPEATED; + } + else if (has_leading_keyword (line, "PASSWORD_FROM_CACHE")) + { + *flag |= PINENTRY_STATUS_PASSWORD_FROM_CACHE; + } + + return 0; +} + + + + +/* Call the Entry and ask for the PIN. We do check for a valid PIN + number here and repeat it as long as we have invalid formed + numbers. KEYINFO and CACHE_MODE are used to tell pinentry something + about the key. */ +gpg_error_t +agent_askpin (ctrl_t ctrl, + const char *desc_text, const char *prompt_text, + const char *initial_errtext, + struct pin_entry_info_s *pininfo, + const char *keyinfo, cache_mode_t cache_mode) +{ + gpg_error_t rc; + char line[ASSUAN_LINELENGTH]; + struct entry_parm_s parm; + const char *errtext = NULL; + int is_pin = 0; + int saveflag; + unsigned int pinentry_status; + + if (opt.batch) + return 0; /* fixme: we should return BAD PIN */ + + if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) + { + if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL) + return gpg_error (GPG_ERR_CANCELED); + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + { + unsigned char *passphrase; + size_t size; + + *pininfo->pin = 0; /* Reset the PIN. */ + rc = pinentry_loopback(ctrl, "PASSPHRASE", &passphrase, &size, + pininfo->max_length - 1); + if (rc) + return rc; + + memcpy(&pininfo->pin, passphrase, size); + xfree(passphrase); + pininfo->pin[size] = 0; + if (pininfo->check_cb) + { + /* More checks by utilizing the optional callback. */ + pininfo->cb_errtext = NULL; + rc = pininfo->check_cb (pininfo); + } + return rc; + } + return gpg_error(GPG_ERR_NO_PIN_ENTRY); + } + + if (!pininfo || pininfo->max_length < 1) + return gpg_error (GPG_ERR_INV_VALUE); + if (!desc_text && pininfo->min_digits) + desc_text = L_("Please enter your PIN, so that the secret key " + "can be unlocked for this session"); + else if (!desc_text) + desc_text = L_("Please enter your passphrase, so that the secret key " + "can be unlocked for this session"); + + if (prompt_text) + is_pin = !!strstr (prompt_text, "PIN"); + else + is_pin = desc_text && strstr (desc_text, "PIN"); + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + /* If we have a KEYINFO string and are normal, user, or ssh cache + mode, we tell that the Pinentry so it may use it for own caching + purposes. Most pinentries won't have this implemented and thus + we do not error out in this case. */ + if (keyinfo && (cache_mode == CACHE_MODE_NORMAL + || cache_mode == CACHE_MODE_USER + || cache_mode == CACHE_MODE_SSH)) + snprintf (line, DIM(line), "SETKEYINFO %c/%s", + cache_mode == CACHE_MODE_USER? 'u' : + cache_mode == CACHE_MODE_SSH? 's' : 'n', + keyinfo); + else + snprintf (line, DIM(line), "SETKEYINFO --clear"); + + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD) + return unlock_pinentry (rc); + + snprintf (line, DIM(line), "SETDESC %s", desc_text); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + + snprintf (line, DIM(line), "SETPROMPT %s", + prompt_text? prompt_text : is_pin? L_("PIN:") : L_("Passphrase:")); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + + /* If a passphrase quality indicator has been requested and a + minimum passphrase length has not been disabled, send the command + to the pinentry. */ + if (pininfo->with_qualitybar && opt.min_passphrase_len ) + { + rc = setup_qualitybar (ctrl); + if (rc) + return unlock_pinentry (rc); + } + + if (initial_errtext) + { + snprintf (line, DIM(line), "SETERROR %s", initial_errtext); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + } + + if (pininfo->with_repeat) + { + snprintf (line, DIM(line), "SETREPEATERROR %s", + L_("does not match - try again")); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + pininfo->with_repeat = 0; /* Pinentry does not support it. */ + } + pininfo->repeat_okay = 0; + + for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++) + { + memset (&parm, 0, sizeof parm); + parm.size = pininfo->max_length; + *pininfo->pin = 0; /* Reset the PIN. */ + parm.buffer = (unsigned char*)pininfo->pin; + + if (errtext) + { + /* TRANSLATORS: The string is appended to an error message in + the pinentry. The %s is the actual error message, the + two %d give the current and maximum number of tries. */ + snprintf (line, DIM(line), L_("SETERROR %s (try %d of %d)"), + errtext, pininfo->failed_tries+1, pininfo->max_tries); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + errtext = NULL; + } + + if (pininfo->with_repeat) + { + snprintf (line, DIM(line), "SETREPEAT %s", L_("Repeat:")); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + } + + saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL); + assuan_begin_confidential (entry_ctx); + pinentry_status = 0; + rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, + inq_quality, entry_ctx, + pinentry_status_cb, &pinentry_status); + assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag); + /* Most pinentries out in the wild return the old Assuan error code + for canceled which gets translated to an assuan Cancel error and + not to the code for a user cancel. Fix this here. */ + if (rc && gpg_err_source (rc) + && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + + /* Change error code in case the window close button was clicked + to cancel the operation. */ + if ((pinentry_status & PINENTRY_STATUS_CLOSE_BUTTON) + && gpg_err_code (rc) == GPG_ERR_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED); + + if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA) + errtext = is_pin? L_("PIN too long") + : L_("Passphrase too long"); + else if (rc) + return unlock_pinentry (rc); + + if (!errtext && pininfo->min_digits) + { + /* do some basic checks on the entered PIN. */ + if (!all_digitsp (pininfo->pin)) + errtext = L_("Invalid characters in PIN"); + else if (pininfo->max_digits + && strlen (pininfo->pin) > pininfo->max_digits) + errtext = L_("PIN too long"); + else if (strlen (pininfo->pin) < pininfo->min_digits) + errtext = L_("PIN too short"); + } + + if (!errtext && pininfo->check_cb) + { + /* More checks by utilizing the optional callback. */ + pininfo->cb_errtext = NULL; + rc = pininfo->check_cb (pininfo); + if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE + && pininfo->cb_errtext) + errtext = pininfo->cb_errtext; + else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE + || gpg_err_code (rc) == GPG_ERR_BAD_PIN) + errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase")); + else if (rc) + return unlock_pinentry (rc); + } + + if (!errtext) + { + if (pininfo->with_repeat + && (pinentry_status & PINENTRY_STATUS_PIN_REPEATED)) + pininfo->repeat_okay = 1; + return unlock_pinentry (0); /* okay, got a PIN or passphrase */ + } + + if ((pinentry_status & PINENTRY_STATUS_PASSWORD_FROM_CACHE)) + /* The password was read from the cache. Don't count this + against the retry count. */ + pininfo->failed_tries --; + } + + return unlock_pinentry (gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN + : GPG_ERR_BAD_PASSPHRASE)); +} + + + +/* Ask for the passphrase using the supplied arguments. The returned + passphrase needs to be freed by the caller. */ +int +agent_get_passphrase (ctrl_t ctrl, + char **retpass, const char *desc, const char *prompt, + const char *errtext, int with_qualitybar, + const char *keyinfo, cache_mode_t cache_mode) +{ + + int rc; + char line[ASSUAN_LINELENGTH]; + struct entry_parm_s parm; + int saveflag; + unsigned int pinentry_status; + + *retpass = NULL; + if (opt.batch) + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + + if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) + { + if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL) + return gpg_error (GPG_ERR_CANCELED); + + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + { + size_t size; + size_t len = ASSUAN_LINELENGTH/2; + + return pinentry_loopback (ctrl, "PASSPHRASE", + (unsigned char **)retpass, &size, len); + } + return gpg_error (GPG_ERR_NO_PIN_ENTRY); + } + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (!prompt) + prompt = desc && strstr (desc, "PIN")? L_("PIN:"): L_("Passphrase:"); + + + /* If we have a KEYINFO string and are normal, user, or ssh cache + mode, we tell that the Pinentry so it may use it for own caching + purposes. Most pinentries won't have this implemented and thus + we do not error out in this case. */ + if (keyinfo && (cache_mode == CACHE_MODE_NORMAL + || cache_mode == CACHE_MODE_USER + || cache_mode == CACHE_MODE_SSH)) + snprintf (line, DIM(line), "SETKEYINFO %c/%s", + cache_mode == CACHE_MODE_USER? 'u' : + cache_mode == CACHE_MODE_SSH? 's' : 'n', + keyinfo); + else + snprintf (line, DIM(line), "SETKEYINFO --clear"); + + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD) + return unlock_pinentry (rc); + + + if (desc) + snprintf (line, DIM(line), "SETDESC %s", desc); + else + snprintf (line, DIM(line), "RESET"); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + + snprintf (line, DIM(line), "SETPROMPT %s", prompt); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + + if (with_qualitybar && opt.min_passphrase_len) + { + rc = setup_qualitybar (ctrl); + if (rc) + return unlock_pinentry (rc); + } + + if (errtext) + { + snprintf (line, DIM(line), "SETERROR %s", errtext); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + } + + memset (&parm, 0, sizeof parm); + parm.size = ASSUAN_LINELENGTH/2 - 5; + parm.buffer = gcry_malloc_secure (parm.size+10); + if (!parm.buffer) + return unlock_pinentry (out_of_core ()); + + saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL); + assuan_begin_confidential (entry_ctx); + pinentry_status = 0; + rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, + inq_quality, entry_ctx, + pinentry_status_cb, &pinentry_status); + assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag); + /* Most pinentries out in the wild return the old Assuan error code + for canceled which gets translated to an assuan Cancel error and + not to the code for a user cancel. Fix this here. */ + if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + /* Change error code in case the window close button was clicked + to cancel the operation. */ + if ((pinentry_status & PINENTRY_STATUS_CLOSE_BUTTON) + && gpg_err_code (rc) == GPG_ERR_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED); + + if (rc) + xfree (parm.buffer); + else + *retpass = parm.buffer; + return unlock_pinentry (rc); +} + + + +/* Pop up the PIN-entry, display the text and the prompt and ask the + user to confirm this. We return 0 for success, ie. the user + confirmed it, GPG_ERR_NOT_CONFIRMED for what the text says or an + other error. If WITH_CANCEL it true an extra cancel button is + displayed to allow the user to easily return a GPG_ERR_CANCELED. + if the Pinentry does not support this, the user can still cancel by + closing the Pinentry window. */ +int +agent_get_confirmation (ctrl_t ctrl, + const char *desc, const char *ok, + const char *notok, int with_cancel) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) + { + if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL) + return gpg_error (GPG_ERR_CANCELED); + + return gpg_error (GPG_ERR_NO_PIN_ENTRY); + } + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (desc) + snprintf (line, DIM(line), "SETDESC %s", desc); + else + snprintf (line, DIM(line), "RESET"); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + /* Most pinentries out in the wild return the old Assuan error code + for canceled which gets translated to an assuan Cancel error and + not to the code for a user cancel. Fix this here. */ + if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + if (rc) + return unlock_pinentry (rc); + + if (ok) + { + snprintf (line, DIM(line), "SETOK %s", ok); + rc = assuan_transact (entry_ctx, + line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + } + if (notok) + { + /* Try to use the newer NOTOK feature if a cancel button is + requested. If no cancel button is requested we keep on using + the standard cancel. */ + if (with_cancel) + { + snprintf (line, DIM(line), "SETNOTOK %s", notok); + rc = assuan_transact (entry_ctx, + line, NULL, NULL, NULL, NULL, NULL, NULL); + } + else + rc = GPG_ERR_ASS_UNKNOWN_CMD; + + if (gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) + { + snprintf (line, DIM(line), "SETCANCEL %s", notok); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + } + if (rc) + return unlock_pinentry (rc); + } + + rc = assuan_transact (entry_ctx, "CONFIRM", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + return unlock_pinentry (rc); +} + + + +/* Pop up the PINentry, display the text DESC and a button with the + text OK_BTN (which may be NULL to use the default of "OK") and wait + for the user to hit this button. The return value is not + relevant. */ +int +agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) + return gpg_error (GPG_ERR_CANCELED); + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (desc) + snprintf (line, DIM(line), "SETDESC %s", desc); + else + snprintf (line, DIM(line), "RESET"); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + /* Most pinentries out in the wild return the old Assuan error code + for canceled which gets translated to an assuan Cancel error and + not to the code for a user cancel. Fix this here. */ + if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + if (rc) + return unlock_pinentry (rc); + + if (ok_btn) + { + snprintf (line, DIM(line), "SETOK %s", ok_btn); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, + NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + } + + rc = assuan_transact (entry_ctx, "CONFIRM --one-button", NULL, NULL, NULL, + NULL, NULL, NULL); + if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + return unlock_pinentry (rc); +} + + +/* The thread running the popup message. */ +static void * +popup_message_thread (void *arg) +{ + (void)arg; + + /* We use the --one-button hack instead of the MESSAGE command to + allow the use of old Pinentries. Those old Pinentries will then + show an additional Cancel button but that is mostly a visual + annoyance. */ + assuan_transact (entry_ctx, "CONFIRM --one-button", + NULL, NULL, NULL, NULL, NULL, NULL); + popup_finished = 1; + return NULL; +} + + +/* Pop up a message window similar to the confirm one but keep it open + until agent_popup_message_stop has been called. It is crucial for + the caller to make sure that the stop function gets called as soon + as the message is not anymore required because the message is + system modal and all other attempts to use the pinentry will fail + (after a timeout). */ +int +agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + npth_attr_t tattr; + int err; + + if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) + return gpg_error (GPG_ERR_CANCELED); + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (desc) + snprintf (line, DIM(line), "SETDESC %s", desc); + else + snprintf (line, DIM(line), "RESET"); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + + if (ok_btn) + { + snprintf (line, DIM(line), "SETOK %s", ok_btn); + rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL); + if (rc) + return unlock_pinentry (rc); + } + + err = npth_attr_init (&tattr); + if (err) + return unlock_pinentry (gpg_error_from_errno (err)); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + + popup_finished = 0; + err = npth_create (&popup_tid, &tattr, popup_message_thread, NULL); + npth_attr_destroy (&tattr); + if (err) + { + rc = gpg_error_from_errno (err); + log_error ("error spawning popup message handler: %s\n", + strerror (err) ); + return unlock_pinentry (rc); + } + npth_setname_np (popup_tid, "popup-message"); + + return 0; +} + +/* Close a popup window. */ +void +agent_popup_message_stop (ctrl_t ctrl) +{ + int rc; + pid_t pid; + + (void)ctrl; + + if (!popup_tid || !entry_ctx) + { + log_debug ("agent_popup_message_stop called with no active popup\n"); + return; + } + + pid = assuan_get_pid (entry_ctx); + if (pid == (pid_t)(-1)) + ; /* No pid available can't send a kill. */ + else if (popup_finished) + ; /* Already finished and ready for joining. */ +#ifdef HAVE_W32_SYSTEM + /* Older versions of assuan set PID to 0 on Windows to indicate an + invalid value. */ + else if (pid != (pid_t) INVALID_HANDLE_VALUE + && pid != 0) + { + HANDLE process = (HANDLE) pid; + + /* Arbitrary error code. */ + TerminateProcess (process, 1); + } +#else + else if (pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) + { /* The daemon already died. No need to send a kill. However + because we already waited for the process, we need to tell + assuan that it should not wait again (done by + unlock_pinentry). */ + if (rc == pid) + assuan_set_flag (entry_ctx, ASSUAN_NO_WAITPID, 1); + } + else if (pid > 0) + kill (pid, SIGINT); +#endif + + /* Now wait for the thread to terminate. */ + rc = npth_join (popup_tid, NULL); + if (rc) + log_debug ("agent_popup_message_stop: pth_join failed: %s\n", + strerror (rc)); + /* Thread IDs are opaque, but we try our best here by resetting it + to the same content that a static global variable has. */ + memset (&popup_tid, '\0', sizeof (popup_tid)); + entry_owner = NULL; + + /* Now we can close the connection. */ + unlock_pinentry (0); +} + +int +agent_clear_passphrase (ctrl_t ctrl, + const char *keyinfo, cache_mode_t cache_mode) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + if (! (keyinfo && (cache_mode == CACHE_MODE_NORMAL + || cache_mode == CACHE_MODE_USER + || cache_mode == CACHE_MODE_SSH))) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + snprintf (line, DIM(line), "CLEARPASSPHRASE %c/%s", + cache_mode == CACHE_MODE_USER? 'u' : + cache_mode == CACHE_MODE_SSH? 's' : 'n', + keyinfo); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + + return unlock_pinentry (rc); +} diff --git a/agent/call-scd.c b/agent/call-scd.c new file mode 100644 index 0000000..ba59c18 --- /dev/null +++ b/agent/call-scd.c @@ -0,0 +1,1270 @@ +/* call-scd.c - fork of the scdaemon to do SC operations + * Copyright (C) 2001, 2002, 2005, 2007, 2010, + * 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include +#include +#ifndef HAVE_W32_SYSTEM +#include +#endif +#include + +#include "agent.h" +#include + +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + +/* Definition of module local data of the CTRL structure. */ +struct scd_local_s +{ + /* We keep a list of all allocated context with a an achnor at + SCD_LOCAL_LIST (see below). */ + struct scd_local_s *next_local; + + /* We need to get back to the ctrl object actually referencing this + structure. This is really an awkward way of enumerint the lcoal + contects. A much cleaner way would be to keep a global list of + ctrl objects to enumerate them. */ + ctrl_t ctrl_backlink; + + assuan_context_t ctx; /* NULL or session context for the SCdaemon + used with this connection. */ + int locked; /* This flag is used to assert proper use of + start_scd and unlock_scd. */ + +}; + + +/* Callback parameter for learn card */ +struct learn_parm_s +{ + void (*kpinfo_cb)(void*, const char *); + void *kpinfo_cb_arg; + void (*certinfo_cb)(void*, const char *); + void *certinfo_cb_arg; + void (*sinfo_cb)(void*, const char *, size_t, const char *); + void *sinfo_cb_arg; +}; + +struct inq_needpin_s +{ + assuan_context_t ctx; + int (*getpin_cb)(void *, const char *, char*, size_t); + void *getpin_cb_arg; + assuan_context_t passthru; /* If not NULL, pass unknown inquiries + up to the caller. */ + int any_inq_seen; +}; + + +/* To keep track of all active SCD contexts, we keep a linked list + anchored at this variable. */ +static struct scd_local_s *scd_local_list; + +/* A Mutex used inside the start_scd function. */ +static npth_mutex_t start_scd_lock; + +/* A malloced string with the name of the socket to be used for + additional connections. May be NULL if not provided by + SCdaemon. */ +static char *socket_name; + +/* The context of the primary connection. This is also used as a flag + to indicate whether the scdaemon has been started. */ +static assuan_context_t primary_scd_ctx; + +/* To allow reuse of the primary connection, the following flag is set + to true if the primary context has been reset and is not in use by + any connection. */ +static int primary_scd_ctx_reusable; + + + +/* Local prototypes. */ + + + + +/* This function must be called once to initialize this module. This + has to be done before a second thread is spawned. We can't do the + static initialization because NPth emulation code might not be able + to do a static init; in particular, it is not possible for W32. */ +void +initialize_module_call_scd (void) +{ + static int initialized; + int err; + + if (!initialized) + { + err = npth_mutex_init (&start_scd_lock, NULL); + if (err) + log_fatal ("error initializing mutex: %s\n", strerror (err)); + initialized = 1; + } +} + + +/* This function may be called to print infromation pertaining to the + current state of this module to the log. */ +void +agent_scd_dump_state (void) +{ + log_info ("agent_scd_dump_state: primary_scd_ctx=%p pid=%ld reusable=%d\n", + primary_scd_ctx, + (long)assuan_get_pid (primary_scd_ctx), + primary_scd_ctx_reusable); + if (socket_name) + log_info ("agent_scd_dump_state: socket='%s'\n", socket_name); +} + + +/* The unlock_scd function shall be called after having accessed the + SCD. It is currently not very useful but gives an opportunity to + keep track of connections currently calling SCD. Note that the + "lock" operation is done by the start_scd() function which must be + called and error checked before any SCD operation. CTRL is the + usual connection context and RC the error code to be passed trhough + the function. */ +static int +unlock_scd (ctrl_t ctrl, int rc) +{ + if (ctrl->scd_local->locked != 1) + { + log_error ("unlock_scd: invalid lock count (%d)\n", + ctrl->scd_local->locked); + if (!rc) + rc = gpg_error (GPG_ERR_INTERNAL); + } + ctrl->scd_local->locked = 0; + return rc; +} + +/* To make sure we leave no secrets in our image after forking of the + scdaemon, we use this callback. */ +static void +atfork_cb (void *opaque, int where) +{ + (void)opaque; + + if (!where) + gcry_control (GCRYCTL_TERM_SECMEM); +} + + +/* Fork off the SCdaemon if this has not already been done. Lock the + daemon and make sure that a proper context has been setup in CTRL. + This function might also lock the daemon, which means that the + caller must call unlock_scd after this function has returned + success and the actual Assuan transaction been done. */ +static int +start_scd (ctrl_t ctrl) +{ + gpg_error_t err = 0; + const char *pgmname; + assuan_context_t ctx = NULL; + const char *argv[5]; + assuan_fd_t no_close_list[3]; + int i; + int rc; + char *abs_homedir = NULL; + + if (opt.disable_scdaemon) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + /* If this is the first call for this session, setup the local data + structure. */ + if (!ctrl->scd_local) + { + ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local); + if (!ctrl->scd_local) + return gpg_error_from_syserror (); + ctrl->scd_local->ctrl_backlink = ctrl; + ctrl->scd_local->next_local = scd_local_list; + scd_local_list = ctrl->scd_local; + } + + + /* Assert that the lock count is as expected. */ + if (ctrl->scd_local->locked) + { + log_error ("start_scd: invalid lock count (%d)\n", + ctrl->scd_local->locked); + return gpg_error (GPG_ERR_INTERNAL); + } + ctrl->scd_local->locked++; + + if (ctrl->scd_local->ctx) + return 0; /* Okay, the context is fine. We used to test for an + alive context here and do an disconnect. Now that we + have a ticker function to check for it, it is easier + not to check here but to let the connection run on an + error instead. */ + + + /* We need to protect the following code. */ + rc = npth_mutex_lock (&start_scd_lock); + if (rc) + { + log_error ("failed to acquire the start_scd lock: %s\n", + strerror (rc)); + return gpg_error (GPG_ERR_INTERNAL); + } + + /* Check whether the pipe server has already been started and in + this case either reuse a lingering pipe connection or establish a + new socket based one. */ + if (primary_scd_ctx && primary_scd_ctx_reusable) + { + ctx = primary_scd_ctx; + primary_scd_ctx_reusable = 0; + if (opt.verbose) + log_info ("new connection to SCdaemon established (reusing)\n"); + goto leave; + } + + rc = assuan_new (&ctx); + if (rc) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc)); + err = rc; + goto leave; + } + + if (socket_name) + { + rc = assuan_socket_connect (ctx, socket_name, 0, 0); + if (rc) + { + log_error ("can't connect to socket '%s': %s\n", + socket_name, gpg_strerror (rc)); + err = gpg_error (GPG_ERR_NO_SCDAEMON); + goto leave; + } + + if (opt.verbose) + log_info ("new connection to SCdaemon established\n"); + goto leave; + } + + if (primary_scd_ctx) + { + log_info ("SCdaemon is running but won't accept further connections\n"); + err = gpg_error (GPG_ERR_NO_SCDAEMON); + goto leave; + } + + /* Nope, it has not been started. Fire it up now. */ + if (opt.verbose) + log_info ("no running SCdaemon - starting it\n"); + + if (fflush (NULL)) + { +#ifndef HAVE_W32_SYSTEM + err = gpg_error_from_syserror (); +#endif + log_error ("error flushing pending output: %s\n", strerror (errno)); + /* At least Windows XP fails here with EBADF. According to docs + and Wine an fflush(NULL) is the same as _flushall. However + the Wime implementaion does not flush stdin,stdout and stderr + - see above. Lets try to ignore the error. */ +#ifndef HAVE_W32_SYSTEM + goto leave; +#endif + } + + if (!opt.scdaemon_program || !*opt.scdaemon_program) + opt.scdaemon_program = gnupg_module_name (GNUPG_MODULE_NAME_SCDAEMON); + if ( !(pgmname = strrchr (opt.scdaemon_program, '/'))) + pgmname = opt.scdaemon_program; + else + pgmname++; + + argv[0] = pgmname; + argv[1] = "--multi-server"; + if (gnupg_default_homedir_p ()) + argv[2] = NULL; + else + { + abs_homedir = make_absfilename_try (gnupg_homedir (), NULL); + if (!abs_homedir) + { + log_error ("error building filename: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + goto leave; + } + + argv[2] = "--homedir"; + argv[3] = abs_homedir; + argv[4] = NULL; + } + + i=0; + if (!opt.running_detached) + { + if (log_get_fd () != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); + no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr)); + } + no_close_list[i] = ASSUAN_INVALID_FD; + + /* Connect to the scdaemon and perform initial handshaking. Use + detached flag so that under Windows SCDAEMON does not show up a + new window. */ + rc = assuan_pipe_connect (ctx, opt.scdaemon_program, argv, + no_close_list, atfork_cb, NULL, + ASSUAN_PIPE_CONNECT_DETACHED); + if (rc) + { + log_error ("can't connect to the SCdaemon: %s\n", + gpg_strerror (rc)); + err = gpg_error (GPG_ERR_NO_SCDAEMON); + goto leave; + } + + if (opt.verbose) + log_debug ("first connection to SCdaemon established\n"); + + + /* Get the name of the additional socket opened by scdaemon. */ + { + membuf_t data; + unsigned char *databuf; + size_t datalen; + + xfree (socket_name); + socket_name = NULL; + init_membuf (&data, 256); + assuan_transact (ctx, "GETINFO socket_name", + put_membuf_cb, &data, NULL, NULL, NULL, NULL); + + databuf = get_membuf (&data, &datalen); + if (databuf && datalen) + { + socket_name = xtrymalloc (datalen + 1); + if (!socket_name) + log_error ("warning: can't store socket name: %s\n", + strerror (errno)); + else + { + memcpy (socket_name, databuf, datalen); + socket_name[datalen] = 0; + if (DBG_IPC) + log_debug ("additional connections at '%s'\n", socket_name); + } + } + xfree (databuf); + } + + /* Tell the scdaemon we want him to send us an event signal. We + don't support this for W32CE. */ +#ifndef HAVE_W32CE_SYSTEM + if (opt.sigusr2_enabled) + { + char buf[100]; + +#ifdef HAVE_W32_SYSTEM + snprintf (buf, sizeof buf, "OPTION event-signal=%lx", + (unsigned long)get_agent_scd_notify_event ()); +#else + snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2); +#endif + assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL); + } +#endif /*HAVE_W32CE_SYSTEM*/ + + primary_scd_ctx = ctx; + primary_scd_ctx_reusable = 0; + + leave: + xfree (abs_homedir); + if (err) + { + unlock_scd (ctrl, err); + if (ctx) + assuan_release (ctx); + } + else + { + ctrl->scd_local->ctx = ctx; + } + rc = npth_mutex_unlock (&start_scd_lock); + if (rc) + log_error ("failed to release the start_scd lock: %s\n", strerror (rc)); + return err; +} + + +/* Check whether the SCdaemon is active. This is a fast check without + any locking and might give a wrong result if another thread is about + to start the daemon or the daemon is about to be stopped.. */ +int +agent_scd_check_running (void) +{ + return !!primary_scd_ctx; +} + + +/* Check whether the Scdaemon is still alive and clean it up if not. */ +void +agent_scd_check_aliveness (void) +{ + pid_t pid; +#ifdef HAVE_W32_SYSTEM + DWORD rc; +#else + int rc; +#endif + struct timespec abstime; + int err; + + if (!primary_scd_ctx) + return; /* No scdaemon running. */ + + /* This is not a critical function so we use a short timeout while + acquiring the lock. */ + npth_clock_gettime (&abstime); + abstime.tv_sec += 1; + err = npth_mutex_timedlock (&start_scd_lock, &abstime); + if (err) + { + if (err == ETIMEDOUT) + { + if (opt.verbose > 1) + log_info ("failed to acquire the start_scd lock while" + " doing an aliveness check: %s\n", strerror (err)); + } + else + log_error ("failed to acquire the start_scd lock while" + " doing an aliveness check: %s\n", strerror (err)); + return; + } + + if (primary_scd_ctx) + { + pid = assuan_get_pid (primary_scd_ctx); +#ifdef HAVE_W32_SYSTEM + /* If we have a PID we disconnect if either GetExitProcessCode + fails or if ir returns the exit code of the scdaemon. 259 is + the error code for STILL_ALIVE. */ + if (pid != (pid_t)(void*)(-1) && pid + && (!GetExitCodeProcess ((HANDLE)pid, &rc) || rc != 259)) +#else + if (pid != (pid_t)(-1) && pid + && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) +#endif + { + /* Okay, scdaemon died. Disconnect the primary connection + now but take care that it won't do another wait. Also + cleanup all other connections and release their + resources. The next use will start a new daemon then. + Due to the use of the START_SCD_LOCAL we are sure that + none of these context are actually in use. */ + struct scd_local_s *sl; + + assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1); + assuan_release (primary_scd_ctx); + + for (sl=scd_local_list; sl; sl = sl->next_local) + { + if (sl->ctx) + { + if (sl->ctx != primary_scd_ctx) + assuan_release (sl->ctx); + sl->ctx = NULL; + } + } + + primary_scd_ctx = NULL; + primary_scd_ctx_reusable = 0; + + xfree (socket_name); + socket_name = NULL; + } + } + + err = npth_mutex_unlock (&start_scd_lock); + if (err) + log_error ("failed to release the start_scd lock while" + " doing the aliveness check: %s\n", strerror (err)); +} + + + +/* Reset the SCD if it has been used. Actually it is not a reset but + a cleanup of resources used by the current connection. */ +int +agent_reset_scd (ctrl_t ctrl) +{ + if (ctrl->scd_local) + { + if (ctrl->scd_local->ctx) + { + /* We can't disconnect the primary context because libassuan + does a waitpid on it and thus the system would hang. + Instead we send a reset and keep that connection for + reuse. */ + if (ctrl->scd_local->ctx == primary_scd_ctx) + { + /* Send a RESTART to the SCD. This is required for the + primary connection as a kind of virtual EOF; we don't + have another way to tell it that the next command + should be viewed as if a new connection has been + made. For the non-primary connections this is not + needed as we simply close the socket. We don't check + for an error here because the RESTART may fail for + example if the scdaemon has already been terminated. + Anyway, we need to set the reusable flag to make sure + that the aliveness check can clean it up. */ + assuan_transact (primary_scd_ctx, "RESTART", + NULL, NULL, NULL, NULL, NULL, NULL); + primary_scd_ctx_reusable = 1; + } + else + assuan_release (ctrl->scd_local->ctx); + ctrl->scd_local->ctx = NULL; + } + + /* Remove the local context from our list and release it. */ + if (!scd_local_list) + BUG (); + else if (scd_local_list == ctrl->scd_local) + scd_local_list = ctrl->scd_local->next_local; + else + { + struct scd_local_s *sl; + + for (sl=scd_local_list; sl->next_local; sl = sl->next_local) + if (sl->next_local == ctrl->scd_local) + break; + if (!sl->next_local) + BUG (); + sl->next_local = ctrl->scd_local->next_local; + } + xfree (ctrl->scd_local); + ctrl->scd_local = NULL; + } + + return 0; +} + + + +static gpg_error_t +learn_status_cb (void *opaque, const char *line) +{ + struct learn_parm_s *parm = opaque; + const char *keyword = line; + int keywordlen; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + if (keywordlen == 8 && !memcmp (keyword, "CERTINFO", keywordlen)) + { + parm->certinfo_cb (parm->certinfo_cb_arg, line); + } + else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) + { + parm->kpinfo_cb (parm->kpinfo_cb_arg, line); + } + else if (keywordlen && *line) + { + parm->sinfo_cb (parm->sinfo_cb_arg, keyword, keywordlen, line); + } + + return 0; +} + +/* Perform the LEARN command and return a list of all private keys + stored on the card. */ +int +agent_card_learn (ctrl_t ctrl, + void (*kpinfo_cb)(void*, const char *), + void *kpinfo_cb_arg, + void (*certinfo_cb)(void*, const char *), + void *certinfo_cb_arg, + void (*sinfo_cb)(void*, const char *, size_t, const char *), + void *sinfo_cb_arg) +{ + int rc; + struct learn_parm_s parm; + + rc = start_scd (ctrl); + if (rc) + return rc; + + memset (&parm, 0, sizeof parm); + parm.kpinfo_cb = kpinfo_cb; + parm.kpinfo_cb_arg = kpinfo_cb_arg; + parm.certinfo_cb = certinfo_cb; + parm.certinfo_cb_arg = certinfo_cb_arg; + parm.sinfo_cb = sinfo_cb; + parm.sinfo_cb_arg = sinfo_cb_arg; + rc = assuan_transact (ctrl->scd_local->ctx, "LEARN --force", + NULL, NULL, NULL, NULL, + learn_status_cb, &parm); + if (rc) + return unlock_scd (ctrl, rc); + + return unlock_scd (ctrl, 0); +} + + + +static gpg_error_t +get_serialno_cb (void *opaque, const char *line) +{ + char **serialno = opaque; + const char *keyword = line; + const char *s; + int keywordlen, n; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) + { + if (*serialno) + return gpg_error (GPG_ERR_CONFLICT); /* Unexpected status line. */ + for (n=0,s=line; hexdigitp (s); s++, n++) + ; + if (!n || (n&1)|| !(spacep (s) || !*s) ) + return gpg_error (GPG_ERR_ASS_PARAMETER); + *serialno = xtrymalloc (n+1); + if (!*serialno) + return out_of_core (); + memcpy (*serialno, line, n); + (*serialno)[n] = 0; + } + + return 0; +} + +/* Return the serial number of the card or an appropriate error. The + serial number is returned as a hexstring. */ +int +agent_card_serialno (ctrl_t ctrl, char **r_serialno) +{ + int rc; + char *serialno = NULL; + + rc = start_scd (ctrl); + if (rc) + return rc; + + rc = assuan_transact (ctrl->scd_local->ctx, "SERIALNO", + NULL, NULL, NULL, NULL, + get_serialno_cb, &serialno); + if (rc) + { + xfree (serialno); + return unlock_scd (ctrl, rc); + } + *r_serialno = serialno; + return unlock_scd (ctrl, 0); +} + + + + +/* Handle the NEEDPIN inquiry. */ +static gpg_error_t +inq_needpin (void *opaque, const char *line) +{ + struct inq_needpin_s *parm = opaque; + const char *s; + char *pin; + size_t pinlen; + int rc; + + parm->any_inq_seen = 1; + if ((s = has_leading_keyword (line, "NEEDPIN"))) + { + line = s; + pinlen = 90; + pin = gcry_malloc_secure (pinlen); + if (!pin) + return out_of_core (); + + rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen); + if (!rc) + rc = assuan_send_data (parm->ctx, pin, pinlen); + xfree (pin); + } + else if ((s = has_leading_keyword (line, "POPUPPINPADPROMPT"))) + { + rc = parm->getpin_cb (parm->getpin_cb_arg, s, NULL, 1); + } + else if ((s = has_leading_keyword (line, "DISMISSPINPADPROMPT"))) + { + rc = parm->getpin_cb (parm->getpin_cb_arg, "", NULL, 0); + } + else if (parm->passthru) + { + unsigned char *value; + size_t valuelen; + int rest; + int needrest = !strncmp (line, "KEYDATA", 8); + + /* Pass the inquiry up to our caller. We limit the maximum + amount to an arbitrary value. As we know that the KEYDATA + enquiry is pretty sensitive we disable logging then */ + if ((rest = (needrest + && !assuan_get_flag (parm->passthru, ASSUAN_CONFIDENTIAL)))) + assuan_begin_confidential (parm->passthru); + rc = assuan_inquire (parm->passthru, line, &value, &valuelen, 8096); + if (rest) + assuan_end_confidential (parm->passthru); + if (!rc) + { + if ((rest = (needrest + && !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL)))) + assuan_begin_confidential (parm->ctx); + rc = assuan_send_data (parm->ctx, value, valuelen); + if (rest) + assuan_end_confidential (parm->ctx); + xfree (value); + } + else + log_error ("error forwarding inquiry '%s': %s\n", + line, gpg_strerror (rc)); + } + else + { + log_error ("unsupported inquiry '%s'\n", line); + rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + } + + return rc; +} + + +/* Helper returning a command option to describe the used hash + algorithm. See scd/command.c:cmd_pksign. */ +static const char * +hash_algo_option (int algo) +{ + switch (algo) + { + case GCRY_MD_MD5 : return "--hash=md5"; + case GCRY_MD_RMD160: return "--hash=rmd160"; + case GCRY_MD_SHA1 : return "--hash=sha1"; + case GCRY_MD_SHA224: return "--hash=sha224"; + case GCRY_MD_SHA256: return "--hash=sha256"; + case GCRY_MD_SHA384: return "--hash=sha384"; + case GCRY_MD_SHA512: return "--hash=sha512"; + default: return ""; + } +} + + +static gpg_error_t +cancel_inquire (ctrl_t ctrl, gpg_error_t rc) +{ + gpg_error_t oldrc = rc; + + /* The inquire callback was called and transact returned a + cancel error. We assume that the inquired process sent a + CANCEL. The passthrough code is not able to pass on the + CANCEL and thus scdaemon would stuck on this. As a + workaround we send a CANCEL now. */ + rc = assuan_write_line (ctrl->scd_local->ctx, "CAN"); + if (!rc) { + char *line; + size_t len; + + rc = assuan_read_line (ctrl->scd_local->ctx, &line, &len); + if (!rc) + rc = oldrc; + } + + return rc; +} + +/* Create a signature using the current card. MDALGO is either 0 or + gives the digest algorithm. */ +int +agent_card_pksign (ctrl_t ctrl, + const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + int mdalgo, + const unsigned char *indata, size_t indatalen, + unsigned char **r_buf, size_t *r_buflen) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + membuf_t data; + struct inq_needpin_s inqparm; + + *r_buf = NULL; + rc = start_scd (ctrl); + if (rc) + return rc; + + if (indatalen*2 + 50 > DIM(line)) + return unlock_scd (ctrl, gpg_error (GPG_ERR_GENERAL)); + + bin2hex (indata, indatalen, stpcpy (line, "SETDATA ")); + + rc = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_scd (ctrl, rc); + + init_membuf (&data, 1024); + inqparm.ctx = ctrl->scd_local->ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + inqparm.passthru = 0; + inqparm.any_inq_seen = 0; + if (ctrl->use_auth_call) + snprintf (line, sizeof line, "PKAUTH %s", keyid); + else + snprintf (line, sizeof line, "PKSIGN %s %s", + hash_algo_option (mdalgo), keyid); + rc = assuan_transact (ctrl->scd_local->ctx, line, + put_membuf_cb, &data, + inq_needpin, &inqparm, + NULL, NULL); + if (inqparm.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED || + gpg_err_code(rc) == GPG_ERR_ASS_CANCELED)) + rc = cancel_inquire (ctrl, rc); + + if (rc) + { + size_t len; + + xfree (get_membuf (&data, &len)); + return unlock_scd (ctrl, rc); + } + + *r_buf = get_membuf (&data, r_buflen); + return unlock_scd (ctrl, 0); +} + + + + +/* Check whether there is any padding info from scdaemon. */ +static gpg_error_t +padding_info_cb (void *opaque, const char *line) +{ + int *r_padding = opaque; + const char *s; + + if ((s=has_leading_keyword (line, "PADDING"))) + { + *r_padding = atoi (s); + } + + return 0; +} + + +/* Decipher INDATA using the current card. Note that the returned + value is not an s-expression but the raw data as returned by + scdaemon. The padding information is stored at R_PADDING with -1 + for not known. */ +int +agent_card_pkdecrypt (ctrl_t ctrl, + const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen, int *r_padding) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + membuf_t data; + struct inq_needpin_s inqparm; + size_t len; + + *r_buf = NULL; + *r_padding = -1; /* Unknown. */ + rc = start_scd (ctrl); + if (rc) + return rc; + + /* FIXME: use secure memory where appropriate */ + + for (len = 0; len < indatalen;) + { + p = stpcpy (line, "SETDATA "); + if (len) + p = stpcpy (p, "--append "); + for (i=0; len < indatalen && (i*2 < DIM(line)-50); i++, len++) + { + sprintf (p, "%02X", indata[len]); + p += 2; + } + rc = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_scd (ctrl, rc); + } + + init_membuf (&data, 1024); + inqparm.ctx = ctrl->scd_local->ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + inqparm.passthru = 0; + inqparm.any_inq_seen = 0; + snprintf (line, DIM(line), "PKDECRYPT %s", keyid); + rc = assuan_transact (ctrl->scd_local->ctx, line, + put_membuf_cb, &data, + inq_needpin, &inqparm, + padding_info_cb, r_padding); + if (inqparm.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED || + gpg_err_code(rc) == GPG_ERR_ASS_CANCELED)) + rc = cancel_inquire (ctrl, rc); + + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (ctrl, rc); + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM)); + + return unlock_scd (ctrl, 0); +} + + + +/* Read a certificate with ID into R_BUF and R_BUFLEN. */ +int +agent_card_readcert (ctrl_t ctrl, + const char *id, char **r_buf, size_t *r_buflen) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + + *r_buf = NULL; + rc = start_scd (ctrl); + if (rc) + return rc; + + init_membuf (&data, 1024); + snprintf (line, DIM(line), "READCERT %s", id); + rc = assuan_transact (ctrl->scd_local->ctx, line, + put_membuf_cb, &data, + NULL, NULL, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (ctrl, rc); + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM)); + + return unlock_scd (ctrl, 0); +} + + + +/* Read a key with ID and return it in an allocate buffer pointed to + by r_BUF as a valid S-expression. */ +int +agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len, buflen; + + *r_buf = NULL; + rc = start_scd (ctrl); + if (rc) + return rc; + + init_membuf (&data, 1024); + snprintf (line, DIM(line), "READKEY %s", id); + rc = assuan_transact (ctrl->scd_local->ctx, line, + put_membuf_cb, &data, + NULL, NULL, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (ctrl, rc); + } + *r_buf = get_membuf (&data, &buflen); + if (!*r_buf) + return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM)); + + if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL)) + { + xfree (*r_buf); *r_buf = NULL; + return unlock_scd (ctrl, gpg_error (GPG_ERR_INV_VALUE)); + } + + return unlock_scd (ctrl, 0); +} + + +struct writekey_parm_s +{ + assuan_context_t ctx; + int (*getpin_cb)(void *, const char *, char*, size_t); + void *getpin_cb_arg; + assuan_context_t passthru; + int any_inq_seen; + /**/ + const unsigned char *keydata; + size_t keydatalen; +}; + +/* Handle a KEYDATA inquiry. Note, we only send the data, + assuan_transact takes care of flushing and writing the end */ +static gpg_error_t +inq_writekey_parms (void *opaque, const char *line) +{ + struct writekey_parm_s *parm = opaque; + + if (has_leading_keyword (line, "KEYDATA")) + return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen); + else + return inq_needpin (opaque, line); +} + + +int +agent_card_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, size_t keydatalen, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + struct writekey_parm_s parms; + + (void)serialno; + rc = start_scd (ctrl); + if (rc) + return rc; + + snprintf (line, DIM(line), "WRITEKEY %s%s", force ? "--force " : "", id); + parms.ctx = ctrl->scd_local->ctx; + parms.getpin_cb = getpin_cb; + parms.getpin_cb_arg = getpin_cb_arg; + parms.passthru = 0; + parms.any_inq_seen = 0; + parms.keydata = keydata; + parms.keydatalen = keydatalen; + + rc = assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL, + inq_writekey_parms, &parms, NULL, NULL); + if (parms.any_inq_seen && (gpg_err_code(rc) == GPG_ERR_CANCELED || + gpg_err_code(rc) == GPG_ERR_ASS_CANCELED)) + rc = cancel_inquire (ctrl, rc); + return unlock_scd (ctrl, rc); +} + +/* Type used with the card_getattr_cb. */ +struct card_getattr_parm_s { + const char *keyword; /* Keyword to look for. */ + size_t keywordlen; /* strlen of KEYWORD. */ + char *data; /* Malloced and unescaped data. */ + int error; /* ERRNO value or 0 on success. */ +}; + +/* Callback function for agent_card_getattr. */ +static gpg_error_t +card_getattr_cb (void *opaque, const char *line) +{ + struct card_getattr_parm_s *parm = opaque; + const char *keyword = line; + int keywordlen; + + if (parm->data) + return 0; /* We want only the first occurrence. */ + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == parm->keywordlen + && !memcmp (keyword, parm->keyword, keywordlen)) + { + parm->data = percent_plus_unescape ((const unsigned char*)line, 0xff); + if (!parm->data) + parm->error = errno; + } + + return 0; +} + + +/* Call the agent to retrieve a single line data object. On success + the object is malloced and stored at RESULT; it is guaranteed that + NULL is never stored in this case. On error an error code is + returned and NULL stored at RESULT. */ +gpg_error_t +agent_card_getattr (ctrl_t ctrl, const char *name, char **result) +{ + int err; + struct card_getattr_parm_s parm; + char line[ASSUAN_LINELENGTH]; + + *result = NULL; + + if (!*name) + return gpg_error (GPG_ERR_INV_VALUE); + + memset (&parm, 0, sizeof parm); + parm.keyword = name; + parm.keywordlen = strlen (name); + + /* We assume that NAME does not need escaping. */ + if (8 + strlen (name) > DIM(line)-1) + return gpg_error (GPG_ERR_TOO_LARGE); + stpcpy (stpcpy (line, "GETATTR "), name); + + err = start_scd (ctrl); + if (err) + return err; + + err = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, + card_getattr_cb, &parm); + if (!err && parm.error) + err = gpg_error_from_errno (parm.error); + + if (!err && !parm.data) + err = gpg_error (GPG_ERR_NO_DATA); + + if (!err) + *result = parm.data; + else + xfree (parm.data); + + return unlock_scd (ctrl, err); +} + + + + +static gpg_error_t +pass_status_thru (void *opaque, const char *line) +{ + assuan_context_t ctx = opaque; + char keyword[200]; + int i; + + if (line[0] == '#' && (!line[1] || spacep (line+1))) + { + /* We are called in convey comments mode. Now, if we see a + comment marker as keyword we forward the line verbatim to the + the caller. This way the comment lines from scdaemon won't + appear as status lines with keyword '#'. */ + assuan_write_line (ctx, line); + } + else + { + for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++) + keyword[i] = *line; + keyword[i] = 0; + + /* Truncate any remaining keyword stuff. */ + for (; *line && !spacep (line); line++) + ; + while (spacep (line)) + line++; + + assuan_write_status (ctx, keyword, line); + } + return 0; +} + +static gpg_error_t +pass_data_thru (void *opaque, const void *buffer, size_t length) +{ + assuan_context_t ctx = opaque; + + assuan_send_data (ctx, buffer, length); + return 0; +} + + +/* Send the line CMDLINE with command for the SCDdaemon to it and send + all status messages back. This command is used as a general quoting + mechanism to pass everything verbatim to SCDAEMON. The PIN + inquiry is handled inside gpg-agent. */ +int +agent_card_scd (ctrl_t ctrl, const char *cmdline, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, void *assuan_context) +{ + int rc; + struct inq_needpin_s inqparm; + int saveflag; + + rc = start_scd (ctrl); + if (rc) + return rc; + + inqparm.ctx = ctrl->scd_local->ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + inqparm.passthru = assuan_context; + inqparm.any_inq_seen = 0; + saveflag = assuan_get_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS); + assuan_set_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS, 1); + rc = assuan_transact (ctrl->scd_local->ctx, cmdline, + pass_data_thru, assuan_context, + inq_needpin, &inqparm, + pass_status_thru, assuan_context); + if (inqparm.any_inq_seen && gpg_err_code(rc) == GPG_ERR_ASS_CANCELED) + rc = cancel_inquire (ctrl, rc); + + assuan_set_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS, saveflag); + if (rc) + { + return unlock_scd (ctrl, rc); + } + + return unlock_scd (ctrl, 0); +} diff --git a/agent/command-ssh.c b/agent/command-ssh.c new file mode 100644 index 0000000..95cef41 --- /dev/null +++ b/agent/command-ssh.c @@ -0,0 +1,3692 @@ +/* command-ssh.c - gpg-agent's ssh-agent emulation layer + * Copyright (C) 2004-2006, 2009, 2012 Free Software Foundation, Inc. + * Copyright (C) 2004-2006, 2009, 2012-2014 Werner Koch + * + * 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 3 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, see . + */ + +/* Only v2 of the ssh-agent protocol is implemented. Relevant RFCs + are: + + RFC-4250 - Protocol Assigned Numbers + RFC-4251 - Protocol Architecture + RFC-4252 - Authentication Protocol + RFC-4253 - Transport Layer Protocol + RFC-5656 - ECC support + + The protocol for the agent is defined in OpenSSH's PROTOCL.agent + file. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" + +#include "i18n.h" +#include "util.h" +#include "ssh-utils.h" + + + + +/* Request types. */ +#define SSH_REQUEST_REQUEST_IDENTITIES 11 +#define SSH_REQUEST_SIGN_REQUEST 13 +#define SSH_REQUEST_ADD_IDENTITY 17 +#define SSH_REQUEST_REMOVE_IDENTITY 18 +#define SSH_REQUEST_REMOVE_ALL_IDENTITIES 19 +#define SSH_REQUEST_LOCK 22 +#define SSH_REQUEST_UNLOCK 23 +#define SSH_REQUEST_ADD_ID_CONSTRAINED 25 + +/* Options. */ +#define SSH_OPT_CONSTRAIN_LIFETIME 1 +#define SSH_OPT_CONSTRAIN_CONFIRM 2 + +/* Response types. */ +#define SSH_RESPONSE_SUCCESS 6 +#define SSH_RESPONSE_FAILURE 5 +#define SSH_RESPONSE_IDENTITIES_ANSWER 12 +#define SSH_RESPONSE_SIGN_RESPONSE 14 + +/* Other constants. */ +#define SSH_DSA_SIGNATURE_PADDING 20 +#define SSH_DSA_SIGNATURE_ELEMS 2 +#define SPEC_FLAG_USE_PKCS1V2 (1 << 0) +#define SPEC_FLAG_IS_ECDSA (1 << 1) +#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/ +#define SPEC_FLAG_WITH_CERT (1 << 7) + +/* The name of the control file. */ +#define SSH_CONTROL_FILE_NAME "sshcontrol" + +/* The blurb we put into the header of a newly created control file. */ +static const char sshcontrolblurb[] = +"# List of allowed ssh keys. Only keys present in this file are used\n" +"# in the SSH protocol. The ssh-add tool may add new entries to this\n" +"# file to enable them; you may also add them manually. Comment\n" +"# lines, like this one, as well as empty lines are ignored. Lines do\n" +"# have a certain length limit but this is not serious limitation as\n" +"# the format of the entries is fixed and checked by gpg-agent. A\n" +"# non-comment line starts with optional white spaces, followed by the\n" +"# keygrip of the key given as 40 hex digits, optionally followed by a\n" +"# caching TTL in seconds, and another optional field for arbitrary\n" +"# flags. Prepend the keygrip with an '!' mark to disable it.\n" +"\n"; + + +/* Macros. */ + +/* Return a new uint32 with b0 being the most significant byte and b3 + being the least significant byte. */ +#define uint32_construct(b0, b1, b2, b3) \ + ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3) + + + + +/* + * Basic types. + */ + +/* Type for a request handler. */ +typedef gpg_error_t (*ssh_request_handler_t) (ctrl_t ctrl, + estream_t request, + estream_t response); + + +struct ssh_key_type_spec; +typedef struct ssh_key_type_spec ssh_key_type_spec_t; + +/* Type, which is used for associating request handlers with the + appropriate request IDs. */ +typedef struct ssh_request_spec +{ + unsigned char type; + ssh_request_handler_t handler; + const char *identifier; + unsigned int secret_input; +} ssh_request_spec_t; + +/* Type for "key modifier functions", which are necessary since + OpenSSH and GnuPG treat key material slightly different. A key + modifier is called right after a new key identity has been received + in order to "sanitize" the material. */ +typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems, + gcry_mpi_t *mpis); + +/* The encoding of a generated signature is dependent on the + algorithm; therefore algorithm specific signature encoding + functions are necessary. */ +typedef gpg_error_t (*ssh_signature_encoder_t) (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t sig); + +/* Type, which is used for boundling all the algorithm specific + information together in a single object. */ +struct ssh_key_type_spec +{ + /* Algorithm identifier as used by OpenSSH. */ + const char *ssh_identifier; + + /* Human readable name of the algorithm. */ + const char *name; + + /* Algorithm identifier as used by GnuPG. */ + int algo; + + /* List of MPI names for secret keys; order matches the one of the + agent protocol. */ + const char *elems_key_secret; + + /* List of MPI names for public keys; order matches the one of the + agent protocol. */ + const char *elems_key_public; + + /* List of MPI names for signature data. */ + const char *elems_signature; + + /* List of MPI names for secret keys; order matches the one, which + is required by gpg-agent's key access layer. */ + const char *elems_sexp_order; + + /* Key modifier function. Key modifier functions are necessary in + order to fix any inconsistencies between the representation of + keys on the SSH and on the GnuPG side. */ + ssh_key_modifier_t key_modifier; + + /* Signature encoder function. Signature encoder functions are + necessary since the encoding of signatures depends on the used + algorithm. */ + ssh_signature_encoder_t signature_encoder; + + /* The name of the ECC curve or NULL. */ + const char *curve_name; + + /* The hash algorithm to be used with this key. 0 for using the + default. */ + int hash_algo; + + /* Misc flags. */ + unsigned int flags; +}; + + +/* Definition of an object to access the sshcontrol file. */ +struct ssh_control_file_s +{ + char *fname; /* Name of the file. */ + FILE *fp; /* This is never NULL. */ + int lnr; /* The current line number. */ + struct { + int valid; /* True if the data of this structure is valid. */ + int disabled; /* The item is disabled. */ + int ttl; /* The TTL of the item. */ + int confirm; /* The confirm flag is set. */ + char hexgrip[40+1]; /* The hexgrip of the item (uppercase). */ + } item; +}; + + +/* Prototypes. */ +static gpg_error_t ssh_handler_request_identities (ctrl_t ctrl, + estream_t request, + estream_t response); +static gpg_error_t ssh_handler_sign_request (ctrl_t ctrl, + estream_t request, + estream_t response); +static gpg_error_t ssh_handler_add_identity (ctrl_t ctrl, + estream_t request, + estream_t response); +static gpg_error_t ssh_handler_remove_identity (ctrl_t ctrl, + estream_t request, + estream_t response); +static gpg_error_t ssh_handler_remove_all_identities (ctrl_t ctrl, + estream_t request, + estream_t response); +static gpg_error_t ssh_handler_lock (ctrl_t ctrl, + estream_t request, + estream_t response); +static gpg_error_t ssh_handler_unlock (ctrl_t ctrl, + estream_t request, + estream_t response); + +static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis); +static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t signature); +static gpg_error_t ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t signature); +static gpg_error_t ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t signature); +static gpg_error_t ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t signature); +static gpg_error_t ssh_key_extract_comment (gcry_sexp_t key, char **comment); + + + +/* Global variables. */ + + +/* Associating request types with the corresponding request + handlers. */ + +static ssh_request_spec_t request_specs[] = + { +#define REQUEST_SPEC_DEFINE(id, name, secret_input) \ + { SSH_REQUEST_##id, ssh_handler_##name, #name, secret_input } + + REQUEST_SPEC_DEFINE (REQUEST_IDENTITIES, request_identities, 1), + REQUEST_SPEC_DEFINE (SIGN_REQUEST, sign_request, 0), + REQUEST_SPEC_DEFINE (ADD_IDENTITY, add_identity, 1), + REQUEST_SPEC_DEFINE (ADD_ID_CONSTRAINED, add_identity, 1), + REQUEST_SPEC_DEFINE (REMOVE_IDENTITY, remove_identity, 0), + REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities, 0), + REQUEST_SPEC_DEFINE (LOCK, lock, 0), + REQUEST_SPEC_DEFINE (UNLOCK, unlock, 0) +#undef REQUEST_SPEC_DEFINE + }; + + +/* Table holding key type specifications. */ +static ssh_key_type_spec_t ssh_key_types[] = + { + { + "ssh-ed25519", "Ed25519", GCRY_PK_EDDSA, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_eddsa, + "Ed25519", 0, SPEC_FLAG_IS_EdDSA + }, + { + "ssh-rsa", "RSA", GCRY_PK_RSA, "nedupq", "en", "s", "nedpqu", + ssh_key_modifier_rsa, ssh_signature_encoder_rsa, + NULL, 0, SPEC_FLAG_USE_PKCS1V2 + }, + { + "ssh-dss", "DSA", GCRY_PK_DSA, "pqgyx", "pqgy", "rs", "pqgyx", + NULL, ssh_signature_encoder_dsa, + NULL, 0, 0 + }, + { + "ecdsa-sha2-nistp256", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_ecdsa, + "nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA + }, + { + "ecdsa-sha2-nistp384", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_ecdsa, + "nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA + }, + { + "ecdsa-sha2-nistp521", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_ecdsa, + "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA + }, + { + "ssh-ed25519-cert-v01@openssh.com", "Ed25519", + GCRY_PK_EDDSA, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_eddsa, + "Ed25519", 0, SPEC_FLAG_IS_EdDSA | SPEC_FLAG_WITH_CERT + }, + { + "ssh-rsa-cert-v01@openssh.com", "RSA", + GCRY_PK_RSA, "nedupq", "en", "s", "nedpqu", + ssh_key_modifier_rsa, ssh_signature_encoder_rsa, + NULL, 0, SPEC_FLAG_USE_PKCS1V2 | SPEC_FLAG_WITH_CERT + }, + { + "ssh-dss-cert-v01@openssh.com", "DSA", + GCRY_PK_DSA, "pqgyx", "pqgy", "rs", "pqgyx", + NULL, ssh_signature_encoder_dsa, + NULL, 0, SPEC_FLAG_WITH_CERT | SPEC_FLAG_WITH_CERT + }, + { + "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA", + GCRY_PK_ECC, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_ecdsa, + "nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT + }, + { + "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA", + GCRY_PK_ECC, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_ecdsa, + "nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT + }, + { + "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA", + GCRY_PK_ECC, "qd", "q", "rs", "qd", + NULL, ssh_signature_encoder_ecdsa, + "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT + } + }; + + + + + +/* + General utility functions. + */ + +/* A secure realloc, i.e. it makes sure to allocate secure memory if A + is NULL. This is required because the standard gcry_realloc does + not know whether to allocate secure or normal if NULL is passed as + existing buffer. */ +static void * +realloc_secure (void *a, size_t n) +{ + void *p; + + if (a) + p = gcry_realloc (a, n); + else + p = gcry_malloc_secure (n); + + return p; +} + + +/* Lookup the ssh-identifier for the ECC curve CURVE_NAME. Returns + NULL if not found. */ +static const char * +ssh_identifier_from_curve_name (const char *curve_name) +{ + int i; + + for (i = 0; i < DIM (ssh_key_types); i++) + if (ssh_key_types[i].curve_name + && !strcmp (ssh_key_types[i].curve_name, curve_name)) + return ssh_key_types[i].ssh_identifier; + + return NULL; +} + + +/* + Primitive I/O functions. + */ + + +/* Read a byte from STREAM, store it in B. */ +static gpg_error_t +stream_read_byte (estream_t stream, unsigned char *b) +{ + gpg_error_t err; + int ret; + + ret = es_fgetc (stream); + if (ret == EOF) + { + if (es_ferror (stream)) + err = gpg_error_from_syserror (); + else + err = gpg_error (GPG_ERR_EOF); + *b = 0; + } + else + { + *b = ret & 0xFF; + err = 0; + } + + return err; +} + +/* Write the byte contained in B to STREAM. */ +static gpg_error_t +stream_write_byte (estream_t stream, unsigned char b) +{ + gpg_error_t err; + int ret; + + ret = es_fputc (b, stream); + if (ret == EOF) + err = gpg_error_from_syserror (); + else + err = 0; + + return err; +} + + +/* Read a uint32 from STREAM, store it in UINT32. */ +static gpg_error_t +stream_read_uint32 (estream_t stream, u32 *uint32) +{ + unsigned char buffer[4]; + size_t bytes_read; + gpg_error_t err; + int ret; + + ret = es_read (stream, buffer, sizeof (buffer), &bytes_read); + if (ret) + err = gpg_error_from_syserror (); + else + { + if (bytes_read != sizeof (buffer)) + err = gpg_error (GPG_ERR_EOF); + else + { + u32 n; + + n = uint32_construct (buffer[0], buffer[1], buffer[2], buffer[3]); + *uint32 = n; + err = 0; + } + } + + return err; +} + +/* Write the uint32 contained in UINT32 to STREAM. */ +static gpg_error_t +stream_write_uint32 (estream_t stream, u32 uint32) +{ + unsigned char buffer[4]; + gpg_error_t err; + int ret; + + buffer[0] = uint32 >> 24; + buffer[1] = uint32 >> 16; + buffer[2] = uint32 >> 8; + buffer[3] = uint32 >> 0; + + ret = es_write (stream, buffer, sizeof (buffer), NULL); + if (ret) + err = gpg_error_from_syserror (); + else + err = 0; + + return err; +} + +/* Read SIZE bytes from STREAM into BUFFER. */ +static gpg_error_t +stream_read_data (estream_t stream, unsigned char *buffer, size_t size) +{ + gpg_error_t err; + size_t bytes_read; + int ret; + + ret = es_read (stream, buffer, size, &bytes_read); + if (ret) + err = gpg_error_from_syserror (); + else + { + if (bytes_read != size) + err = gpg_error (GPG_ERR_EOF); + else + err = 0; + } + + return err; +} + +/* Skip over SIZE bytes from STREAM. */ +static gpg_error_t +stream_read_skip (estream_t stream, size_t size) +{ + char buffer[128]; + size_t bytes_to_read, bytes_read; + int ret; + + do + { + bytes_to_read = size; + if (bytes_to_read > sizeof buffer) + bytes_to_read = sizeof buffer; + + ret = es_read (stream, buffer, bytes_to_read, &bytes_read); + if (ret) + return gpg_error_from_syserror (); + else if (bytes_read != bytes_to_read) + return gpg_error (GPG_ERR_EOF); + else + size -= bytes_to_read; + } + while (size); + + return 0; +} + + +/* Write SIZE bytes from BUFFER to STREAM. */ +static gpg_error_t +stream_write_data (estream_t stream, const unsigned char *buffer, size_t size) +{ + gpg_error_t err; + int ret; + + ret = es_write (stream, buffer, size, NULL); + if (ret) + err = gpg_error_from_syserror (); + else + err = 0; + + return err; +} + +/* Read a binary string from STREAM into STRING, store size of string + in STRING_SIZE. Append a hidden nul so that the result may + directly be used as a C string. Depending on SECURE use secure + memory for STRING. If STRING is NULL do only a dummy read. */ +static gpg_error_t +stream_read_string (estream_t stream, unsigned int secure, + unsigned char **string, u32 *string_size) +{ + gpg_error_t err; + unsigned char *buffer = NULL; + u32 length = 0; + + if (string_size) + *string_size = 0; + + /* Read string length. */ + err = stream_read_uint32 (stream, &length); + if (err) + goto out; + + if (string) + { + /* Allocate space. */ + if (secure) + buffer = xtrymalloc_secure (length + 1); + else + buffer = xtrymalloc (length + 1); + if (! buffer) + { + err = gpg_error_from_syserror (); + goto out; + } + + /* Read data. */ + err = stream_read_data (stream, buffer, length); + if (err) + goto out; + + /* Finalize string object. */ + buffer[length] = 0; + *string = buffer; + } + else /* Dummy read requested. */ + { + err = stream_read_skip (stream, length); + if (err) + goto out; + } + + if (string_size) + *string_size = length; + + out: + + if (err) + xfree (buffer); + + return err; +} + + +/* Read a binary string from STREAM and store it as an opaque MPI at + R_MPI, adding 0x40 (this is the prefix for EdDSA key in OpenPGP). + Depending on SECURE use secure memory. If the string is too large + for key material return an error. */ +static gpg_error_t +stream_read_blob (estream_t stream, unsigned int secure, gcry_mpi_t *r_mpi) +{ + gpg_error_t err; + unsigned char *buffer = NULL; + u32 length = 0; + + *r_mpi = NULL; + + /* Read string length. */ + err = stream_read_uint32 (stream, &length); + if (err) + goto leave; + + /* To avoid excessive use of secure memory we check that an MPI is + not too large. */ + if (length > (4096/8) + 8) + { + log_error (_("ssh keys greater than %d bits are not supported\n"), 4096); + err = GPG_ERR_TOO_LARGE; + goto leave; + } + + /* Allocate space. */ + if (secure) + buffer = xtrymalloc_secure (length+1); + else + buffer = xtrymalloc (length+1); + if (!buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Read data. */ + err = stream_read_data (stream, buffer + 1, length); + if (err) + goto leave; + + buffer[0] = 0x40; + *r_mpi = gcry_mpi_set_opaque (NULL, buffer, 8*(length+1)); + buffer = NULL; + + leave: + xfree (buffer); + return err; +} + + +/* Read a C-string from STREAM, store copy in STRING. */ +static gpg_error_t +stream_read_cstring (estream_t stream, char **string) +{ + gpg_error_t err; + unsigned char *buffer; + + err = stream_read_string (stream, 0, &buffer, NULL); + if (!err) + *string = (char *)buffer; + return err; +} + + +/* Write a binary string from STRING of size STRING_N to STREAM. */ +static gpg_error_t +stream_write_string (estream_t stream, + const unsigned char *string, u32 string_n) +{ + gpg_error_t err; + + err = stream_write_uint32 (stream, string_n); + if (err) + goto out; + + err = stream_write_data (stream, string, string_n); + + out: + + return err; +} + +/* Write a C-string from STRING to STREAM. */ +static gpg_error_t +stream_write_cstring (estream_t stream, const char *string) +{ + gpg_error_t err; + + err = stream_write_string (stream, + (const unsigned char *) string, strlen (string)); + + return err; +} + +/* Read an MPI from STREAM, store it in MPINT. Depending on SECURE + use secure memory. */ +static gpg_error_t +stream_read_mpi (estream_t stream, unsigned int secure, gcry_mpi_t *mpint) +{ + unsigned char *mpi_data; + u32 mpi_data_size; + gpg_error_t err; + gcry_mpi_t mpi; + + mpi_data = NULL; + + err = stream_read_string (stream, secure, &mpi_data, &mpi_data_size); + if (err) + goto out; + + /* To avoid excessive use of secure memory we check that an MPI is + not too large. */ + if (mpi_data_size > 520) + { + log_error (_("ssh keys greater than %d bits are not supported\n"), 4096); + err = GPG_ERR_TOO_LARGE; + goto out; + } + + err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_STD, mpi_data, mpi_data_size, NULL); + if (err) + goto out; + + *mpint = mpi; + + out: + + xfree (mpi_data); + + return err; +} + +/* Write the MPI contained in MPINT to STREAM. */ +static gpg_error_t +stream_write_mpi (estream_t stream, gcry_mpi_t mpint) +{ + unsigned char *mpi_buffer; + size_t mpi_buffer_n; + gpg_error_t err; + + mpi_buffer = NULL; + + err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &mpi_buffer, &mpi_buffer_n, mpint); + if (err) + goto out; + + err = stream_write_string (stream, mpi_buffer, mpi_buffer_n); + + out: + + xfree (mpi_buffer); + + return err; +} + + +/* Copy data from SRC to DST until EOF is reached. */ +static gpg_error_t +stream_copy (estream_t dst, estream_t src) +{ + char buffer[BUFSIZ]; + size_t bytes_read; + gpg_error_t err; + int ret; + + err = 0; + while (1) + { + ret = es_read (src, buffer, sizeof (buffer), &bytes_read); + if (ret || (! bytes_read)) + { + if (ret) + err = gpg_error_from_syserror (); + break; + } + ret = es_write (dst, buffer, bytes_read, NULL); + if (ret) + { + err = gpg_error_from_syserror (); + break; + } + } + + return err; +} + +/* Open the ssh control file and create it if not available. With + APPEND passed as true the file will be opened in append mode, + otherwise in read only mode. On success 0 is returned and a new + control file object stored at R_CF. On error an error code is + returned and NULL is stored at R_CF. */ +static gpg_error_t +open_control_file (ssh_control_file_t *r_cf, int append) +{ + gpg_error_t err; + ssh_control_file_t cf; + + cf = xtrycalloc (1, sizeof *cf); + if (!cf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Note: As soon as we start to use non blocking functions here + (i.e. where Pth might switch threads) we need to employ a + mutex. */ + cf->fname = make_filename_try (gnupg_homedir (), SSH_CONTROL_FILE_NAME, NULL); + if (!cf->fname) + { + err = gpg_error_from_syserror (); + goto leave; + } + /* FIXME: With "a+" we are not able to check whether this will + be created and thus the blurb needs to be written first. */ + cf->fp = fopen (cf->fname, append? "a+":"r"); + if (!cf->fp && errno == ENOENT) + { + estream_t stream = es_fopen (cf->fname, "wx,mode=-rw-r"); + if (!stream) + { + err = gpg_error_from_syserror (); + log_error (_("can't create '%s': %s\n"), + cf->fname, gpg_strerror (err)); + goto leave; + } + es_fputs (sshcontrolblurb, stream); + es_fclose (stream); + cf->fp = fopen (cf->fname, append? "a+":"r"); + } + + if (!cf->fp) + { + err = gpg_error_from_syserror (); + log_error (_("can't open '%s': %s\n"), + cf->fname, gpg_strerror (err)); + goto leave; + } + + err = 0; + + leave: + if (err && cf) + { + if (cf->fp) + fclose (cf->fp); + xfree (cf->fname); + xfree (cf); + } + else + *r_cf = cf; + + return err; +} + + +static void +rewind_control_file (ssh_control_file_t cf) +{ + fseek (cf->fp, 0, SEEK_SET); + cf->lnr = 0; + clearerr (cf->fp); +} + + +static void +close_control_file (ssh_control_file_t cf) +{ + if (!cf) + return; + fclose (cf->fp); + xfree (cf->fname); + xfree (cf); +} + + + +/* Read the next line from the control file and store the data in CF. + Returns 0 on success, GPG_ERR_EOF on EOF, or other error codes. */ +static gpg_error_t +read_control_file_item (ssh_control_file_t cf) +{ + int c, i, n; + char *p, *pend, line[256]; + long ttl = 0; + + cf->item.valid = 0; + clearerr (cf->fp); + + do + { + if (!fgets (line, DIM(line)-1, cf->fp) ) + { + if (feof (cf->fp)) + return gpg_error (GPG_ERR_EOF); + return gpg_error_from_syserror (); + } + cf->lnr++; + + if (!*line || line[strlen(line)-1] != '\n') + { + /* Eat until end of line */ + while ( (c=getc (cf->fp)) != EOF && c != '\n') + ; + return gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + } + + /* Allow for empty lines and spaces */ + for (p=line; spacep (p); p++) + ; + } + while (!*p || *p == '\n' || *p == '#'); + + cf->item.disabled = 0; + if (*p == '!') + { + cf->item.disabled = 1; + for (p++; spacep (p); p++) + ; + } + + for (i=0; hexdigitp (p) && i < 40; p++, i++) + cf->item.hexgrip[i] = (*p >= 'a'? (*p & 0xdf): *p); + cf->item.hexgrip[i] = 0; + if (i != 40 || !(spacep (p) || *p == '\n')) + { + log_error ("%s:%d: invalid formatted line\n", cf->fname, cf->lnr); + return gpg_error (GPG_ERR_BAD_DATA); + } + + ttl = strtol (p, &pend, 10); + p = pend; + if (!(spacep (p) || *p == '\n') || (int)ttl < -1) + { + log_error ("%s:%d: invalid TTL value; assuming 0\n", cf->fname, cf->lnr); + cf->item.ttl = 0; + } + cf->item.ttl = ttl; + + /* Now check for key-value pairs of the form NAME[=VALUE]. */ + cf->item.confirm = 0; + while (*p) + { + for (; spacep (p) && *p != '\n'; p++) + ; + if (!*p || *p == '\n') + break; + n = strcspn (p, "= \t\n"); + if (p[n] == '=') + { + log_error ("%s:%d: assigning a value to a flag is not yet supported; " + "flag ignored\n", cf->fname, cf->lnr); + p++; + } + else if (n == 7 && !memcmp (p, "confirm", 7)) + { + cf->item.confirm = 1; + } + else + log_error ("%s:%d: invalid flag '%.*s'; ignored\n", + cf->fname, cf->lnr, n, p); + p += n; + } + + /* log_debug ("%s:%d: grip=%s ttl=%d%s%s\n", */ + /* cf->fname, cf->lnr, */ + /* cf->item.hexgrip, cf->item.ttl, */ + /* cf->item.disabled? " disabled":"", */ + /* cf->item.confirm? " confirm":""); */ + + cf->item.valid = 1; + return 0; /* Okay: valid entry found. */ +} + + + +/* Search the control file CF from the beginning until a matching + HEXGRIP is found; return success in this case and store true at + DISABLED if the found key has been disabled. If R_TTL is not NULL + a specified TTL for that key is stored there. If R_CONFIRM is not + NULL it is set to 1 if the key has the confirm flag set. */ +static gpg_error_t +search_control_file (ssh_control_file_t cf, const char *hexgrip, + int *r_disabled, int *r_ttl, int *r_confirm) +{ + gpg_error_t err; + + assert (strlen (hexgrip) == 40 ); + + if (r_disabled) + *r_disabled = 0; + if (r_ttl) + *r_ttl = 0; + if (r_confirm) + *r_confirm = 0; + + rewind_control_file (cf); + while (!(err=read_control_file_item (cf))) + { + if (!cf->item.valid) + continue; /* Should not happen. */ + if (!strcmp (hexgrip, cf->item.hexgrip)) + break; + } + if (!err) + { + if (r_disabled) + *r_disabled = cf->item.disabled; + if (r_ttl) + *r_ttl = cf->item.ttl; + if (r_confirm) + *r_confirm = cf->item.confirm; + } + return err; +} + + + +/* Add an entry to the control file to mark the key with the keygrip + HEXGRIP as usable for SSH; i.e. it will be returned when ssh asks + for it. FMTFPR is the fingerprint string. This function is in + general used to add a key received through the ssh-add function. + We can assume that the user wants to allow ssh using this key. */ +static gpg_error_t +add_control_entry (ctrl_t ctrl, ssh_key_type_spec_t *spec, + const char *hexgrip, const char *fmtfpr, + int ttl, int confirm) +{ + gpg_error_t err; + ssh_control_file_t cf; + int disabled; + + (void)ctrl; + + err = open_control_file (&cf, 1); + if (err) + return err; + + err = search_control_file (cf, hexgrip, &disabled, NULL, NULL); + if (err && gpg_err_code(err) == GPG_ERR_EOF) + { + struct tm *tp; + time_t atime = time (NULL); + + /* Not yet in the file - add it. Because the file has been + opened in append mode, we simply need to write to it. */ + tp = localtime (&atime); + fprintf (cf->fp, + ("# %s key added on: %04d-%02d-%02d %02d:%02d:%02d\n" + "# MD5 Fingerprint: %s\n" + "%s %d%s\n"), + spec->name, + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec, + fmtfpr, hexgrip, ttl, confirm? " confirm":""); + + } + close_control_file (cf); + return 0; +} + + +/* Scan the sshcontrol file and return the TTL. */ +static int +ttl_from_sshcontrol (const char *hexgrip) +{ + ssh_control_file_t cf; + int disabled, ttl; + + if (!hexgrip || strlen (hexgrip) != 40) + return 0; /* Wrong input: Use global default. */ + + if (open_control_file (&cf, 0)) + return 0; /* Error: Use the global default TTL. */ + + if (search_control_file (cf, hexgrip, &disabled, &ttl, NULL) + || disabled) + ttl = 0; /* Use the global default if not found or disabled. */ + + close_control_file (cf); + + return ttl; +} + + +/* Scan the sshcontrol file and return the confirm flag. */ +static int +confirm_flag_from_sshcontrol (const char *hexgrip) +{ + ssh_control_file_t cf; + int disabled, confirm; + + if (!hexgrip || strlen (hexgrip) != 40) + return 1; /* Wrong input: Better ask for confirmation. */ + + if (open_control_file (&cf, 0)) + return 1; /* Error: Better ask for confirmation. */ + + if (search_control_file (cf, hexgrip, &disabled, NULL, &confirm) + || disabled) + confirm = 0; /* If not found or disabled, there is no reason to + ask for confirmation. */ + + close_control_file (cf); + + return confirm; +} + + + + +/* Open the ssh control file for reading. This is a public version of + open_control_file. The caller must use ssh_close_control_file to + release the returned handle. */ +ssh_control_file_t +ssh_open_control_file (void) +{ + ssh_control_file_t cf; + + /* Then look at all the registered and non-disabled keys. */ + if (open_control_file (&cf, 0)) + return NULL; + return cf; +} + +/* Close an ssh control file handle. This is the public version of + close_control_file. CF may be NULL. */ +void +ssh_close_control_file (ssh_control_file_t cf) +{ + close_control_file (cf); +} + +/* Read the next item from the ssh control file. The function returns + 0 if a item was read, GPG_ERR_EOF on eof or another error value. + R_HEXGRIP shall either be null or a BUFFER of at least 41 byte. + R_DISABLED, R_TTLm and R_CONFIRM return flags from the control + file; they are only set on success. */ +gpg_error_t +ssh_read_control_file (ssh_control_file_t cf, + char *r_hexgrip, + int *r_disabled, int *r_ttl, int *r_confirm) +{ + gpg_error_t err; + + do + err = read_control_file_item (cf); + while (!err && !cf->item.valid); + if (!err) + { + if (r_hexgrip) + strcpy (r_hexgrip, cf->item.hexgrip); + if (r_disabled) + *r_disabled = cf->item.disabled; + if (r_ttl) + *r_ttl = cf->item.ttl; + if (r_confirm) + *r_confirm = cf->item.confirm; + } + return err; +} + + +/* Search for a key with HEXGRIP in sshcontrol and return all + info. */ +gpg_error_t +ssh_search_control_file (ssh_control_file_t cf, + const char *hexgrip, + int *r_disabled, int *r_ttl, int *r_confirm) +{ + gpg_error_t err; + int i; + const char *s; + char uphexgrip[41]; + + /* We need to make sure that HEXGRIP is all uppercase. The easiest + way to do this and also check its length is by copying to a + second buffer. */ + for (i=0, s=hexgrip; i < 40 && *s; s++, i++) + uphexgrip[i] = *s >= 'a'? (*s & 0xdf): *s; + uphexgrip[i] = 0; + if (i != 40) + err = gpg_error (GPG_ERR_INV_LENGTH); + else + err = search_control_file (cf, uphexgrip, r_disabled, r_ttl, r_confirm); + if (gpg_err_code (err) == GPG_ERR_EOF) + err = gpg_error (GPG_ERR_NOT_FOUND); + return err; +} + + + + +/* + + MPI lists. + + */ + +/* Free the list of MPIs MPI_LIST. */ +static void +mpint_list_free (gcry_mpi_t *mpi_list) +{ + if (mpi_list) + { + unsigned int i; + + for (i = 0; mpi_list[i]; i++) + gcry_mpi_release (mpi_list[i]); + xfree (mpi_list); + } +} + +/* Receive key material MPIs from STREAM according to KEY_SPEC; + depending on SECRET expect a public key or secret key. CERT is the + certificate blob used if KEY_SPEC indicates the certificate format; + it needs to be positioned to the end of the nonce. The newly + allocated list of MPIs is stored in MPI_LIST. Returns usual error + code. */ +static gpg_error_t +ssh_receive_mpint_list (estream_t stream, int secret, + ssh_key_type_spec_t *spec, estream_t cert, + gcry_mpi_t **mpi_list) +{ + const char *elems_public; + unsigned int elems_n; + const char *elems; + int elem_is_secret; + gcry_mpi_t *mpis = NULL; + gpg_error_t err = 0; + unsigned int i; + + if (secret) + elems = spec->elems_key_secret; + else + elems = spec->elems_key_public; + elems_n = strlen (elems); + elems_public = spec->elems_key_public; + + /* Check that either both, CERT and the WITH_CERT flag, are given or + none of them. */ + if (!(!!(spec->flags & SPEC_FLAG_WITH_CERT) ^ !cert)) + { + err = gpg_error (GPG_ERR_INV_CERT_OBJ); + goto out; + } + + mpis = xtrycalloc (elems_n + 1, sizeof *mpis ); + if (!mpis) + { + err = gpg_error_from_syserror (); + goto out; + } + + elem_is_secret = 0; + for (i = 0; i < elems_n; i++) + { + if (secret) + elem_is_secret = !strchr (elems_public, elems[i]); + + if (cert && !elem_is_secret) + err = stream_read_mpi (cert, elem_is_secret, &mpis[i]); + else + err = stream_read_mpi (stream, elem_is_secret, &mpis[i]); + if (err) + goto out; + } + + *mpi_list = mpis; + mpis = NULL; + + out: + if (err) + mpint_list_free (mpis); + + return err; +} + + + +/* Key modifier function for RSA. */ +static gpg_error_t +ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis) +{ + gcry_mpi_t p; + gcry_mpi_t q; + gcry_mpi_t u; + + if (strcmp (elems, "nedupq")) + /* Modifying only necessary for secret keys. */ + goto out; + + u = mpis[3]; + p = mpis[4]; + q = mpis[5]; + + if (gcry_mpi_cmp (p, q) > 0) + { + /* P shall be smaller then Q! Swap primes. iqmp becomes u. */ + gcry_mpi_t tmp; + + tmp = mpis[4]; + mpis[4] = mpis[5]; + mpis[5] = tmp; + } + else + /* U needs to be recomputed. */ + gcry_mpi_invm (u, p, q); + + out: + + return 0; +} + +/* Signature encoder function for RSA. */ +static gpg_error_t +ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t s_signature) +{ + gpg_error_t err = 0; + gcry_sexp_t valuelist = NULL; + gcry_sexp_t sublist = NULL; + gcry_mpi_t sig_value = NULL; + gcry_mpi_t *mpis = NULL; + const char *elems; + size_t elems_n; + int i; + + unsigned char *data; + size_t data_n; + gcry_mpi_t s; + + valuelist = gcry_sexp_nth (s_signature, 1); + if (!valuelist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + elems = spec->elems_signature; + elems_n = strlen (elems); + + mpis = xtrycalloc (elems_n + 1, sizeof *mpis); + if (!mpis) + { + err = gpg_error_from_syserror (); + goto out; + } + + for (i = 0; i < elems_n; i++) + { + sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1); + if (!sublist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + + sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG); + if (!sig_value) + { + err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */ + break; + } + gcry_sexp_release (sublist); + sublist = NULL; + + mpis[i] = sig_value; + } + if (err) + goto out; + + /* RSA specific */ + s = mpis[0]; + + err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s); + if (err) + goto out; + + err = stream_write_string (signature_blob, data, data_n); + xfree (data); + + out: + gcry_sexp_release (valuelist); + gcry_sexp_release (sublist); + mpint_list_free (mpis); + return err; +} + + +/* Signature encoder function for DSA. */ +static gpg_error_t +ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec, + estream_t signature_blob, + gcry_sexp_t s_signature) +{ + gpg_error_t err = 0; + gcry_sexp_t valuelist = NULL; + gcry_sexp_t sublist = NULL; + gcry_mpi_t sig_value = NULL; + gcry_mpi_t *mpis = NULL; + const char *elems; + size_t elems_n; + int i; + + unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS]; + unsigned char *data = NULL; + size_t data_n; + + valuelist = gcry_sexp_nth (s_signature, 1); + if (!valuelist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + elems = spec->elems_signature; + elems_n = strlen (elems); + + mpis = xtrycalloc (elems_n + 1, sizeof *mpis); + if (!mpis) + { + err = gpg_error_from_syserror (); + goto out; + } + + for (i = 0; i < elems_n; i++) + { + sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1); + if (!sublist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + + sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG); + if (!sig_value) + { + err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */ + break; + } + gcry_sexp_release (sublist); + sublist = NULL; + + mpis[i] = sig_value; + } + if (err) + goto out; + + /* DSA specific code. */ + + /* FIXME: Why this complicated code? Why collecting boths mpis in a + buffer instead of writing them out one after the other? */ + for (i = 0; i < 2; i++) + { + err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, mpis[i]); + if (err) + break; + + if (data_n > SSH_DSA_SIGNATURE_PADDING) + { + err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */ + break; + } + + memset (buffer + (i * SSH_DSA_SIGNATURE_PADDING), 0, + SSH_DSA_SIGNATURE_PADDING - data_n); + memcpy (buffer + (i * SSH_DSA_SIGNATURE_PADDING) + + (SSH_DSA_SIGNATURE_PADDING - data_n), data, data_n); + + xfree (data); + data = NULL; + } + if (err) + goto out; + + err = stream_write_string (signature_blob, buffer, sizeof (buffer)); + + out: + xfree (data); + gcry_sexp_release (valuelist); + gcry_sexp_release (sublist); + mpint_list_free (mpis); + return err; +} + + +/* Signature encoder function for ECDSA. */ +static gpg_error_t +ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec, + estream_t stream, gcry_sexp_t s_signature) +{ + gpg_error_t err = 0; + gcry_sexp_t valuelist = NULL; + gcry_sexp_t sublist = NULL; + gcry_mpi_t sig_value = NULL; + gcry_mpi_t *mpis = NULL; + const char *elems; + size_t elems_n; + int i; + + unsigned char *data[2] = {NULL, NULL}; + size_t data_n[2]; + size_t innerlen; + + valuelist = gcry_sexp_nth (s_signature, 1); + if (!valuelist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + elems = spec->elems_signature; + elems_n = strlen (elems); + + mpis = xtrycalloc (elems_n + 1, sizeof *mpis); + if (!mpis) + { + err = gpg_error_from_syserror (); + goto out; + } + + for (i = 0; i < elems_n; i++) + { + sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1); + if (!sublist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + + sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG); + if (!sig_value) + { + err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */ + break; + } + gcry_sexp_release (sublist); + sublist = NULL; + + mpis[i] = sig_value; + } + if (err) + goto out; + + /* ECDSA specific */ + + innerlen = 0; + for (i = 0; i < DIM(data); i++) + { + err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &data[i], &data_n[i], mpis[i]); + if (err) + goto out; + innerlen += 4 + data_n[i]; + } + + err = stream_write_uint32 (stream, innerlen); + if (err) + goto out; + + for (i = 0; i < DIM(data); i++) + { + err = stream_write_string (stream, data[i], data_n[i]); + if (err) + goto out; + } + + out: + for (i = 0; i < DIM(data); i++) + xfree (data[i]); + gcry_sexp_release (valuelist); + gcry_sexp_release (sublist); + mpint_list_free (mpis); + return err; +} + + +/* Signature encoder function for EdDSA. */ +static gpg_error_t +ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec, + estream_t stream, gcry_sexp_t s_signature) +{ + gpg_error_t err = 0; + gcry_sexp_t valuelist = NULL; + gcry_sexp_t sublist = NULL; + const char *elems; + size_t elems_n; + int i; + + unsigned char *data[2] = {NULL, NULL}; + size_t data_n[2]; + size_t totallen = 0; + + valuelist = gcry_sexp_nth (s_signature, 1); + if (!valuelist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + elems = spec->elems_signature; + elems_n = strlen (elems); + + if (elems_n != DIM(data)) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + for (i = 0; i < DIM(data); i++) + { + sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1); + if (!sublist) + { + err = gpg_error (GPG_ERR_INV_SEXP); + break; + } + + data[i] = gcry_sexp_nth_buffer (sublist, 1, &data_n[i]); + if (!data[i]) + { + err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */ + break; + } + totallen += data_n[i]; + gcry_sexp_release (sublist); + sublist = NULL; + } + if (err) + goto out; + + err = stream_write_uint32 (stream, totallen); + if (err) + goto out; + + for (i = 0; i < DIM(data); i++) + { + err = stream_write_data (stream, data[i], data_n[i]); + if (err) + goto out; + } + + out: + for (i = 0; i < DIM(data); i++) + xfree (data[i]); + gcry_sexp_release (valuelist); + gcry_sexp_release (sublist); + return err; +} + + +/* + S-Expressions. + */ + + +/* This function constructs a new S-Expression for the key identified + by the KEY_SPEC, SECRET, CURVE_NAME, MPIS, and COMMENT, which is to + be stored at R_SEXP. Returns an error code. */ +static gpg_error_t +sexp_key_construct (gcry_sexp_t *r_sexp, + ssh_key_type_spec_t key_spec, int secret, + const char *curve_name, gcry_mpi_t *mpis, + const char *comment) +{ + gpg_error_t err; + gcry_sexp_t sexp_new = NULL; + void *formatbuf = NULL; + void **arg_list = NULL; + estream_t format = NULL; + char *algo_name = NULL; + + if ((key_spec.flags & SPEC_FLAG_IS_EdDSA)) + { + /* It is much easier and more readable to use a separate code + path for EdDSA. */ + if (!curve_name) + err = gpg_error (GPG_ERR_INV_CURVE); + else if (!mpis[0] || !gcry_mpi_get_flag (mpis[0], GCRYMPI_FLAG_OPAQUE)) + err = gpg_error (GPG_ERR_BAD_PUBKEY); + else if (secret + && (!mpis[1] + || !gcry_mpi_get_flag (mpis[1], GCRYMPI_FLAG_OPAQUE))) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else if (secret) + err = gcry_sexp_build (&sexp_new, NULL, + "(private-key(ecc(curve %s)" + "(flags eddsa)(q %m)(d %m))" + "(comment%s))", + curve_name, + mpis[0], mpis[1], + comment? comment:""); + else + err = gcry_sexp_build (&sexp_new, NULL, + "(public-key(ecc(curve %s)" + "(flags eddsa)(q %m))" + "(comment%s))", + curve_name, + mpis[0], + comment? comment:""); + } + else + { + const char *key_identifier[] = { "public-key", "private-key" }; + int arg_idx; + const char *elems; + size_t elems_n; + unsigned int i, j; + + if (secret) + elems = key_spec.elems_sexp_order; + else + elems = key_spec.elems_key_public; + elems_n = strlen (elems); + + format = es_fopenmem (0, "a+b"); + if (!format) + { + err = gpg_error_from_syserror (); + goto out; + } + + /* Key identifier, algorithm identifier, mpis, comment, and a NULL + as a safeguard. */ + arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1)); + if (!arg_list) + { + err = gpg_error_from_syserror (); + goto out; + } + arg_idx = 0; + + es_fputs ("(%s(%s", format); + arg_list[arg_idx++] = &key_identifier[secret]; + algo_name = xtrystrdup (gcry_pk_algo_name (key_spec.algo)); + if (!algo_name) + { + err = gpg_error_from_syserror (); + goto out; + } + strlwr (algo_name); + arg_list[arg_idx++] = &algo_name; + if (curve_name) + { + es_fputs ("(curve%s)", format); + arg_list[arg_idx++] = &curve_name; + } + + for (i = 0; i < elems_n; i++) + { + es_fprintf (format, "(%c%%m)", elems[i]); + if (secret) + { + for (j = 0; j < elems_n; j++) + if (key_spec.elems_key_secret[j] == elems[i]) + break; + } + else + j = i; + arg_list[arg_idx++] = &mpis[j]; + } + es_fputs (")(comment%s))", format); + arg_list[arg_idx++] = &comment; + arg_list[arg_idx] = NULL; + + es_putc (0, format); + if (es_ferror (format)) + { + err = gpg_error_from_syserror (); + goto out; + } + if (es_fclose_snatch (format, &formatbuf, NULL)) + { + err = gpg_error_from_syserror (); + goto out; + } + format = NULL; + + err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list); + } + + if (!err) + *r_sexp = sexp_new; + + out: + es_fclose (format); + xfree (arg_list); + xfree (formatbuf); + xfree (algo_name); + + return err; +} + + +/* This function extracts the key from the s-expression SEXP according + to KEY_SPEC and stores it in ssh format at (R_BLOB, R_BLOBLEN). If + WITH_SECRET is true, the secret key parts are also extracted if + possible. Returns 0 on success or an error code. Note that data + stored at R_BLOB must be freed using es_free! */ +static gpg_error_t +ssh_key_to_blob (gcry_sexp_t sexp, int with_secret, + ssh_key_type_spec_t key_spec, + void **r_blob, size_t *r_blob_size) +{ + gpg_error_t err = 0; + gcry_sexp_t value_list = NULL; + gcry_sexp_t value_pair = NULL; + char *curve_name = NULL; + estream_t stream = NULL; + void *blob = NULL; + size_t blob_size; + const char *elems, *p_elems; + const char *data; + size_t datalen; + + *r_blob = NULL; + *r_blob_size = 0; + + stream = es_fopenmem (0, "r+b"); + if (!stream) + { + err = gpg_error_from_syserror (); + goto out; + } + + /* Get the type of the key extpression. */ + data = gcry_sexp_nth_data (sexp, 0, &datalen); + if (!data) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + if ((datalen == 10 && !strncmp (data, "public-key", 10)) + || (datalen == 21 && !strncmp (data, "protected-private-key", 21)) + || (datalen == 20 && !strncmp (data, "shadowed-private-key", 20))) + elems = key_spec.elems_key_public; + else if (datalen == 11 && !strncmp (data, "private-key", 11)) + elems = with_secret? key_spec.elems_key_secret : key_spec.elems_key_public; + else + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + /* Get key value list. */ + value_list = gcry_sexp_cadr (sexp); + if (!value_list) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + + /* Write the ssh algorithm identifier. */ + if ((key_spec.flags & SPEC_FLAG_IS_ECDSA)) + { + /* Parse the "curve" parameter. We currently expect the curve + name for ECC and not the parameters of the curve. This can + easily be changed but then we need to find the curve name + from the parameters using gcry_pk_get_curve. */ + const char *mapped; + const char *sshname; + + gcry_sexp_release (value_pair); + value_pair = gcry_sexp_find_token (value_list, "curve", 5); + if (!value_pair) + { + err = gpg_error (GPG_ERR_INV_CURVE); + goto out; + } + curve_name = gcry_sexp_nth_string (value_pair, 1); + if (!curve_name) + { + err = gpg_error (GPG_ERR_INV_CURVE); /* (Or out of core.) */ + goto out; + } + + /* Fixme: The mapping should be done by using gcry_pk_get_curve + et al to iterate over all name aliases. */ + if (!strcmp (curve_name, "NIST P-256")) + mapped = "nistp256"; + else if (!strcmp (curve_name, "NIST P-384")) + mapped = "nistp384"; + else if (!strcmp (curve_name, "NIST P-521")) + mapped = "nistp521"; + else + mapped = NULL; + if (mapped) + { + xfree (curve_name); + curve_name = xtrystrdup (mapped); + if (!curve_name) + { + err = gpg_error_from_syserror (); + goto out; + } + } + + sshname = ssh_identifier_from_curve_name (curve_name); + if (!sshname) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + goto out; + } + err = stream_write_cstring (stream, sshname); + if (err) + goto out; + err = stream_write_cstring (stream, curve_name); + if (err) + goto out; + } + else + { + /* Note: This is also used for EdDSA. */ + err = stream_write_cstring (stream, key_spec.ssh_identifier); + if (err) + goto out; + } + + /* Write the parameters. */ + for (p_elems = elems; *p_elems; p_elems++) + { + gcry_sexp_release (value_pair); + value_pair = gcry_sexp_find_token (value_list, p_elems, 1); + if (!value_pair) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + if ((key_spec.flags & SPEC_FLAG_IS_EdDSA)) + { + + data = gcry_sexp_nth_data (value_pair, 1, &datalen); + if (!data) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + if (*p_elems == 'q' && datalen) + { /* Remove the prefix 0x40. */ + data++; + datalen--; + } + err = stream_write_string (stream, data, datalen); + if (err) + goto out; + } + else + { + gcry_mpi_t mpi; + + /* Note that we need to use STD format; i.e. prepend a 0x00 + to indicate a positive number if the high bit is set. */ + mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD); + if (!mpi) + { + err = gpg_error (GPG_ERR_INV_SEXP); + goto out; + } + err = stream_write_mpi (stream, mpi); + gcry_mpi_release (mpi); + if (err) + goto out; + } + } + + if (es_fclose_snatch (stream, &blob, &blob_size)) + { + err = gpg_error_from_syserror (); + goto out; + } + stream = NULL; + + *r_blob = blob; + blob = NULL; + *r_blob_size = blob_size; + + out: + gcry_sexp_release (value_list); + gcry_sexp_release (value_pair); + xfree (curve_name); + es_fclose (stream); + es_free (blob); + + return err; +} + + +/* + + Key I/O. + +*/ + +/* Search for a key specification entry. If SSH_NAME is not NULL, + search for an entry whose "ssh_name" is equal to SSH_NAME; + otherwise, search for an entry whose algorithm is equal to ALGO. + Store found entry in SPEC on success, return error otherwise. */ +static gpg_error_t +ssh_key_type_lookup (const char *ssh_name, int algo, + ssh_key_type_spec_t *spec) +{ + gpg_error_t err; + unsigned int i; + + for (i = 0; i < DIM (ssh_key_types); i++) + if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier))) + || algo == ssh_key_types[i].algo) + break; + + if (i == DIM (ssh_key_types)) + err = gpg_error (GPG_ERR_NOT_FOUND); + else + { + *spec = ssh_key_types[i]; + err = 0; + } + + return err; +} + + +/* Receive a key from STREAM, according to the key specification given + as KEY_SPEC. Depending on SECRET, receive a secret or a public + key. If READ_COMMENT is true, receive a comment string as well. + Constructs a new S-Expression from received data and stores it in + KEY_NEW. Returns zero on success or an error code. */ +static gpg_error_t +ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret, + int read_comment, ssh_key_type_spec_t *key_spec) +{ + gpg_error_t err; + char *key_type = NULL; + char *comment = NULL; + estream_t cert = NULL; + gcry_sexp_t key = NULL; + ssh_key_type_spec_t spec; + gcry_mpi_t *mpi_list = NULL; + const char *elems; + char *curve_name = NULL; + + + err = stream_read_cstring (stream, &key_type); + if (err) + goto out; + + err = ssh_key_type_lookup (key_type, 0, &spec); + if (err) + goto out; + + if ((spec.flags & SPEC_FLAG_WITH_CERT)) + { + /* This is an OpenSSH certificate+private key. The certificate + is an SSH string and which we store in an estream object. */ + unsigned char *buffer; + u32 buflen; + char *cert_key_type; + + err = stream_read_string (stream, 0, &buffer, &buflen); + if (err) + goto out; + cert = es_fopenmem_init (0, "rb", buffer, buflen); + xfree (buffer); + if (!cert) + { + err = gpg_error_from_syserror (); + goto out; + } + + /* Check that the key type matches. */ + err = stream_read_cstring (cert, &cert_key_type); + if (err) + goto out; + if (strcmp (cert_key_type, key_type) ) + { + xfree (cert_key_type); + log_error ("key types in received ssh certificate do not match\n"); + err = gpg_error (GPG_ERR_INV_CERT_OBJ); + goto out; + } + xfree (cert_key_type); + + /* Skip the nonce. */ + err = stream_read_string (cert, 0, NULL, NULL); + if (err) + goto out; + } + + if ((spec.flags & SPEC_FLAG_IS_EdDSA)) + { + /* The format of an EdDSA key is: + * string key_type ("ssh-ed25519") + * string public_key + * string private_key + * + * Note that the private key is the concatenation of the private + * key with the public key. Thus theres are 64 bytes; however + * we only want the real 32 byte private key - Libgcrypt expects + * this. + */ + mpi_list = xtrycalloc (3, sizeof *mpi_list); + if (!mpi_list) + { + err = gpg_error_from_syserror (); + goto out; + } + + err = stream_read_blob (cert? cert : stream, 0, &mpi_list[0]); + if (err) + goto out; + if (secret) + { + u32 len = 0; + unsigned char *buffer; + + /* Read string length. */ + err = stream_read_uint32 (stream, &len); + if (err) + goto out; + if (len != 32 && len != 64) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto out; + } + buffer = xtrymalloc_secure (32); + if (!buffer) + { + err = gpg_error_from_syserror (); + goto out; + } + err = stream_read_data (stream, buffer, 32); + if (err) + { + xfree (buffer); + goto out; + } + mpi_list[1] = gcry_mpi_set_opaque (NULL, buffer, 8*32); + buffer = NULL; + if (len == 64) + { + err = stream_read_skip (stream, 32); + if (err) + goto out; + } + } + } + else if ((spec.flags & SPEC_FLAG_IS_ECDSA)) + { + /* The format of an ECDSA key is: + * string key_type ("ecdsa-sha2-nistp256" | + * "ecdsa-sha2-nistp384" | + * "ecdsa-sha2-nistp521" ) + * string ecdsa_curve_name + * string ecdsa_public_key + * mpint ecdsa_private + * + * Note that we use the mpint reader instead of the string + * reader for ecsa_public_key. For the certificate variante + * ecdsa_curve_name+ecdsa_public_key are replaced by the + * certificate. + */ + unsigned char *buffer; + const char *mapped; + + err = stream_read_string (cert? cert : stream, 0, &buffer, NULL); + if (err) + goto out; + curve_name = buffer; + /* Fixme: Check that curve_name matches the keytype. */ + /* Because Libgcrypt < 1.6 has no support for the "nistpNNN" + curve names, we need to translate them here to Libgcrypt's + native names. */ + if (!strcmp (curve_name, "nistp256")) + mapped = "NIST P-256"; + else if (!strcmp (curve_name, "nistp384")) + mapped = "NIST P-384"; + else if (!strcmp (curve_name, "nistp521")) + mapped = "NIST P-521"; + else + mapped = NULL; + if (mapped) + { + xfree (curve_name); + curve_name = xtrystrdup (mapped); + if (!curve_name) + { + err = gpg_error_from_syserror (); + goto out; + } + } + + err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list); + if (err) + goto out; + } + else + { + err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list); + if (err) + goto out; + } + + if (read_comment) + { + err = stream_read_cstring (stream, &comment); + if (err) + goto out; + } + + if (secret) + elems = spec.elems_key_secret; + else + elems = spec.elems_key_public; + + if (spec.key_modifier) + { + err = (*spec.key_modifier) (elems, mpi_list); + if (err) + goto out; + } + + if ((spec.flags & SPEC_FLAG_IS_EdDSA)) + { + if (secret) + { + err = gcry_sexp_build (&key, NULL, + "(private-key(ecc(curve \"Ed25519\")" + "(flags eddsa)(q %m)(d %m))" + "(comment%s))", + mpi_list[0], mpi_list[1], + comment? comment:""); + } + else + { + err = gcry_sexp_build (&key, NULL, + "(public-key(ecc(curve \"Ed25519\")" + "(flags eddsa)(q %m))" + "(comment%s))", + mpi_list[0], + comment? comment:""); + } + } + else + { + err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list, + comment? comment:""); + if (err) + goto out; + } + + if (key_spec) + *key_spec = spec; + *key_new = key; + + out: + es_fclose (cert); + mpint_list_free (mpi_list); + xfree (curve_name); + xfree (key_type); + xfree (comment); + + return err; +} + + +/* Write the public key from KEY to STREAM in SSH key format. If + OVERRIDE_COMMENT is not NULL, it will be used instead of the + comment stored in the key. */ +static gpg_error_t +ssh_send_key_public (estream_t stream, gcry_sexp_t key, + const char *override_comment) +{ + ssh_key_type_spec_t spec; + int algo; + char *comment = NULL; + void *blob = NULL; + size_t bloblen; + gpg_error_t err = 0; + + algo = get_pk_algo_from_key (key); + if (algo == 0) + goto out; + + err = ssh_key_type_lookup (NULL, algo, &spec); + if (err) + goto out; + + err = ssh_key_to_blob (key, 0, spec, &blob, &bloblen); + if (err) + goto out; + + err = stream_write_string (stream, blob, bloblen); + if (err) + goto out; + + if (override_comment) + err = stream_write_cstring (stream, override_comment); + else + { + err = ssh_key_extract_comment (key, &comment); + if (err) + err = stream_write_cstring (stream, "(none)"); + else + err = stream_write_cstring (stream, comment); + } + if (err) + goto out; + + out: + xfree (comment); + es_free (blob); + + return err; +} + + +/* Read a public key out of BLOB/BLOB_SIZE according to the key + specification given as KEY_SPEC, storing the new key in KEY_PUBLIC. + Returns zero on success or an error code. */ +static gpg_error_t +ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size, + gcry_sexp_t *key_public, + ssh_key_type_spec_t *key_spec) +{ + gpg_error_t err; + estream_t blob_stream; + + blob_stream = es_fopenmem (0, "r+b"); + if (!blob_stream) + { + err = gpg_error_from_syserror (); + goto out; + } + + err = stream_write_data (blob_stream, blob, blob_size); + if (err) + goto out; + + err = es_fseek (blob_stream, 0, SEEK_SET); + if (err) + goto out; + + err = ssh_receive_key (blob_stream, key_public, 0, 0, key_spec); + + out: + es_fclose (blob_stream); + return err; +} + + + +/* This function calculates the key grip for the key contained in the + S-Expression KEY and writes it to BUFFER, which must be large + enough to hold it. Returns usual error code. */ +static gpg_error_t +ssh_key_grip (gcry_sexp_t key, unsigned char *buffer) +{ + if (!gcry_pk_get_keygrip (key, buffer)) + { + gpg_error_t err = gcry_pk_testkey (key); + return err? err : gpg_error (GPG_ERR_INTERNAL); + } + + return 0; +} + + +/* Check whether a smartcard is available and whether it has a usable + key. Store a copy of that key at R_PK and return 0. If no key is + available store NULL at R_PK and return an error code. If CARDSN + is not NULL, a string with the serial number of the card will be + a malloced and stored there. */ +static gpg_error_t +card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn) +{ + gpg_error_t err; + char *authkeyid; + char *serialno = NULL; + unsigned char *pkbuf; + size_t pkbuflen; + gcry_sexp_t s_pk; + unsigned char grip[20]; + + *r_pk = NULL; + if (cardsn) + *cardsn = NULL; + + /* First see whether a card is available and whether the application + is supported. */ + err = agent_card_getattr (ctrl, "$AUTHKEYID", &authkeyid); + if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED ) + { + /* Ask for the serial number to reset the card. */ + err = agent_card_serialno (ctrl, &serialno); + if (err) + { + if (opt.verbose) + log_info (_("error getting serial number of card: %s\n"), + gpg_strerror (err)); + return err; + } + log_info (_("detected card with S/N: %s\n"), serialno); + err = agent_card_getattr (ctrl, "$AUTHKEYID", &authkeyid); + } + if (err) + { + log_error (_("no authentication key for ssh on card: %s\n"), + gpg_strerror (err)); + xfree (serialno); + return err; + } + + /* Get the S/N if we don't have it yet. Use the fast getattr method. */ + if (!serialno && (err = agent_card_getattr (ctrl, "SERIALNO", &serialno)) ) + { + log_error (_("error getting serial number of card: %s\n"), + gpg_strerror (err)); + xfree (authkeyid); + return err; + } + + /* Read the public key. */ + err = agent_card_readkey (ctrl, authkeyid, &pkbuf); + if (err) + { + if (opt.verbose) + log_info (_("no suitable card key found: %s\n"), gpg_strerror (err)); + xfree (serialno); + xfree (authkeyid); + return err; + } + + pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL); + err = gcry_sexp_sscan (&s_pk, NULL, (char*)pkbuf, pkbuflen); + if (err) + { + log_error ("failed to build S-Exp from received card key: %s\n", + gpg_strerror (err)); + xfree (pkbuf); + xfree (serialno); + xfree (authkeyid); + return err; + } + + err = ssh_key_grip (s_pk, grip); + if (err) + { + log_debug ("error computing keygrip from received card key: %s\n", + gcry_strerror (err)); + xfree (pkbuf); + gcry_sexp_release (s_pk); + xfree (serialno); + xfree (authkeyid); + return err; + } + + if ( agent_key_available (grip) ) + { + /* (Shadow)-key is not available in our key storage. */ + err = agent_write_shadow_key (grip, serialno, authkeyid, pkbuf, 0); + if (err) + { + xfree (pkbuf); + gcry_sexp_release (s_pk); + xfree (serialno); + xfree (authkeyid); + return err; + } + } + + if (cardsn) + { + char *dispsn; + + /* If the card handler is able to return a short serialnumber, + use that one, else use the complete serialno. */ + if (!agent_card_getattr (ctrl, "$DISPSERIALNO", &dispsn)) + { + *cardsn = xtryasprintf ("cardno:%s", dispsn); + xfree (dispsn); + } + else + *cardsn = xtryasprintf ("cardno:%s", serialno); + if (!*cardsn) + { + err = gpg_error_from_syserror (); + xfree (pkbuf); + gcry_sexp_release (s_pk); + xfree (serialno); + xfree (authkeyid); + return err; + } + } + + xfree (pkbuf); + xfree (serialno); + xfree (authkeyid); + *r_pk = s_pk; + return 0; +} + + + + +/* + + Request handler. Each handler is provided with a CTRL context, a + REQUEST object and a RESPONSE object. The actual request is to be + read from REQUEST, the response needs to be written to RESPONSE. + +*/ + + +/* Handler for the "request_identities" command. */ +static gpg_error_t +ssh_handler_request_identities (ctrl_t ctrl, + estream_t request, estream_t response) +{ + u32 key_counter; + estream_t key_blobs; + gcry_sexp_t key_public; + gpg_error_t err; + int ret; + ssh_control_file_t cf = NULL; + char *cardsn; + gpg_error_t ret_err; + + (void)request; + + /* Prepare buffer stream. */ + + key_public = NULL; + key_counter = 0; + err = 0; + + key_blobs = es_fopenmem (0, "r+b"); + if (! key_blobs) + { + err = gpg_error_from_syserror (); + goto out; + } + + /* First check whether a key is currently available in the card + reader - this should be allowed even without being listed in + sshcontrol. */ + + if (!opt.disable_scdaemon + && !card_key_available (ctrl, &key_public, &cardsn)) + { + err = ssh_send_key_public (key_blobs, key_public, cardsn); + gcry_sexp_release (key_public); + key_public = NULL; + xfree (cardsn); + if (err) + goto out; + + key_counter++; + } + + /* Then look at all the registered and non-disabled keys. */ + err = open_control_file (&cf, 0); + if (err) + goto out; + + while (!read_control_file_item (cf)) + { + unsigned char grip[20]; + + if (!cf->item.valid) + continue; /* Should not happen. */ + if (cf->item.disabled) + continue; + assert (strlen (cf->item.hexgrip) == 40); + hex2bin (cf->item.hexgrip, grip, sizeof (grip)); + + err = agent_public_key_from_file (ctrl, grip, &key_public); + if (err) + { + log_error ("%s:%d: key '%s' skipped: %s\n", + cf->fname, cf->lnr, cf->item.hexgrip, + gpg_strerror (err)); + continue; + } + + err = ssh_send_key_public (key_blobs, key_public, NULL); + if (err) + goto out; + gcry_sexp_release (key_public); + key_public = NULL; + + key_counter++; + } + err = 0; + + ret = es_fseek (key_blobs, 0, SEEK_SET); + if (ret) + { + err = gpg_error_from_syserror (); + goto out; + } + + out: + /* Send response. */ + + gcry_sexp_release (key_public); + + if (!err) + { + ret_err = stream_write_byte (response, SSH_RESPONSE_IDENTITIES_ANSWER); + if (!ret_err) + ret_err = stream_write_uint32 (response, key_counter); + if (!ret_err) + ret_err = stream_copy (response, key_blobs); + } + else + { + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + } + + es_fclose (key_blobs); + close_control_file (cf); + + return ret_err; +} + + +/* This function hashes the data contained in DATA of size DATA_N + according to the message digest algorithm specified by MD_ALGORITHM + and writes the message digest to HASH, which needs to large enough + for the digest. */ +static gpg_error_t +data_hash (unsigned char *data, size_t data_n, + int md_algorithm, unsigned char *hash) +{ + gcry_md_hash_buffer (md_algorithm, hash, data, data_n); + + return 0; +} + + +/* This function signs the data described by CTRL. If HASH is is not + NULL, (HASH,HASHLEN) overrides the hash stored in CTRL. This is to + allow the use of signature algorithms that implement the hashing + internally (e.g. Ed25519). On success the created signature is + stored in ssh format at R_SIG and it's size at R_SIGLEN; the caller + must use es_free to releaase this memory. */ +static gpg_error_t +data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec, + const void *hash, size_t hashlen, + unsigned char **r_sig, size_t *r_siglen) +{ + gpg_error_t err; + gcry_sexp_t signature_sexp = NULL; + estream_t stream = NULL; + void *blob = NULL; + size_t bloblen; + char hexgrip[40+1]; + + *r_sig = NULL; + *r_siglen = 0; + + /* Quick check to see whether we have a valid keygrip and convert it + to hex. */ + if (!ctrl->have_keygrip) + { + err = gpg_error (GPG_ERR_NO_SECKEY); + goto out; + } + bin2hex (ctrl->keygrip, 20, hexgrip); + + /* Ask for confirmation if needed. */ + if (confirm_flag_from_sshcontrol (hexgrip)) + { + gcry_sexp_t key; + char *fpr, *prompt; + char *comment = NULL; + + err = agent_raw_key_from_file (ctrl, ctrl->keygrip, &key); + if (err) + goto out; + err = ssh_get_fingerprint_string (key, &fpr); + if (!err) + { + gcry_sexp_t tmpsxp = gcry_sexp_find_token (key, "comment", 0); + if (tmpsxp) + comment = gcry_sexp_nth_string (tmpsxp, 1); + gcry_sexp_release (tmpsxp); + } + gcry_sexp_release (key); + if (err) + goto out; + prompt = xtryasprintf (L_("An ssh process requested the use of key%%0A" + " %s%%0A" + " (%s)%%0A" + "Do you want to allow this?"), + fpr, comment? comment:""); + xfree (fpr); + gcry_free (comment); + err = agent_get_confirmation (ctrl, prompt, L_("Allow"), L_("Deny"), 0); + xfree (prompt); + if (err) + goto out; + } + + /* Create signature. */ + ctrl->use_auth_call = 1; + err = agent_pksign_do (ctrl, NULL, + L_("Please enter the passphrase " + "for the ssh key%%0A %F%%0A (%c)"), + &signature_sexp, + CACHE_MODE_SSH, ttl_from_sshcontrol, + hash, hashlen); + ctrl->use_auth_call = 0; + if (err) + goto out; + + stream = es_fopenmem (0, "r+b"); + if (!stream) + { + err = gpg_error_from_syserror (); + goto out; + } + + err = stream_write_cstring (stream, spec->ssh_identifier); + if (err) + goto out; + + err = spec->signature_encoder (spec, stream, signature_sexp); + if (err) + goto out; + + err = es_fclose_snatch (stream, &blob, &bloblen); + if (err) + goto out; + stream = NULL; + + *r_sig = blob; blob = NULL; + *r_siglen = bloblen; + + out: + xfree (blob); + es_fclose (stream); + gcry_sexp_release (signature_sexp); + + return err; +} + + +/* Handler for the "sign_request" command. */ +static gpg_error_t +ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response) +{ + gcry_sexp_t key = NULL; + ssh_key_type_spec_t spec; + unsigned char hash[MAX_DIGEST_LEN]; + unsigned int hash_n; + unsigned char key_grip[20]; + unsigned char *key_blob = NULL; + u32 key_blob_size; + unsigned char *data = NULL; + unsigned char *sig = NULL; + size_t sig_n; + u32 data_size; + u32 flags; + gpg_error_t err; + gpg_error_t ret_err; + int hash_algo; + + /* Receive key. */ + + err = stream_read_string (request, 0, &key_blob, &key_blob_size); + if (err) + goto out; + + err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, &spec); + if (err) + goto out; + + /* Receive data to sign. */ + err = stream_read_string (request, 0, &data, &data_size); + if (err) + goto out; + + /* FIXME? */ + err = stream_read_uint32 (request, &flags); + if (err) + goto out; + + hash_algo = spec.hash_algo; + if (!hash_algo) + hash_algo = GCRY_MD_SHA1; /* Use the default. */ + ctrl->digest.algo = hash_algo; + if ((spec.flags & SPEC_FLAG_USE_PKCS1V2)) + ctrl->digest.raw_value = 0; + else + ctrl->digest.raw_value = 1; + + /* Calculate key grip. */ + err = ssh_key_grip (key, key_grip); + if (err) + goto out; + ctrl->have_keygrip = 1; + memcpy (ctrl->keygrip, key_grip, 20); + + /* Hash data unless we use EdDSA. */ + if ((spec.flags & SPEC_FLAG_IS_EdDSA)) + { + ctrl->digest.valuelen = 0; + } + else + { + hash_n = gcry_md_get_algo_dlen (hash_algo); + if (!hash_n) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto out; + } + err = data_hash (data, data_size, hash_algo, hash); + if (err) + goto out; + memcpy (ctrl->digest.value, hash, hash_n); + ctrl->digest.valuelen = hash_n; + } + + /* Sign data. */ + if ((spec.flags & SPEC_FLAG_IS_EdDSA)) + err = data_sign (ctrl, &spec, data, data_size, &sig, &sig_n); + else + err = data_sign (ctrl, &spec, NULL, 0, &sig, &sig_n); + + out: + /* Done. */ + if (!err) + { + ret_err = stream_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE); + if (ret_err) + goto leave; + ret_err = stream_write_string (response, sig, sig_n); + if (ret_err) + goto leave; + } + else + { + log_error ("ssh sign request failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + if (ret_err) + goto leave; + } + + leave: + + gcry_sexp_release (key); + xfree (key_blob); + xfree (data); + es_free (sig); + + return ret_err; +} + + +/* This function extracts the comment contained in the key + s-expression KEY and stores a copy in COMMENT. Returns usual error + code. */ +static gpg_error_t +ssh_key_extract_comment (gcry_sexp_t key, char **r_comment) +{ + gcry_sexp_t comment_list; + + *r_comment = NULL; + + comment_list = gcry_sexp_find_token (key, "comment", 0); + if (!comment_list) + return gpg_error (GPG_ERR_INV_SEXP); + + *r_comment = gcry_sexp_nth_string (comment_list, 1); + gcry_sexp_release (comment_list); + if (!*r_comment) + return gpg_error (GPG_ERR_INV_SEXP); + + return 0; +} + + +/* This function converts the key contained in the S-Expression KEY + into a buffer, which is protected by the passphrase PASSPHRASE. + Returns usual error code. */ +static gpg_error_t +ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase, + unsigned char **buffer, size_t *buffer_n) +{ + unsigned char *buffer_new; + unsigned int buffer_new_n; + gpg_error_t err; + + err = 0; + buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, NULL, 0); + buffer_new = xtrymalloc_secure (buffer_new_n); + if (! buffer_new) + { + err = gpg_error_from_syserror (); + goto out; + } + + gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n); + /* FIXME: guarantee? */ + + err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0, -1); + + out: + + xfree (buffer_new); + + return err; +} + + + +/* Callback function to compare the first entered PIN with the one + currently being entered. */ +static gpg_error_t +reenter_compare_cb (struct pin_entry_info_s *pi) +{ + const char *pin1 = pi->check_cb_arg; + + if (!strcmp (pin1, pi->pin)) + return 0; /* okay */ + return gpg_error (GPG_ERR_BAD_PASSPHRASE); +} + + +/* Store the ssh KEY into our local key storage and protect it after + asking for a passphrase. Cache that passphrase. TTL is the + maximum caching time for that key. If the key already exists in + our key storage, don't do anything. When entering a key also add + an entry to the sshcontrol file. */ +static gpg_error_t +ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec, + gcry_sexp_t key, int ttl, int confirm) +{ + gpg_error_t err; + unsigned char key_grip_raw[20]; + char key_grip[41]; + unsigned char *buffer = NULL; + size_t buffer_n; + char *description = NULL; + const char *description2 = L_("Please re-enter this passphrase"); + char *comment = NULL; + char *key_fpr = NULL; + const char *initial_errtext = NULL; + struct pin_entry_info_s *pi = NULL; + struct pin_entry_info_s *pi2 = NULL; + + err = ssh_key_grip (key, key_grip_raw); + if (err) + goto out; + + bin2hex (key_grip_raw, 20, key_grip); + + err = ssh_get_fingerprint_string (key, &key_fpr); + if (err) + goto out; + + /* Check whether the key is already in our key storage. Don't do + anything then besides (re-)adding it to sshcontrol. */ + if ( !agent_key_available (key_grip_raw) ) + goto key_exists; /* Yes, key is available. */ + + err = ssh_key_extract_comment (key, &comment); + if (err) + goto out; + + if ( asprintf (&description, + L_("Please enter a passphrase to protect" + " the received secret key%%0A" + " %s%%0A" + " %s%%0A" + "within gpg-agent's key storage"), + key_fpr, comment ? comment : "") < 0) + { + err = gpg_error_from_syserror (); + goto out; + } + + pi = gcry_calloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1); + if (!pi) + { + err = gpg_error_from_syserror (); + goto out; + } + pi2 = gcry_calloc_secure (1, sizeof (*pi2) + MAX_PASSPHRASE_LEN + 1); + if (!pi2) + { + err = gpg_error_from_syserror (); + goto out; + } + pi->max_length = MAX_PASSPHRASE_LEN + 1; + pi->max_tries = 1; + pi->with_repeat = 1; + pi2->max_length = MAX_PASSPHRASE_LEN + 1; + pi2->max_tries = 1; + pi2->check_cb = reenter_compare_cb; + pi2->check_cb_arg = pi->pin; + + next_try: + err = agent_askpin (ctrl, description, NULL, initial_errtext, pi, NULL, 0); + initial_errtext = NULL; + if (err) + goto out; + + /* Unless the passphrase is empty or the pinentry told us that + it already did the repetition check, ask to confirm it. */ + if (*pi->pin && !pi->repeat_okay) + { + err = agent_askpin (ctrl, description2, NULL, NULL, pi2, NULL, 0); + if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) + { /* The re-entered one did not match and the user did not + hit cancel. */ + initial_errtext = L_("does not match - try again"); + goto next_try; + } + } + + err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n); + if (err) + goto out; + + /* Store this key to our key storage. */ + err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0); + if (err) + goto out; + + /* Cache this passphrase. */ + err = agent_put_cache (key_grip, CACHE_MODE_SSH, pi->pin, ttl); + if (err) + goto out; + + key_exists: + /* And add an entry to the sshcontrol file. */ + err = add_control_entry (ctrl, spec, key_grip, key_fpr, ttl, confirm); + + + out: + if (pi2 && pi2->max_length) + wipememory (pi2->pin, pi2->max_length); + xfree (pi2); + if (pi && pi->max_length) + wipememory (pi->pin, pi->max_length); + xfree (pi); + xfree (buffer); + xfree (comment); + xfree (key_fpr); + xfree (description); + + return err; +} + + +/* This function removes the key contained in the S-Expression KEY + from the local key storage, in case it exists there. Returns usual + error code. FIXME: this function is a stub. */ +static gpg_error_t +ssh_identity_drop (gcry_sexp_t key) +{ + unsigned char key_grip[21] = { 0 }; + gpg_error_t err; + + err = ssh_key_grip (key, key_grip); + if (err) + goto out; + + key_grip[sizeof (key_grip) - 1] = 0; + + /* FIXME: What to do here - forgetting the passphrase or deleting + the key from key cache? */ + + out: + + return err; +} + +/* Handler for the "add_identity" command. */ +static gpg_error_t +ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response) +{ + gpg_error_t ret_err; + ssh_key_type_spec_t spec; + gpg_error_t err; + gcry_sexp_t key; + unsigned char b; + int confirm; + int ttl; + + confirm = 0; + key = NULL; + ttl = 0; + + /* FIXME? */ + err = ssh_receive_key (request, &key, 1, 1, &spec); + if (err) + goto out; + + while (1) + { + err = stream_read_byte (request, &b); + if (gpg_err_code (err) == GPG_ERR_EOF) + { + err = 0; + break; + } + + switch (b) + { + case SSH_OPT_CONSTRAIN_LIFETIME: + { + u32 n = 0; + + err = stream_read_uint32 (request, &n); + if (! err) + ttl = n; + break; + } + + case SSH_OPT_CONSTRAIN_CONFIRM: + { + confirm = 1; + break; + } + + default: + /* FIXME: log/bad? */ + break; + } + } + if (err) + goto out; + + err = ssh_identity_register (ctrl, &spec, key, ttl, confirm); + + out: + + gcry_sexp_release (key); + + if (! err) + ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS); + else + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + + return ret_err; +} + +/* Handler for the "remove_identity" command. */ +static gpg_error_t +ssh_handler_remove_identity (ctrl_t ctrl, + estream_t request, estream_t response) +{ + unsigned char *key_blob; + u32 key_blob_size; + gcry_sexp_t key; + gpg_error_t ret_err; + gpg_error_t err; + + (void)ctrl; + + /* Receive key. */ + + key_blob = NULL; + key = NULL; + + err = stream_read_string (request, 0, &key_blob, &key_blob_size); + if (err) + goto out; + + err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL); + if (err) + goto out; + + err = ssh_identity_drop (key); + + out: + + xfree (key_blob); + gcry_sexp_release (key); + + if (! err) + ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS); + else + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + + return ret_err; +} + +/* FIXME: stub function. Actually useful? */ +static gpg_error_t +ssh_identities_remove_all (void) +{ + gpg_error_t err; + + err = 0; + + /* FIXME: shall we remove _all_ cache entries or only those + registered through the ssh emulation? */ + + return err; +} + +/* Handler for the "remove_all_identities" command. */ +static gpg_error_t +ssh_handler_remove_all_identities (ctrl_t ctrl, + estream_t request, estream_t response) +{ + gpg_error_t ret_err; + gpg_error_t err; + + (void)ctrl; + (void)request; + + err = ssh_identities_remove_all (); + + if (! err) + ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS); + else + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + + return ret_err; +} + +/* Lock agent? FIXME: stub function. */ +static gpg_error_t +ssh_lock (void) +{ + gpg_error_t err; + + /* FIXME */ + log_error ("ssh-agent's lock command is not implemented\n"); + err = 0; + + return err; +} + +/* Unock agent? FIXME: stub function. */ +static gpg_error_t +ssh_unlock (void) +{ + gpg_error_t err; + + log_error ("ssh-agent's unlock command is not implemented\n"); + err = 0; + + return err; +} + +/* Handler for the "lock" command. */ +static gpg_error_t +ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response) +{ + gpg_error_t ret_err; + gpg_error_t err; + + (void)ctrl; + (void)request; + + err = ssh_lock (); + + if (! err) + ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS); + else + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + + return ret_err; +} + +/* Handler for the "unlock" command. */ +static gpg_error_t +ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response) +{ + gpg_error_t ret_err; + gpg_error_t err; + + (void)ctrl; + (void)request; + + err = ssh_unlock (); + + if (! err) + ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS); + else + ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE); + + return ret_err; +} + + + +/* Return the request specification for the request identified by TYPE + or NULL in case the requested request specification could not be + found. */ +static ssh_request_spec_t * +request_spec_lookup (int type) +{ + ssh_request_spec_t *spec; + unsigned int i; + + for (i = 0; i < DIM (request_specs); i++) + if (request_specs[i].type == type) + break; + if (i == DIM (request_specs)) + { + if (opt.verbose) + log_info ("ssh request %u is not supported\n", type); + spec = NULL; + } + else + spec = request_specs + i; + + return spec; +} + +/* Process a single request. The request is read from and the + response is written to STREAM_SOCK. Uses CTRL as context. Returns + zero in case of success, non zero in case of failure. */ +static int +ssh_request_process (ctrl_t ctrl, estream_t stream_sock) +{ + ssh_request_spec_t *spec; + estream_t response = NULL; + estream_t request = NULL; + unsigned char request_type; + gpg_error_t err; + int send_err = 0; + int ret; + unsigned char *request_data = NULL; + u32 request_data_size; + u32 response_size; + + /* Create memory streams for request/response data. The entire + request will be stored in secure memory, since it might contain + secret key material. The response does not have to be stored in + secure memory, since we never give out secret keys. + + Note: we only have little secure memory, but there is NO + possibility of DoS here; only trusted clients are allowed to + connect to the agent. What could happen is that the agent + returns out-of-secure-memory errors on requests in case the + agent's owner floods his own agent with many large messages. + -moritz */ + + /* Retrieve request. */ + err = stream_read_string (stream_sock, 1, &request_data, &request_data_size); + if (err) + goto out; + + if (opt.verbose > 1) + log_info ("received ssh request of length %u\n", + (unsigned int)request_data_size); + + if (! request_data_size) + { + send_err = 1; + goto out; + /* Broken request; FIXME. */ + } + + request_type = request_data[0]; + spec = request_spec_lookup (request_type); + if (! spec) + { + send_err = 1; + goto out; + /* Unknown request; FIXME. */ + } + + if (spec->secret_input) + request = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+b"); + else + request = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+b"); + if (! request) + { + err = gpg_error_from_syserror (); + goto out; + } + ret = es_setvbuf (request, NULL, _IONBF, 0); + if (ret) + { + err = gpg_error_from_syserror (); + goto out; + } + err = stream_write_data (request, request_data + 1, request_data_size - 1); + if (err) + goto out; + es_rewind (request); + + response = es_fopenmem (0, "r+b"); + if (! response) + { + err = gpg_error_from_syserror (); + goto out; + } + + if (opt.verbose) + log_info ("ssh request handler for %s (%u) started\n", + spec->identifier, spec->type); + + err = (*spec->handler) (ctrl, request, response); + + if (opt.verbose) + { + if (err) + log_info ("ssh request handler for %s (%u) failed: %s\n", + spec->identifier, spec->type, gpg_strerror (err)); + else + log_info ("ssh request handler for %s (%u) ready\n", + spec->identifier, spec->type); + } + + if (err) + { + send_err = 1; + goto out; + } + + response_size = es_ftell (response); + if (opt.verbose > 1) + log_info ("sending ssh response of length %u\n", + (unsigned int)response_size); + + err = es_fseek (response, 0, SEEK_SET); + if (err) + { + send_err = 1; + goto out; + } + + err = stream_write_uint32 (stream_sock, response_size); + if (err) + { + send_err = 1; + goto out; + } + + err = stream_copy (stream_sock, response); + if (err) + goto out; + + err = es_fflush (stream_sock); + if (err) + goto out; + + out: + + if (err && es_feof (stream_sock)) + log_error ("error occurred while processing request: %s\n", + gpg_strerror (err)); + + if (send_err) + { + if (opt.verbose > 1) + log_info ("sending ssh error response\n"); + err = stream_write_uint32 (stream_sock, 1); + if (err) + goto leave; + err = stream_write_byte (stream_sock, SSH_RESPONSE_FAILURE); + if (err) + goto leave; + } + + leave: + + es_fclose (request); + es_fclose (response); + xfree (request_data); + + return !!err; +} + + +/* Start serving client on SOCK_CLIENT. */ +void +start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client) +{ + estream_t stream_sock = NULL; + gpg_error_t err; + int ret; + + err = agent_copy_startup_env (ctrl); + if (err) + goto out; + + /* Create stream from socket. */ + stream_sock = es_fdopen (FD2INT(sock_client), "r+"); + if (!stream_sock) + { + err = gpg_error_from_syserror (); + log_error (_("failed to create stream from socket: %s\n"), + gpg_strerror (err)); + goto out; + } + /* We have to disable the estream buffering, because the estream + core doesn't know about secure memory. */ + ret = es_setvbuf (stream_sock, NULL, _IONBF, 0); + if (ret) + { + err = gpg_error_from_syserror (); + log_error ("failed to disable buffering " + "on socket stream: %s\n", gpg_strerror (err)); + goto out; + } + + /* Main processing loop. */ + while ( !ssh_request_process (ctrl, stream_sock) ) + { + /* Check wether we have reached EOF before trying to read + another request. */ + int c; + + c = es_fgetc (stream_sock); + if (c == EOF) + break; + es_ungetc (c, stream_sock); + } + + /* Reset the SCD in case it has been used. */ + agent_reset_scd (ctrl); + + + out: + if (stream_sock) + es_fclose (stream_sock); +} + + +#ifdef HAVE_W32_SYSTEM +/* Serve one ssh-agent request. This is used for the Putty support. + REQUEST is the the mmapped memory which may be accessed up to a + length of MAXREQLEN. Returns 0 on success which also indicates + that a valid SSH response message is now in REQUEST. */ +int +serve_mmapped_ssh_request (ctrl_t ctrl, + unsigned char *request, size_t maxreqlen) +{ + gpg_error_t err; + int send_err = 0; + int valid_response = 0; + ssh_request_spec_t *spec; + u32 msglen; + estream_t request_stream, response_stream; + + if (agent_copy_startup_env (ctrl)) + goto leave; /* Error setting up the environment. */ + + if (maxreqlen < 5) + goto leave; /* Caller error. */ + + msglen = uint32_construct (request[0], request[1], request[2], request[3]); + if (msglen < 1 || msglen > maxreqlen - 4) + { + log_error ("ssh message len (%u) out of range", (unsigned int)msglen); + goto leave; + } + + spec = request_spec_lookup (request[4]); + if (!spec) + { + send_err = 1; /* Unknown request type. */ + goto leave; + } + + /* Create a stream object with the data part of the request. */ + if (spec->secret_input) + request_stream = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+"); + else + request_stream = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+"); + if (!request_stream) + { + err = gpg_error_from_syserror (); + goto leave; + } + /* We have to disable the estream buffering, because the estream + core doesn't know about secure memory. */ + if (es_setvbuf (request_stream, NULL, _IONBF, 0)) + { + err = gpg_error_from_syserror (); + goto leave; + } + /* Copy the request to the stream but omit the request type. */ + err = stream_write_data (request_stream, request + 5, msglen - 1); + if (err) + goto leave; + es_rewind (request_stream); + + response_stream = es_fopenmem (0, "r+b"); + if (!response_stream) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (opt.verbose) + log_info ("ssh request handler for %s (%u) started\n", + spec->identifier, spec->type); + + err = (*spec->handler) (ctrl, request_stream, response_stream); + + if (opt.verbose) + { + if (err) + log_info ("ssh request handler for %s (%u) failed: %s\n", + spec->identifier, spec->type, gpg_strerror (err)); + else + log_info ("ssh request handler for %s (%u) ready\n", + spec->identifier, spec->type); + } + + es_fclose (request_stream); + request_stream = NULL; + + if (err) + { + send_err = 1; + goto leave; + } + + /* Put the response back into the mmapped buffer. */ + { + void *response_data; + size_t response_size; + + /* NB: In contrast to the request-stream, the response stream + includes the the message type byte. */ + if (es_fclose_snatch (response_stream, &response_data, &response_size)) + { + log_error ("snatching ssh response failed: %s", + gpg_strerror (gpg_error_from_syserror ())); + send_err = 1; /* Ooops. */ + goto leave; + } + + if (opt.verbose > 1) + log_info ("sending ssh response of length %u\n", + (unsigned int)response_size); + if (response_size > maxreqlen - 4) + { + log_error ("invalid length of the ssh response: %s", + gpg_strerror (GPG_ERR_INTERNAL)); + es_free (response_data); + send_err = 1; + goto leave; + } + + request[0] = response_size >> 24; + request[1] = response_size >> 16; + request[2] = response_size >> 8; + request[3] = response_size >> 0; + memcpy (request+4, response_data, response_size); + es_free (response_data); + valid_response = 1; + } + + leave: + if (send_err) + { + request[0] = 0; + request[1] = 0; + request[2] = 0; + request[3] = 1; + request[4] = SSH_RESPONSE_FAILURE; + valid_response = 1; + } + + /* Reset the SCD in case it has been used. */ + agent_reset_scd (ctrl); + + return valid_response? 0 : -1; +} +#endif /*HAVE_W32_SYSTEM*/ diff --git a/agent/command.c b/agent/command.c new file mode 100644 index 0000000..a2d4931 --- /dev/null +++ b/agent/command.c @@ -0,0 +1,3351 @@ +/* command.c - gpg-agent command handler + * Copyright (C) 2001-2011 Free Software Foundation, Inc. + * Copyright (C) 2001-2013 Werner Koch + * Copyright (C) 2015 g10 Code GmbH. + * + * 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 3 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, see . + */ + +/* FIXME: we should not use the default assuan buffering but setup + some buffering in secure mempory to protect session keys etc. */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include +#include "i18n.h" +#include "cvt-openpgp.h" +#include "../common/ssh-utils.h" +#include "../common/asshelp.h" +#include "../common/server-help.h" + + +/* Maximum allowed size of the inquired ciphertext. */ +#define MAXLEN_CIPHERTEXT 4096 +/* Maximum allowed size of the key parameters. */ +#define MAXLEN_KEYPARAM 1024 +/* Maximum allowed size of key data as used in inquiries (bytes). */ +#define MAXLEN_KEYDATA 8192 +/* The size of the import/export KEK key (in bytes). */ +#define KEYWRAP_KEYSIZE (128/8) + +/* A shortcut to call assuan_set_error using an gpg_err_code_t and a + text string. */ +#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) + +/* Check that the maximum digest length we support has at least the + length of the keygrip. */ +#if MAX_DIGEST_LEN < 20 +#error MAX_DIGEST_LEN shorter than keygrip +#endif + +/* Data used to associate an Assuan context with local server data. + This is this modules local part of the server_control_s struct. */ +struct server_local_s +{ + /* Our Assuan context. */ + assuan_context_t assuan_ctx; + + /* If this flag is true, the passphrase cache is used for signing + operations. It defaults to true but may be set on a per + connection base. The global option opt.ignore_cache_for_signing + takes precedence over this flag. */ + unsigned int use_cache_for_signing : 1; + + /* Flag to suppress I/O logging during a command. */ + unsigned int pause_io_logging : 1; + + /* Flag indicating that the connection is from ourselves. */ + unsigned int connect_from_self : 1; + + /* Helper flag for io_monitor to allow suppressing of our own + * greeting in some cases. See io_monitor for details. */ + unsigned int greeting_seen : 1; + + /* If this flag is set to true the agent will be terminated after + the end of the current session. */ + unsigned int stopme : 1; + + /* Flag indicating whether pinentry notifications shall be done. */ + unsigned int allow_pinentry_notify : 1; + + /* An allocated description for the next key operation. This is + used if a pinnetry needs to be popped up. */ + char *keydesc; + + /* Malloced KEK (Key-Encryption-Key) for the import_key command. */ + void *import_key; + + /* Malloced KEK for the export_key command. */ + void *export_key; + + /* Client is aware of the error code GPG_ERR_FULLY_CANCELED. */ + int allow_fully_canceled; + + /* Last CACHE_NONCE sent as status (malloced). */ + char *last_cache_nonce; + + /* Last PASSWD_NONCE sent as status (malloced). */ + char *last_passwd_nonce; +}; + + +/* An entry for the getval/putval commands. */ +struct putval_item_s +{ + struct putval_item_s *next; + size_t off; /* Offset to the value into DATA. */ + size_t len; /* Length of the value. */ + char d[1]; /* Key | Nul | value. */ +}; + + +/* A list of key value pairs fpr the getval/putval commands. */ +static struct putval_item_s *putval_list; + + + +/* To help polling clients, we keep track of the number of certain + events. This structure keeps those counters. The counters are + integers and there should be no problem if they are overflowing as + callers need to check only whether a counter changed. The actual + values are not meaningful. */ +struct +{ + /* Incremented if any of the other counters below changed. */ + unsigned int any; + + /* Incremented if a key is added or removed from the internal privat + key database. */ + unsigned int key; + + /* Incremented if a change of the card readers stati has been + detected. */ + unsigned int card; + +} eventcounter; + + + +/* Local prototypes. */ +static int command_has_option (const char *cmd, const char *cmdopt); + + + + +/* Release the memory buffer MB but first wipe out the used memory. */ +static void +clear_outbuf (membuf_t *mb) +{ + void *p; + size_t n; + + p = get_membuf (mb, &n); + if (p) + { + wipememory (p, n); + xfree (p); + } +} + + +/* Write the content of memory buffer MB as assuan data to CTX and + wipe the buffer out afterwards. */ +static gpg_error_t +write_and_clear_outbuf (assuan_context_t ctx, membuf_t *mb) +{ + gpg_error_t ae; + void *p; + size_t n; + + p = get_membuf (mb, &n); + if (!p) + return out_of_core (); + ae = assuan_send_data (ctx, p, n); + memset (p, 0, n); + xfree (p); + return ae; +} + + +/* Clear the nonces used to enable the passphrase cache for certain + multi-command command sequences. */ +static void +clear_nonce_cache (ctrl_t ctrl) +{ + if (ctrl->server_local->last_cache_nonce) + { + agent_put_cache (ctrl->server_local->last_cache_nonce, + CACHE_MODE_NONCE, NULL, 0); + xfree (ctrl->server_local->last_cache_nonce); + ctrl->server_local->last_cache_nonce = NULL; + } + if (ctrl->server_local->last_passwd_nonce) + { + agent_put_cache (ctrl->server_local->last_passwd_nonce, + CACHE_MODE_NONCE, NULL, 0); + xfree (ctrl->server_local->last_passwd_nonce); + ctrl->server_local->last_passwd_nonce = NULL; + } +} + + +/* This function is called by Libassuan whenever the client sends a + reset. It has been registered similar to the other Assuan + commands. */ +static gpg_error_t +reset_notify (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void) line; + + memset (ctrl->keygrip, 0, 20); + ctrl->have_keygrip = 0; + ctrl->digest.valuelen = 0; + + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + + clear_nonce_cache (ctrl); + + return 0; +} + + +/* Replace all '+' by a blank in the string S. */ +static void +plus_to_blank (char *s) +{ + for (; *s; s++) + { + if (*s == '+') + *s = ' '; + } +} + + +/* Parse a hex string. Return an Assuan error code or 0 on success and the + length of the parsed string in LEN. */ +static int +parse_hexstring (assuan_context_t ctx, const char *string, size_t *len) +{ + const char *p; + size_t n; + + /* parse the hash value */ + for (p=string, n=0; hexdigitp (p); p++, n++) + ; + if (*p != ' ' && *p != '\t' && *p) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid hexstring"); + if ((n&1)) + return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits"); + *len = n; + return 0; +} + + +/* Parse the keygrip in STRING into the provided buffer BUF. BUF must + provide space for 20 bytes. BUF is not changed if the function + returns an error. */ +static int +parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf) +{ + int rc; + size_t n = 0; + + rc = parse_hexstring (ctx, string, &n); + if (rc) + return rc; + n /= 2; + if (n != 20) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of keygrip"); + + if (hex2bin (string, buf, 20) < 0) + return set_error (GPG_ERR_BUG, "hex2bin"); + + return 0; +} + + +/* Write an Assuan status line. KEYWORD is the first item on the + status line. The following arguments are all separated by a space + in the output. The last argument must be a NULL. Linefeeds and + carriage returns characters (which are not allowed in an Assuan + status line) are silently quoted in C-style. */ +gpg_error_t +agent_write_status (ctrl_t ctrl, const char *keyword, ...) +{ + gpg_error_t err = 0; + va_list arg_ptr; + const char *text; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + char buf[950], *p; + size_t n; + + va_start (arg_ptr, keyword); + + p = buf; + n = 0; + while ( (text = va_arg (arg_ptr, const char *)) ) + { + if (n) + { + *p++ = ' '; + n++; + } + for ( ; *text && n < DIM (buf)-3; n++, text++) + { + if (*text == '\n') + { + *p++ = '\\'; + *p++ = 'n'; + } + else if (*text == '\r') + { + *p++ = '\\'; + *p++ = 'r'; + } + else + *p++ = *text; + } + } + *p = 0; + err = assuan_write_status (ctx, keyword, buf); + + va_end (arg_ptr); + return err; +} + + +/* This function is similar to print_assuan_status but takes a CTRL + arg instead of an assuan context as first argument. */ +gpg_error_t +agent_print_status (ctrl_t ctrl, const char *keyword, const char *format, ...) +{ + gpg_error_t err; + va_list arg_ptr; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + + va_start (arg_ptr, format); + err = vprint_assuan_status (ctx, keyword, format, arg_ptr); + va_end (arg_ptr); + return err; +} + + +/* Helper to notify the client about a launched Pinentry. Because + that might disturb some older clients, this is only done if enabled + via an option. Returns an gpg error code. */ +gpg_error_t +agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid, const char *extra) +{ + char line[256]; + + if (!ctrl || !ctrl->server_local + || !ctrl->server_local->allow_pinentry_notify) + return 0; + snprintf (line, DIM(line), "PINENTRY_LAUNCHED %lu%s%s", + pid, extra?" ":"", extra? extra:""); + return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0); +} + + +/* An agent progress callback for Libgcrypt. This has been registered + * to be called via the progress dispatcher mechanism from + * gpg-agent.c */ +static void +progress_cb (ctrl_t ctrl, const char *what, int printchar, + int current, int total) +{ + if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) + ; + else if (printchar == '\n' && what && !strcmp (what, "primegen")) + agent_print_status (ctrl, "PROGRESS", "%.20s X 100 100", what); + else + agent_print_status (ctrl, "PROGRESS", "%.20s %c %d %d", + what, printchar=='\n'?'X':printchar, current, total); +} + + +/* Helper to print a message while leaving a command. Note that this + * function does not call assuan_set_error; the caller may do this + * prior to calling us. */ +static gpg_error_t +leave_cmd (assuan_context_t ctx, gpg_error_t err) +{ + if (err) + { + const char *name = assuan_get_command_name (ctx); + if (!name) + name = "?"; + + /* Not all users of gpg-agent know about the fully canceled + error code; map it back if needed. */ + if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) + { + ctrl_t ctrl = assuan_get_pointer (ctx); + + if (!ctrl->server_local->allow_fully_canceled) + err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); + } + + /* Most code from common/ does not know the error source, thus + we fix this here. */ + if (gpg_err_source (err) == GPG_ERR_SOURCE_UNKNOWN) + err = gpg_err_make (GPG_ERR_SOURCE_DEFAULT, gpg_err_code (err)); + + if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) + log_error ("command '%s' failed: %s\n", name, + gpg_strerror (err)); + else + log_error ("command '%s' failed: %s <%s>\n", name, + gpg_strerror (err), gpg_strsource (err)); + } + return err; +} + + + +static const char hlp_geteventcounter[] = + "GETEVENTCOUNTER\n" + "\n" + "Return a a status line named EVENTCOUNTER with the current values\n" + "of all event counters. The values are decimal numbers in the range\n" + "0 to UINT_MAX and wrapping around to 0. The actual values should\n" + "not be relied upon, they shall only be used to detect a change.\n" + "\n" + "The currently defined counters are:\n" + "\n" + "ANY - Incremented with any change of any of the other counters.\n" + "KEY - Incremented for added or removed private keys.\n" + "CARD - Incremented for changes of the card readers stati."; +static gpg_error_t +cmd_geteventcounter (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void)line; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + return agent_print_status (ctrl, "EVENTCOUNTER", "%u %u %u", + eventcounter.any, + eventcounter.key, + eventcounter.card); +} + + +/* This function should be called once for all key removals or + additions. This function is assured not to do any context + switches. */ +void +bump_key_eventcounter (void) +{ + eventcounter.key++; + eventcounter.any++; +} + + +/* This function should be called for all card reader status + changes. This function is assured not to do any context + switches. */ +void +bump_card_eventcounter (void) +{ + eventcounter.card++; + eventcounter.any++; +} + + + + +static const char hlp_istrusted[] = + "ISTRUSTED \n" + "\n" + "Return OK when we have an entry with this fingerprint in our\n" + "trustlist"; +static gpg_error_t +cmd_istrusted (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc, n, i; + char *p; + char fpr[41]; + + /* Parse the fingerprint value. */ + for (p=line,n=0; hexdigitp (p); p++, n++) + ; + if (*p || !(n == 40 || n == 32)) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid fingerprint"); + i = 0; + if (n==32) + { + strcpy (fpr, "00000000"); + i += 8; + } + for (p=line; i < 40; p++, i++) + fpr[i] = *p >= 'a'? (*p & 0xdf): *p; + fpr[i] = 0; + rc = agent_istrusted (ctrl, fpr, NULL); + if (!rc || gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) + return rc; + else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF ) + return gpg_error (GPG_ERR_NOT_TRUSTED); + else + return leave_cmd (ctx, rc); +} + + +static const char hlp_listtrusted[] = + "LISTTRUSTED\n" + "\n" + "List all entries from the trustlist."; +static gpg_error_t +cmd_listtrusted (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + + (void)line; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + rc = agent_listtrusted (ctx); + return leave_cmd (ctx, rc); +} + + +static const char hlp_martrusted[] = + "MARKTRUSTED \n" + "\n" + "Store a new key in into the trustlist."; +static gpg_error_t +cmd_marktrusted (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc, n, i; + char *p; + char fpr[41]; + int flag; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + /* parse the fingerprint value */ + for (p=line,n=0; hexdigitp (p); p++, n++) + ; + if (!spacep (p) || !(n == 40 || n == 32)) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid fingerprint"); + i = 0; + if (n==32) + { + strcpy (fpr, "00000000"); + i += 8; + } + for (p=line; i < 40; p++, i++) + fpr[i] = *p >= 'a'? (*p & 0xdf): *p; + fpr[i] = 0; + + while (spacep (p)) + p++; + flag = *p++; + if ( (flag != 'S' && flag != 'P') || !spacep (p) ) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid flag - must be P or S"); + while (spacep (p)) + p++; + + rc = agent_marktrusted (ctrl, p, fpr, flag); + return leave_cmd (ctx, rc); +} + + + + +static const char hlp_havekey[] = + "HAVEKEY \n" + "\n" + "Return success if at least one of the secret keys with the given\n" + "keygrips is available."; +static gpg_error_t +cmd_havekey (assuan_context_t ctx, char *line) +{ + gpg_error_t err; + unsigned char buf[20]; + + do + { + err = parse_keygrip (ctx, line, buf); + if (err) + return err; + + if (!agent_key_available (buf)) + return 0; /* Found. */ + + while (*line && *line != ' ' && *line != '\t') + line++; + while (*line == ' ' || *line == '\t') + line++; + } + while (*line); + + /* No leave_cmd() here because errors are expected and would clutter + the log. */ + return gpg_error (GPG_ERR_NO_SECKEY); +} + + +static const char hlp_sigkey[] = + "SIGKEY \n" + "SETKEY \n" + "\n" + "Set the key used for a sign or decrypt operation."; +static gpg_error_t +cmd_sigkey (assuan_context_t ctx, char *line) +{ + int rc; + ctrl_t ctrl = assuan_get_pointer (ctx); + + rc = parse_keygrip (ctx, line, ctrl->keygrip); + if (rc) + return rc; + ctrl->have_keygrip = 1; + return 0; +} + + +static const char hlp_setkeydesc[] = + "SETKEYDESC plus_percent_escaped_string\n" + "\n" + "Set a description to be used for the next PKSIGN, PKDECRYPT, IMPORT_KEY\n" + "or EXPORT_KEY operation if this operation requires a passphrase. If\n" + "this command is not used a default text will be used. Note, that\n" + "this description implictly selects the label used for the entry\n" + "box; if the string contains the string PIN (which in general will\n" + "not be translated), \"PIN\" is used, otherwise the translation of\n" + "\"passphrase\" is used. The description string should not contain\n" + "blanks unless they are percent or '+' escaped.\n" + "\n" + "The description is only valid for the next PKSIGN, PKDECRYPT,\n" + "IMPORT_KEY, EXPORT_KEY, or DELETE_KEY operation."; +static gpg_error_t +cmd_setkeydesc (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + char *desc, *p; + + for (p=line; *p == ' '; p++) + ; + desc = p; + p = strchr (desc, ' '); + if (p) + *p = 0; /* We ignore any garbage; we might late use it for other args. */ + + if (!*desc) + return set_error (GPG_ERR_ASS_PARAMETER, "no description given"); + + /* Note, that we only need to replace the + characters and should + leave the other escaping in place because the escaped string is + send verbatim to the pinentry which does the unescaping (but not + the + replacing) */ + plus_to_blank (desc); + + xfree (ctrl->server_local->keydesc); + + if (ctrl->restricted) + { + ctrl->server_local->keydesc = strconcat + ((ctrl->restricted == 2 + ? _("Note: Request from the web browser.") + : _("Note: Request from a remote site.") ), "%0A%0A", desc, NULL); + } + else + ctrl->server_local->keydesc = xtrystrdup (desc); + if (!ctrl->server_local->keydesc) + return out_of_core (); + return 0; +} + + +static const char hlp_sethash[] = + "SETHASH (--hash=)|() \n" + "\n" + "The client can use this command to tell the server about the data\n" + "(which usually is a hash) to be signed."; +static gpg_error_t +cmd_sethash (assuan_context_t ctx, char *line) +{ + int rc; + size_t n; + char *p; + ctrl_t ctrl = assuan_get_pointer (ctx); + unsigned char *buf; + char *endp; + int algo; + + /* Parse the alternative hash options which may be used instead of + the algo number. */ + if (has_option_name (line, "--hash")) + { + if (has_option (line, "--hash=sha1")) + algo = GCRY_MD_SHA1; + else if (has_option (line, "--hash=sha224")) + algo = GCRY_MD_SHA224; + else if (has_option (line, "--hash=sha256")) + algo = GCRY_MD_SHA256; + else if (has_option (line, "--hash=sha384")) + algo = GCRY_MD_SHA384; + else if (has_option (line, "--hash=sha512")) + algo = GCRY_MD_SHA512; + else if (has_option (line, "--hash=rmd160")) + algo = GCRY_MD_RMD160; + else if (has_option (line, "--hash=md5")) + algo = GCRY_MD_MD5; + else if (has_option (line, "--hash=tls-md5sha1")) + algo = MD_USER_TLS_MD5SHA1; + else + return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm"); + } + else + algo = 0; + + line = skip_options (line); + + if (!algo) + { + /* No hash option has been given: require an algo number instead */ + algo = (int)strtoul (line, &endp, 10); + for (line = endp; *line == ' ' || *line == '\t'; line++) + ; + if (!algo || gcry_md_test_algo (algo)) + return set_error (GPG_ERR_UNSUPPORTED_ALGORITHM, NULL); + } + ctrl->digest.algo = algo; + ctrl->digest.raw_value = 0; + + /* Parse the hash value. */ + n = 0; + rc = parse_hexstring (ctx, line, &n); + if (rc) + return rc; + n /= 2; + if (algo == MD_USER_TLS_MD5SHA1 && n == 36) + ; + else if (n != 16 && n != 20 && n != 24 + && n != 28 && n != 32 && n != 48 && n != 64) + return set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash"); + + if (n > MAX_DIGEST_LEN) + return set_error (GPG_ERR_ASS_PARAMETER, "hash value to long"); + + buf = ctrl->digest.value; + ctrl->digest.valuelen = n; + for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++) + buf[n] = xtoi_2 (p); + for (; n < ctrl->digest.valuelen; n++) + buf[n] = 0; + return 0; +} + + +static const char hlp_pksign[] = + "PKSIGN [] []\n" + "\n" + "Perform the actual sign operation. Neither input nor output are\n" + "sensitive to eavesdropping."; +static gpg_error_t +cmd_pksign (assuan_context_t ctx, char *line) +{ + int rc; + cache_mode_t cache_mode = CACHE_MODE_NORMAL; + ctrl_t ctrl = assuan_get_pointer (ctx); + membuf_t outbuf; + char *cache_nonce = NULL; + char *p; + + line = skip_options (line); + + p = line; + for (p=line; *p && *p != ' ' && *p != '\t'; p++) + ; + *p = '\0'; + if (*line) + cache_nonce = xtrystrdup (line); + + if (opt.ignore_cache_for_signing) + cache_mode = CACHE_MODE_IGNORE; + else if (!ctrl->server_local->use_cache_for_signing) + cache_mode = CACHE_MODE_IGNORE; + + init_membuf (&outbuf, 512); + + rc = agent_pksign (ctrl, cache_nonce, ctrl->server_local->keydesc, + &outbuf, cache_mode); + if (rc) + clear_outbuf (&outbuf); + else + rc = write_and_clear_outbuf (ctx, &outbuf); + + xfree (cache_nonce); + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + return leave_cmd (ctx, rc); +} + + +static const char hlp_pkdecrypt[] = + "PKDECRYPT []\n" + "\n" + "Perform the actual decrypt operation. Input is not\n" + "sensitive to eavesdropping."; +static gpg_error_t +cmd_pkdecrypt (assuan_context_t ctx, char *line) +{ + int rc; + ctrl_t ctrl = assuan_get_pointer (ctx); + unsigned char *value; + size_t valuelen; + membuf_t outbuf; + int padding; + + (void)line; + + /* First inquire the data to decrypt */ + rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_CIPHERTEXT); + if (!rc) + rc = assuan_inquire (ctx, "CIPHERTEXT", + &value, &valuelen, MAXLEN_CIPHERTEXT); + if (rc) + return rc; + + init_membuf (&outbuf, 512); + + rc = agent_pkdecrypt (ctrl, ctrl->server_local->keydesc, + value, valuelen, &outbuf, &padding); + xfree (value); + if (rc) + clear_outbuf (&outbuf); + else + { + if (padding != -1) + rc = print_assuan_status (ctx, "PADDING", "%d", padding); + else + rc = 0; + if (!rc) + rc = write_and_clear_outbuf (ctx, &outbuf); + } + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + return leave_cmd (ctx, rc); +} + + +static const char hlp_genkey[] = + "GENKEY [--no-protection] [--preset] [--inq-passwd]\n" + " [--passwd-nonce=] []\n" + "\n" + "Generate a new key, store the secret part and return the public\n" + "part. Here is an example transaction:\n" + "\n" + " C: GENKEY\n" + " S: INQUIRE KEYPARAM\n" + " C: D (genkey (rsa (nbits 2048)))\n" + " C: END\n" + " S: D (public-key\n" + " S: D (rsa (n 326487324683264) (e 10001)))\n" + " S: OK key created\n" + "\n" + "When the --preset option is used the passphrase for the generated\n" + "key will be added to the cache. When --inq-passwd is used an inquire\n" + "with the keyword NEWPASSWD is used to request the passphrase for the\n" + "new key. When a --passwd-nonce is used, the corresponding cached\n" + "passphrase is used to protect the new key."; +static gpg_error_t +cmd_genkey (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + int no_protection; + unsigned char *value; + size_t valuelen; + unsigned char *newpasswd = NULL; + membuf_t outbuf; + char *cache_nonce = NULL; + char *passwd_nonce = NULL; + int opt_preset; + int opt_inq_passwd; + size_t n; + char *p, *pend; + int c; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + no_protection = has_option (line, "--no-protection"); + opt_preset = has_option (line, "--preset"); + opt_inq_passwd = has_option (line, "--inq-passwd"); + passwd_nonce = option_value (line, "--passwd-nonce"); + if (passwd_nonce) + { + for (pend = passwd_nonce; *pend && !spacep (pend); pend++) + ; + c = *pend; + *pend = '\0'; + passwd_nonce = xtrystrdup (passwd_nonce); + *pend = c; + if (!passwd_nonce) + { + rc = gpg_error_from_syserror (); + goto leave; + } + } + line = skip_options (line); + + p = line; + for (p=line; *p && *p != ' ' && *p != '\t'; p++) + ; + *p = '\0'; + if (*line) + cache_nonce = xtrystrdup (line); + + /* First inquire the parameters */ + rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_KEYPARAM); + if (!rc) + rc = assuan_inquire (ctx, "KEYPARAM", &value, &valuelen, MAXLEN_KEYPARAM); + if (rc) + return rc; + + init_membuf (&outbuf, 512); + + /* If requested, ask for the password to be used for the key. If + this is not used the regular Pinentry mechanism is used. */ + if (opt_inq_passwd && !no_protection) + { + /* (N is used as a dummy) */ + assuan_begin_confidential (ctx); + rc = assuan_inquire (ctx, "NEWPASSWD", &newpasswd, &n, 256); + assuan_end_confidential (ctx); + if (rc) + goto leave; + if (!*newpasswd) + { + /* Empty password given - switch to no-protection mode. */ + xfree (newpasswd); + newpasswd = NULL; + no_protection = 1; + } + + } + else if (passwd_nonce) + newpasswd = agent_get_cache (passwd_nonce, CACHE_MODE_NONCE); + + rc = agent_genkey (ctrl, cache_nonce, (char*)value, valuelen, no_protection, + newpasswd, opt_preset, &outbuf); + + leave: + if (newpasswd) + { + /* Assuan_inquire does not allow us to read into secure memory + thus we need to wipe it ourself. */ + wipememory (newpasswd, strlen (newpasswd)); + xfree (newpasswd); + } + xfree (value); + if (rc) + clear_outbuf (&outbuf); + else + rc = write_and_clear_outbuf (ctx, &outbuf); + xfree (cache_nonce); + xfree (passwd_nonce); + return leave_cmd (ctx, rc); +} + + + + +static const char hlp_readkey[] = + "READKEY \n" + " --card \n" + "\n" + "Return the public key for the given keygrip or keyid.\n" + "With --card, private key file with card information will be created."; +static gpg_error_t +cmd_readkey (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char grip[20]; + gcry_sexp_t s_pkey = NULL; + unsigned char *pkbuf = NULL; + char *serialno = NULL; + size_t pkbuflen; + const char *opt_card; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + opt_card = has_option_name (line, "--card"); + line = skip_options (line); + + if (opt_card) + { + const char *keyid = opt_card; + + rc = agent_card_getattr (ctrl, "SERIALNO", &serialno); + if (rc) + { + log_error (_("error getting serial number of card: %s\n"), + gpg_strerror (rc)); + goto leave; + } + + rc = agent_card_readkey (ctrl, keyid, &pkbuf); + if (rc) + goto leave; + pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL); + rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)pkbuf, pkbuflen); + if (rc) + goto leave; + + if (!gcry_pk_get_keygrip (s_pkey, grip)) + { + rc = gcry_pk_testkey (s_pkey); + if (rc == 0) + rc = gpg_error (GPG_ERR_INTERNAL); + + goto leave; + } + + rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0); + if (rc) + goto leave; + + rc = assuan_send_data (ctx, pkbuf, pkbuflen); + } + else + { + rc = parse_keygrip (ctx, line, grip); + if (rc) + goto leave; + + rc = agent_public_key_from_file (ctrl, grip, &s_pkey); + if (!rc) + { + pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); + log_assert (pkbuflen); + pkbuf = xtrymalloc (pkbuflen); + if (!pkbuf) + rc = gpg_error_from_syserror (); + else + { + gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen); + rc = assuan_send_data (ctx, pkbuf, pkbuflen); + } + } + } + + leave: + xfree (serialno); + xfree (pkbuf); + gcry_sexp_release (s_pkey); + return leave_cmd (ctx, rc); +} + + + +static const char hlp_keyinfo[] = + "KEYINFO [--[ssh-]list] [--data] [--ssh-fpr] [--with-ssh] \n" + "\n" + "Return information about the key specified by the KEYGRIP. If the\n" + "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n" + "--list is given the keygrip is ignored and information about all\n" + "available keys are returned. If --ssh-list is given information\n" + "about all keys listed in the sshcontrol are returned. With --with-ssh\n" + "information from sshcontrol is always added to the info. Unless --data\n" + "is given, the information is returned as a status line using the format:\n" + "\n" + " KEYINFO \n" + "\n" + "KEYGRIP is the keygrip.\n" + "\n" + "TYPE is describes the type of the key:\n" + " 'D' - Regular key stored on disk,\n" + " 'T' - Key is stored on a smartcard (token),\n" + " 'X' - Unknown type,\n" + " '-' - Key is missing.\n" + "\n" + "SERIALNO is an ASCII string with the serial number of the\n" + " smartcard. If the serial number is not known a single\n" + " dash '-' is used instead.\n" + "\n" + "IDSTR is the IDSTR used to distinguish keys on a smartcard. If it\n" + " is not known a dash is used instead.\n" + "\n" + "CACHED is 1 if the passphrase for the key was found in the key cache.\n" + " If not, a '-' is used instead.\n" + "\n" + "PROTECTION describes the key protection type:\n" + " 'P' - The key is protected with a passphrase,\n" + " 'C' - The key is not protected,\n" + " '-' - Unknown protection.\n" + "\n" + "FPR returns the formatted ssh-style fingerprint of the key. It is only\n" + " printed if the option --ssh-fpr has been used. It defaults to '-'.\n" + "\n" + "TTL is the TTL in seconds for that key or '-' if n/a.\n" + "\n" + "FLAGS is a word consisting of one-letter flags:\n" + " 'D' - The key has been disabled,\n" + " 'S' - The key is listed in sshcontrol (requires --with-ssh),\n" + " 'c' - Use of the key needs to be confirmed,\n" + " '-' - No flags given.\n" + "\n" + "More information may be added in the future."; +static gpg_error_t +do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, + int data, int with_ssh_fpr, int in_ssh, + int ttl, int disabled, int confirm) +{ + gpg_error_t err; + char hexgrip[40+1]; + char *fpr = NULL; + int keytype; + unsigned char *shadow_info = NULL; + char *serialno = NULL; + char *idstr = NULL; + const char *keytypestr; + const char *cached; + const char *protectionstr; + char *pw; + int missing_key = 0; + char ttlbuf[20]; + char flagsbuf[5]; + + err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info); + if (err) + { + if (in_ssh && gpg_err_code (err) == GPG_ERR_NOT_FOUND) + missing_key = 1; + else + goto leave; + } + + /* Reformat the grip so that we use uppercase as good style. */ + bin2hex (grip, 20, hexgrip); + + if (ttl > 0) + snprintf (ttlbuf, sizeof ttlbuf, "%d", ttl); + else + strcpy (ttlbuf, "-"); + + *flagsbuf = 0; + if (disabled) + strcat (flagsbuf, "D"); + if (in_ssh) + strcat (flagsbuf, "S"); + if (confirm) + strcat (flagsbuf, "c"); + if (!*flagsbuf) + strcpy (flagsbuf, "-"); + + + if (missing_key) + { + protectionstr = "-"; keytypestr = "-"; + } + else + { + switch (keytype) + { + case PRIVATE_KEY_CLEAR: + case PRIVATE_KEY_OPENPGP_NONE: + protectionstr = "C"; keytypestr = "D"; + break; + case PRIVATE_KEY_PROTECTED: protectionstr = "P"; keytypestr = "D"; + break; + case PRIVATE_KEY_SHADOWED: protectionstr = "-"; keytypestr = "T"; + break; + default: protectionstr = "-"; keytypestr = "X"; + break; + } + } + + /* Compute the ssh fingerprint if requested. */ + if (with_ssh_fpr) + { + gcry_sexp_t key; + + if (!agent_raw_key_from_file (ctrl, grip, &key)) + { + ssh_get_fingerprint_string (key, &fpr); + gcry_sexp_release (key); + } + } + + /* Here we have a little race by doing the cache check separately + from the retrieval function. Given that the cache flag is only a + hint, it should not really matter. */ + pw = agent_get_cache (hexgrip, CACHE_MODE_NORMAL); + cached = pw ? "1" : "-"; + xfree (pw); + + if (shadow_info) + { + err = parse_shadow_info (shadow_info, &serialno, &idstr, NULL); + if (err) + goto leave; + } + + if (!data) + err = agent_write_status (ctrl, "KEYINFO", + hexgrip, + keytypestr, + serialno? serialno : "-", + idstr? idstr : "-", + cached, + protectionstr, + fpr? fpr : "-", + ttlbuf, + flagsbuf, + NULL); + else + { + char *string; + + string = xtryasprintf ("%s %s %s %s %s %s %s %s %s\n", + hexgrip, keytypestr, + serialno? serialno : "-", + idstr? idstr : "-", cached, protectionstr, + fpr? fpr : "-", + ttlbuf, + flagsbuf); + if (!string) + err = gpg_error_from_syserror (); + else + err = assuan_send_data (ctx, string, strlen(string)); + xfree (string); + } + + leave: + xfree (fpr); + xfree (shadow_info); + xfree (serialno); + xfree (idstr); + return err; +} + + +/* Entry int for the command KEYINFO. This function handles the + command option processing. For details see hlp_keyinfo above. */ +static gpg_error_t +cmd_keyinfo (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int err; + unsigned char grip[20]; + DIR *dir = NULL; + int list_mode; + int opt_data, opt_ssh_fpr, opt_with_ssh; + ssh_control_file_t cf = NULL; + char hexgrip[41]; + int disabled, ttl, confirm, is_ssh; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + if (has_option (line, "--ssh-list")) + list_mode = 2; + else + list_mode = has_option (line, "--list"); + opt_data = has_option (line, "--data"); + opt_ssh_fpr = has_option (line, "--ssh-fpr"); + opt_with_ssh = has_option (line, "--with-ssh"); + line = skip_options (line); + + if (opt_with_ssh || list_mode == 2) + cf = ssh_open_control_file (); + + if (list_mode == 2) + { + if (cf) + { + while (!ssh_read_control_file (cf, hexgrip, + &disabled, &ttl, &confirm)) + { + if (hex2bin (hexgrip, grip, 20) < 0 ) + continue; /* Bad hex string. */ + err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, 1, + ttl, disabled, confirm); + if (err) + goto leave; + } + } + err = 0; + } + else if (list_mode) + { + char *dirname; + struct dirent *dir_entry; + + dirname = make_filename_try (gnupg_homedir (), + GNUPG_PRIVATE_KEYS_DIR, NULL); + if (!dirname) + { + err = gpg_error_from_syserror (); + goto leave; + } + dir = opendir (dirname); + if (!dir) + { + err = gpg_error_from_syserror (); + xfree (dirname); + goto leave; + } + xfree (dirname); + + while ( (dir_entry = readdir (dir)) ) + { + if (strlen (dir_entry->d_name) != 44 + || strcmp (dir_entry->d_name + 40, ".key")) + continue; + strncpy (hexgrip, dir_entry->d_name, 40); + hexgrip[40] = 0; + + if ( hex2bin (hexgrip, grip, 20) < 0 ) + continue; /* Bad hex string. */ + + disabled = ttl = confirm = is_ssh = 0; + if (opt_with_ssh) + { + err = ssh_search_control_file (cf, hexgrip, + &disabled, &ttl, &confirm); + if (!err) + is_ssh = 1; + else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) + goto leave; + } + + err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, is_ssh, + ttl, disabled, confirm); + if (err) + goto leave; + } + err = 0; + } + else + { + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + disabled = ttl = confirm = is_ssh = 0; + if (opt_with_ssh) + { + err = ssh_search_control_file (cf, line, + &disabled, &ttl, &confirm); + if (!err) + is_ssh = 1; + else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) + goto leave; + } + + err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, is_ssh, + ttl, disabled, confirm); + } + + leave: + ssh_close_control_file (cf); + if (dir) + closedir (dir); + if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) + leave_cmd (ctx, err); + return err; +} + + + +/* Helper for cmd_get_passphrase. */ +static int +send_back_passphrase (assuan_context_t ctx, int via_data, const char *pw) +{ + size_t n; + int rc; + + assuan_begin_confidential (ctx); + n = strlen (pw); + if (via_data) + rc = assuan_send_data (ctx, pw, n); + else + { + char *p = xtrymalloc_secure (n*2+1); + if (!p) + rc = gpg_error_from_syserror (); + else + { + bin2hex (pw, n, p); + rc = assuan_set_okay_line (ctx, p); + xfree (p); + } + } + return rc; +} + + +static const char hlp_get_passphrase[] = + "GET_PASSPHRASE [--data] [--check] [--no-ask] [--repeat[=N]]\n" + " [--qualitybar] \n" + " [ ]\n" + "\n" + "This function is usually used to ask for a passphrase to be used\n" + "for conventional encryption, but may also be used by programs which\n" + "need specal handling of passphrases. This command uses a syntax\n" + "which helps clients to use the agent with minimum effort. The\n" + "agent either returns with an error or with a OK followed by the hex\n" + "encoded passphrase. Note that the length of the strings is\n" + "implicitly limited by the maximum length of a command.\n" + "\n" + "If the option \"--data\" is used the passphrase is returned by usual\n" + "data lines and not on the okay line.\n" + "\n" + "If the option \"--check\" is used the passphrase constraints checks as\n" + "implemented by gpg-agent are applied. A check is not done if the\n" + "passphrase has been found in the cache.\n" + "\n" + "If the option \"--no-ask\" is used and the passphrase is not in the\n" + "cache the user will not be asked to enter a passphrase but the error\n" + "code GPG_ERR_NO_DATA is returned. \n" + "\n" + "If the option \"--qualitybar\" is used a visual indication of the\n" + "entered passphrase quality is shown. (Unless no minimum passphrase\n" + "length has been configured.)"; +static gpg_error_t +cmd_get_passphrase (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + char *pw; + char *response; + char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL; + const char *desc2 = _("Please re-enter this passphrase"); + char *p; + int opt_data, opt_check, opt_no_ask, opt_qualbar; + int opt_repeat = 0; + char *entry_errtext = NULL; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + opt_data = has_option (line, "--data"); + opt_check = has_option (line, "--check"); + opt_no_ask = has_option (line, "--no-ask"); + if (has_option_name (line, "--repeat")) + { + p = option_value (line, "--repeat"); + if (p) + opt_repeat = atoi (p); + else + opt_repeat = 1; + } + opt_qualbar = has_option (line, "--qualitybar"); + line = skip_options (line); + + cacheid = line; + p = strchr (cacheid, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + errtext = p; + p = strchr (errtext, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + prompt = p; + p = strchr (prompt, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + desc = p; + p = strchr (desc, ' '); + if (p) + *p = 0; /* Ignore trailing garbage. */ + } + } + } + if (!*cacheid || strlen (cacheid) > 50) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of cacheID"); + if (!desc) + return set_error (GPG_ERR_ASS_PARAMETER, "no description given"); + + if (!strcmp (cacheid, "X")) + cacheid = NULL; + if (!strcmp (errtext, "X")) + errtext = NULL; + if (!strcmp (prompt, "X")) + prompt = NULL; + if (!strcmp (desc, "X")) + desc = NULL; + + pw = cacheid ? agent_get_cache (cacheid, CACHE_MODE_USER) : NULL; + if (pw) + { + rc = send_back_passphrase (ctx, opt_data, pw); + xfree (pw); + } + else if (opt_no_ask) + rc = gpg_error (GPG_ERR_NO_DATA); + else + { + /* Note, that we only need to replace the + characters and + should leave the other escaping in place because the escaped + string is send verbatim to the pinentry which does the + unescaping (but not the + replacing) */ + if (errtext) + plus_to_blank (errtext); + if (prompt) + plus_to_blank (prompt); + if (desc) + plus_to_blank (desc); + + next_try: + rc = agent_get_passphrase (ctrl, &response, desc, prompt, + entry_errtext? entry_errtext:errtext, + opt_qualbar, cacheid, CACHE_MODE_USER); + xfree (entry_errtext); + entry_errtext = NULL; + if (!rc) + { + int i; + + if (opt_check + && check_passphrase_constraints (ctrl, response, &entry_errtext)) + { + xfree (response); + goto next_try; + } + for (i = 0; i < opt_repeat; i++) + { + char *response2; + + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + break; + + rc = agent_get_passphrase (ctrl, &response2, desc2, prompt, + errtext, 0, + cacheid, CACHE_MODE_USER); + if (rc) + break; + if (strcmp (response2, response)) + { + xfree (response2); + xfree (response); + entry_errtext = try_percent_escape + (_("does not match - try again"), NULL); + if (!entry_errtext) + { + rc = gpg_error_from_syserror (); + break; + } + goto next_try; + } + xfree (response2); + } + if (!rc) + { + if (cacheid) + agent_put_cache (cacheid, CACHE_MODE_USER, response, 0); + rc = send_back_passphrase (ctx, opt_data, response); + } + xfree (response); + } + } + + return leave_cmd (ctx, rc); +} + + +static const char hlp_clear_passphrase[] = + "CLEAR_PASSPHRASE [--mode=normal] \n" + "\n" + "may be used to invalidate the cache entry for a passphrase. The\n" + "function returns with OK even when there is no cached passphrase.\n" + "The --mode=normal option is used to clear an entry for a cacheid\n" + "added by the agent.\n"; +static gpg_error_t +cmd_clear_passphrase (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + char *cacheid = NULL; + char *p; + int opt_normal; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + opt_normal = has_option (line, "--mode=normal"); + line = skip_options (line); + + /* parse the stuff */ + for (p=line; *p == ' '; p++) + ; + cacheid = p; + p = strchr (cacheid, ' '); + if (p) + *p = 0; /* ignore garbage */ + if (!*cacheid || strlen (cacheid) > 50) + return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of cacheID"); + + agent_put_cache (cacheid, opt_normal ? CACHE_MODE_NORMAL : CACHE_MODE_USER, + NULL, 0); + + agent_clear_passphrase (ctrl, cacheid, + opt_normal ? CACHE_MODE_NORMAL : CACHE_MODE_USER); + + return 0; +} + + +static const char hlp_get_confirmation[] = + "GET_CONFIRMATION \n" + "\n" + "This command may be used to ask for a simple confirmation.\n" + "DESCRIPTION is displayed along with a Okay and Cancel button. This\n" + "command uses a syntax which helps clients to use the agent with\n" + "minimum effort. The agent either returns with an error or with a\n" + "OK. Note, that the length of DESCRIPTION is implicitly limited by\n" + "the maximum length of a command. DESCRIPTION should not contain\n" + "any spaces, those must be encoded either percent escaped or simply\n" + "as '+'."; +static gpg_error_t +cmd_get_confirmation (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + char *desc = NULL; + char *p; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + /* parse the stuff */ + for (p=line; *p == ' '; p++) + ; + desc = p; + p = strchr (desc, ' '); + if (p) + *p = 0; /* We ignore any garbage -may be later used for other args. */ + + if (!*desc) + return set_error (GPG_ERR_ASS_PARAMETER, "no description given"); + + if (!strcmp (desc, "X")) + desc = NULL; + + /* Note, that we only need to replace the + characters and should + leave the other escaping in place because the escaped string is + send verbatim to the pinentry which does the unescaping (but not + the + replacing) */ + if (desc) + plus_to_blank (desc); + + rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); + return leave_cmd (ctx, rc); +} + + + +static const char hlp_learn[] = + "LEARN [--send] [--sendinfo] [--force]\n" + "\n" + "Learn something about the currently inserted smartcard. With\n" + "--sendinfo information about the card is returned; with --send\n" + "the available certificates are returned as D lines; with --force\n" + "private key storage will be updated by the result."; +static gpg_error_t +cmd_learn (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + int send, sendinfo, force; + + send = has_option (line, "--send"); + sendinfo = send? 1 : has_option (line, "--sendinfo"); + force = has_option (line, "--force"); + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + err = agent_handle_learn (ctrl, send, sendinfo? ctx : NULL, force); + return leave_cmd (ctx, err); +} + + + +static const char hlp_passwd[] = + "PASSWD [--cache-nonce=] [--passwd-nonce=] [--preset]\n" + " [--verify] \n" + "\n" + "Change the passphrase/PIN for the key identified by keygrip in LINE. If\n" + "--preset is used then the new passphrase will be added to the cache.\n" + "If --verify is used the command asks for the passphrase and verifies\n" + "that the passphrase valid.\n"; +static gpg_error_t +cmd_passwd (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + int c; + char *cache_nonce = NULL; + char *passwd_nonce = NULL; + unsigned char grip[20]; + gcry_sexp_t s_skey = NULL; + unsigned char *shadow_info = NULL; + char *passphrase = NULL; + char *pend; + int opt_preset, opt_verify; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + opt_preset = has_option (line, "--preset"); + cache_nonce = option_value (line, "--cache-nonce"); + opt_verify = has_option (line, "--verify"); + if (cache_nonce) + { + for (pend = cache_nonce; *pend && !spacep (pend); pend++) + ; + c = *pend; + *pend = '\0'; + cache_nonce = xtrystrdup (cache_nonce); + *pend = c; + if (!cache_nonce) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + passwd_nonce = option_value (line, "--passwd-nonce"); + if (passwd_nonce) + { + for (pend = passwd_nonce; *pend && !spacep (pend); pend++) + ; + c = *pend; + *pend = '\0'; + passwd_nonce = xtrystrdup (passwd_nonce); + *pend = c; + if (!passwd_nonce) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + line = skip_options (line); + + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + + ctrl->in_passwd++; + err = agent_key_from_file (ctrl, + opt_verify? NULL : cache_nonce, + ctrl->server_local->keydesc, + grip, &shadow_info, CACHE_MODE_IGNORE, NULL, + &s_skey, &passphrase); + if (err) + ; + else if (shadow_info) + { + log_error ("changing a smartcard PIN is not yet supported\n"); + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else if (opt_verify) + { + /* All done. */ + if (passphrase) + { + if (!passwd_nonce) + { + char buf[12]; + gcry_create_nonce (buf, 12); + passwd_nonce = bin2hex (buf, 12, NULL); + } + if (passwd_nonce + && !agent_put_cache (passwd_nonce, CACHE_MODE_NONCE, + passphrase, CACHE_TTL_NONCE)) + { + assuan_write_status (ctx, "PASSWD_NONCE", passwd_nonce); + xfree (ctrl->server_local->last_passwd_nonce); + ctrl->server_local->last_passwd_nonce = passwd_nonce; + passwd_nonce = NULL; + } + } + } + else + { + char *newpass = NULL; + + if (passwd_nonce) + newpass = agent_get_cache (passwd_nonce, CACHE_MODE_NONCE); + err = agent_protect_and_store (ctrl, s_skey, &newpass); + if (!err && passphrase) + { + /* A passphrase existed on the old key and the change was + successful. Return a nonce for that old passphrase to + let the caller try to unprotect the other subkeys with + the same key. */ + if (!cache_nonce) + { + char buf[12]; + gcry_create_nonce (buf, 12); + cache_nonce = bin2hex (buf, 12, NULL); + } + if (cache_nonce + && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE, + passphrase, CACHE_TTL_NONCE)) + { + assuan_write_status (ctx, "CACHE_NONCE", cache_nonce); + xfree (ctrl->server_local->last_cache_nonce); + ctrl->server_local->last_cache_nonce = cache_nonce; + cache_nonce = NULL; + } + if (newpass) + { + /* If we have a new passphrase (which might be empty) we + store it under a passwd nonce so that the caller may + send that nonce again to use it for another key. */ + if (!passwd_nonce) + { + char buf[12]; + gcry_create_nonce (buf, 12); + passwd_nonce = bin2hex (buf, 12, NULL); + } + if (passwd_nonce + && !agent_put_cache (passwd_nonce, CACHE_MODE_NONCE, + newpass, CACHE_TTL_NONCE)) + { + assuan_write_status (ctx, "PASSWD_NONCE", passwd_nonce); + xfree (ctrl->server_local->last_passwd_nonce); + ctrl->server_local->last_passwd_nonce = passwd_nonce; + passwd_nonce = NULL; + } + } + } + if (!err && opt_preset) + { + char hexgrip[40+1]; + bin2hex(grip, 20, hexgrip); + err = agent_put_cache (hexgrip, CACHE_MODE_ANY, newpass, + ctrl->cache_ttl_opt_preset); + } + xfree (newpass); + } + ctrl->in_passwd--; + + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + + leave: + xfree (passphrase); + gcry_sexp_release (s_skey); + xfree (shadow_info); + xfree (cache_nonce); + xfree (passwd_nonce); + return leave_cmd (ctx, err); +} + + +static const char hlp_preset_passphrase[] = + "PRESET_PASSPHRASE [--inquire] []\n" + "\n" + "Set the cached passphrase/PIN for the key identified by the keygrip\n" + "to passwd for the given time, where -1 means infinite and 0 means\n" + "the default (currently only a timeout of -1 is allowed, which means\n" + "to never expire it). If passwd is not provided, ask for it via the\n" + "pinentry module unless --inquire is passed in which case the passphrase\n" + "is retrieved from the client via a server inquire.\n"; +static gpg_error_t +cmd_preset_passphrase (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + char *grip_clear = NULL; + unsigned char *passphrase = NULL; + int ttl; + size_t len; + int opt_inquire; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + if (!opt.allow_preset_passphrase) + return set_error (GPG_ERR_NOT_SUPPORTED, "no --allow-preset-passphrase"); + + opt_inquire = has_option (line, "--inquire"); + line = skip_options (line); + grip_clear = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + return gpg_error (GPG_ERR_MISSING_VALUE); + *line = '\0'; + line++; + while (*line && (*line == ' ' || *line == '\t')) + line++; + + /* Currently, only infinite timeouts are allowed. */ + ttl = -1; + if (line[0] != '-' || line[1] != '1') + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + line++; + line++; + while (!(*line != ' ' && *line != '\t')) + line++; + + /* Syntax check the hexstring. */ + len = 0; + rc = parse_hexstring (ctx, line, &len); + if (rc) + return rc; + line[len] = '\0'; + + /* If there is a passphrase, use it. Currently, a passphrase is + required. */ + if (*line) + { + if (opt_inquire) + { + rc = set_error (GPG_ERR_ASS_PARAMETER, + "both --inquire and passphrase specified"); + goto leave; + } + + /* Do in-place conversion. */ + passphrase = line; + if (!hex2str (passphrase, passphrase, strlen (passphrase)+1, NULL)) + rc = set_error (GPG_ERR_ASS_PARAMETER, "invalid hexstring"); + } + else if (opt_inquire) + { + /* Note that the passphrase will be truncated at any null byte and the + * limit is 480 characters. */ + size_t maxlen = 480; + + rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%zu", maxlen); + if (!rc) + rc = assuan_inquire (ctx, "PASSPHRASE", &passphrase, &len, maxlen); + } + else + rc = set_error (GPG_ERR_NOT_IMPLEMENTED, "passphrase is required"); + + if (!rc) + { + rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl); + if (opt_inquire) + xfree (passphrase); + } + +leave: + return leave_cmd (ctx, rc); +} + + + +static const char hlp_scd[] = + "SCD \n" + " \n" + "This is a general quote command to redirect everything to the\n" + "SCdaemon."; +static gpg_error_t +cmd_scd (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + rc = divert_generic_cmd (ctrl, line, ctx); + + return rc; +} + + + +static const char hlp_keywrap_key[] = + "KEYWRAP_KEY [--clear] \n" + "\n" + "Return a key to wrap another key. For now the key is returned\n" + "verbatim and and thus makes not much sense because an eavesdropper on\n" + "the gpg-agent connection will see the key as well as the wrapped key.\n" + "However, this function may either be equipped with a public key\n" + "mechanism or not used at all if the key is a pre-shared key. In any\n" + "case wrapping the import and export of keys is a requirement for\n" + "certain cryptographic validations and thus useful. The key persists\n" + "until a RESET command but may be cleared using the option --clear.\n" + "\n" + "Supported modes are:\n" + " --import - Return a key to import a key into gpg-agent\n" + " --export - Return a key to export a key from gpg-agent"; +static gpg_error_t +cmd_keywrap_key (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + int clearopt = has_option (line, "--clear"); + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + assuan_begin_confidential (ctx); + if (has_option (line, "--import")) + { + xfree (ctrl->server_local->import_key); + if (clearopt) + ctrl->server_local->import_key = NULL; + else if (!(ctrl->server_local->import_key = + gcry_random_bytes (KEYWRAP_KEYSIZE, GCRY_STRONG_RANDOM))) + err = gpg_error_from_syserror (); + else + err = assuan_send_data (ctx, ctrl->server_local->import_key, + KEYWRAP_KEYSIZE); + } + else if (has_option (line, "--export")) + { + xfree (ctrl->server_local->export_key); + if (clearopt) + ctrl->server_local->export_key = NULL; + else if (!(ctrl->server_local->export_key = + gcry_random_bytes (KEYWRAP_KEYSIZE, GCRY_STRONG_RANDOM))) + err = gpg_error_from_syserror (); + else + err = assuan_send_data (ctx, ctrl->server_local->export_key, + KEYWRAP_KEYSIZE); + } + else + err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for MODE"); + assuan_end_confidential (ctx); + + return leave_cmd (ctx, err); +} + + + +static const char hlp_import_key[] = + "IMPORT_KEY [--unattended] [--force] []\n" + "\n" + "Import a secret key into the key store. The key is expected to be\n" + "encrypted using the current session's key wrapping key (cf. command\n" + "KEYWRAP_KEY) using the AESWRAP-128 algorithm. This function takes\n" + "no arguments but uses the inquiry \"KEYDATA\" to ask for the actual\n" + "key data. The unwrapped key must be a canonical S-expression. The\n" + "option --unattended tries to import the key as-is without any\n" + "re-encryption. Existing key can be overwritten with --force."; +static gpg_error_t +cmd_import_key (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + int opt_unattended; + int force; + unsigned char *wrappedkey = NULL; + size_t wrappedkeylen; + gcry_cipher_hd_t cipherhd = NULL; + unsigned char *key = NULL; + size_t keylen, realkeylen; + char *passphrase = NULL; + unsigned char *finalkey = NULL; + size_t finalkeylen; + unsigned char grip[20]; + gcry_sexp_t openpgp_sexp = NULL; + char *cache_nonce = NULL; + char *p; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + if (!ctrl->server_local->import_key) + { + err = gpg_error (GPG_ERR_MISSING_KEY); + goto leave; + } + + opt_unattended = has_option (line, "--unattended"); + force = has_option (line, "--force"); + line = skip_options (line); + + p = line; + for (p=line; *p && *p != ' ' && *p != '\t'; p++) + ; + *p = '\0'; + if (*line) + cache_nonce = xtrystrdup (line); + + assuan_begin_confidential (ctx); + err = assuan_inquire (ctx, "KEYDATA", + &wrappedkey, &wrappedkeylen, MAXLEN_KEYDATA); + assuan_end_confidential (ctx); + if (err) + goto leave; + if (wrappedkeylen < 24) + { + err = gpg_error (GPG_ERR_INV_LENGTH); + goto leave; + } + keylen = wrappedkeylen - 8; + key = xtrymalloc_secure (keylen); + if (!key) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) + goto leave; + err = gcry_cipher_setkey (cipherhd, + ctrl->server_local->import_key, KEYWRAP_KEYSIZE); + if (err) + goto leave; + err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); + if (err) + goto leave; + gcry_cipher_close (cipherhd); + cipherhd = NULL; + xfree (wrappedkey); + wrappedkey = NULL; + + realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err); + if (!realkeylen) + goto leave; /* Invalid canonical encoded S-expression. */ + + err = keygrip_from_canon_sexp (key, realkeylen, grip); + if (err) + { + /* This might be due to an unsupported S-expression format. + Check whether this is openpgp-private-key and trigger that + import code. */ + if (!gcry_sexp_sscan (&openpgp_sexp, NULL, key, realkeylen)) + { + const char *tag; + size_t taglen; + + tag = gcry_sexp_nth_data (openpgp_sexp, 0, &taglen); + if (tag && taglen == 19 && !memcmp (tag, "openpgp-private-key", 19)) + ; + else + { + gcry_sexp_release (openpgp_sexp); + openpgp_sexp = NULL; + } + } + if (!openpgp_sexp) + goto leave; /* Note that ERR is still set. */ + } + + + if (openpgp_sexp) + { + /* In most cases the key is encrypted and thus the conversion + function from the OpenPGP format to our internal format will + ask for a passphrase. That passphrase will be returned and + used to protect the key using the same code as for regular + key import. */ + + xfree (key); + key = NULL; + err = convert_from_openpgp (ctrl, openpgp_sexp, force, grip, + ctrl->server_local->keydesc, cache_nonce, + &key, opt_unattended? NULL : &passphrase); + if (err) + goto leave; + realkeylen = gcry_sexp_canon_len (key, 0, NULL, &err); + if (!realkeylen) + goto leave; /* Invalid canonical encoded S-expression. */ + if (passphrase) + { + assert (!opt_unattended); + if (!cache_nonce) + { + char buf[12]; + gcry_create_nonce (buf, 12); + cache_nonce = bin2hex (buf, 12, NULL); + } + if (cache_nonce + && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE, + passphrase, CACHE_TTL_NONCE)) + assuan_write_status (ctx, "CACHE_NONCE", cache_nonce); + } + } + else if (opt_unattended) + { + err = set_error (GPG_ERR_ASS_PARAMETER, + "\"--unattended\" may only be used with OpenPGP keys"); + goto leave; + } + else + { + if (!force && !agent_key_available (grip)) + err = gpg_error (GPG_ERR_EEXIST); + else + { + char *prompt = xtryasprintf + (_("Please enter the passphrase to protect the " + "imported object within the %s system."), GNUPG_NAME); + if (!prompt) + err = gpg_error_from_syserror (); + else + err = agent_ask_new_passphrase (ctrl, prompt, &passphrase); + xfree (prompt); + } + if (err) + goto leave; + } + + if (passphrase) + { + err = agent_protect (key, passphrase, &finalkey, &finalkeylen, + ctrl->s2k_count, -1); + if (!err) + err = agent_write_private_key (grip, finalkey, finalkeylen, force); + } + else + err = agent_write_private_key (grip, key, realkeylen, force); + + leave: + gcry_sexp_release (openpgp_sexp); + xfree (finalkey); + xfree (passphrase); + xfree (key); + gcry_cipher_close (cipherhd); + xfree (wrappedkey); + xfree (cache_nonce); + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + return leave_cmd (ctx, err); +} + + + +static const char hlp_export_key[] = + "EXPORT_KEY [--cache-nonce=] [--openpgp] \n" + "\n" + "Export a secret key from the key store. The key will be encrypted\n" + "using the current session's key wrapping key (cf. command KEYWRAP_KEY)\n" + "using the AESWRAP-128 algorithm. The caller needs to retrieve that key\n" + "prior to using this command. The function takes the keygrip as argument.\n" + "\n" + "If --openpgp is used, the secret key material will be exported in RFC 4880\n" + "compatible passphrase-protected form. Without --openpgp, the secret key\n" + "material will be exported in the clear (after prompting the user to unlock\n" + "it, if needed).\n"; +static gpg_error_t +cmd_export_key (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char grip[20]; + gcry_sexp_t s_skey = NULL; + unsigned char *key = NULL; + size_t keylen; + gcry_cipher_hd_t cipherhd = NULL; + unsigned char *wrappedkey = NULL; + size_t wrappedkeylen; + int openpgp; + char *cache_nonce; + char *passphrase = NULL; + unsigned char *shadow_info = NULL; + char *pend; + int c; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + openpgp = has_option (line, "--openpgp"); + cache_nonce = option_value (line, "--cache-nonce"); + if (cache_nonce) + { + for (pend = cache_nonce; *pend && !spacep (pend); pend++) + ; + c = *pend; + *pend = '\0'; + cache_nonce = xtrystrdup (cache_nonce); + *pend = c; + if (!cache_nonce) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + line = skip_options (line); + + if (!ctrl->server_local->export_key) + { + err = set_error (GPG_ERR_MISSING_KEY, "did you run KEYWRAP_KEY ?"); + goto leave; + } + + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + + if (agent_key_available (grip)) + { + err = gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + + /* Get the key from the file. With the openpgp flag we also ask for + the passphrase so that we can use it to re-encrypt it. */ + err = agent_key_from_file (ctrl, cache_nonce, + ctrl->server_local->keydesc, grip, + &shadow_info, CACHE_MODE_IGNORE, NULL, &s_skey, + openpgp ? &passphrase : NULL); + if (err) + goto leave; + if (shadow_info) + { + /* Key is on a smartcard. */ + err = gpg_error (GPG_ERR_UNUSABLE_SECKEY); + goto leave; + } + + if (openpgp) + { + /* The openpgp option changes the key format into the OpenPGP + key transfer format. The result is already a padded + canonical S-expression. */ + if (!passphrase) + { + err = agent_ask_new_passphrase + (ctrl, _("This key (or subkey) is not protected with a passphrase." + " Please enter a new passphrase to export it."), + &passphrase); + if (err) + goto leave; + } + err = convert_to_openpgp (ctrl, s_skey, passphrase, &key, &keylen); + if (!err && passphrase) + { + if (!cache_nonce) + { + char buf[12]; + gcry_create_nonce (buf, 12); + cache_nonce = bin2hex (buf, 12, NULL); + } + if (cache_nonce + && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE, + passphrase, CACHE_TTL_NONCE)) + { + assuan_write_status (ctx, "CACHE_NONCE", cache_nonce); + xfree (ctrl->server_local->last_cache_nonce); + ctrl->server_local->last_cache_nonce = cache_nonce; + cache_nonce = NULL; + } + } + } + else + { + /* Convert into a canonical S-expression and wrap that. */ + err = make_canon_sexp_pad (s_skey, 1, &key, &keylen); + } + if (err) + goto leave; + gcry_sexp_release (s_skey); + s_skey = NULL; + + err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) + goto leave; + err = gcry_cipher_setkey (cipherhd, + ctrl->server_local->export_key, KEYWRAP_KEYSIZE); + if (err) + goto leave; + + wrappedkeylen = keylen + 8; + wrappedkey = xtrymalloc (wrappedkeylen); + if (!wrappedkey) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen); + if (err) + goto leave; + xfree (key); + key = NULL; + gcry_cipher_close (cipherhd); + cipherhd = NULL; + + assuan_begin_confidential (ctx); + err = assuan_send_data (ctx, wrappedkey, wrappedkeylen); + assuan_end_confidential (ctx); + + + leave: + xfree (cache_nonce); + xfree (passphrase); + xfree (wrappedkey); + gcry_cipher_close (cipherhd); + xfree (key); + gcry_sexp_release (s_skey); + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + xfree (shadow_info); + + return leave_cmd (ctx, err); +} + + + +static const char hlp_delete_key[] = + "DELETE_KEY [--force] \n" + "\n" + "Delete a secret key from the key store. If --force is used\n" + "and a loopback pinentry is allowed, the agent will not ask\n" + "the user for confirmation."; +static gpg_error_t +cmd_delete_key (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + int force; + unsigned char grip[20]; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + force = has_option (line, "--force"); + line = skip_options (line); + + /* If the use of a loopback pinentry has been disabled, we assume + * that a silent deletion of keys shall also not be allowed. */ + if (!opt.allow_loopback_pinentry) + force = 0; + + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + + err = agent_delete_key (ctrl, ctrl->server_local->keydesc, grip, force ); + if (err) + goto leave; + + leave: + xfree (ctrl->server_local->keydesc); + ctrl->server_local->keydesc = NULL; + + return leave_cmd (ctx, err); +} + + + +static const char hlp_keytocard[] = + "KEYTOCARD [--force] \n" + "\n"; +static gpg_error_t +cmd_keytocard (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int force; + gpg_error_t err = 0; + unsigned char grip[20]; + gcry_sexp_t s_skey = NULL; + unsigned char *keydata; + size_t keydatalen, timestamplen; + const char *serialno, *timestamp_str, *id; + unsigned char *shadow_info = NULL; + time_t timestamp; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + force = has_option (line, "--force"); + line = skip_options (line); + + err = parse_keygrip (ctx, line, grip); + if (err) + return err; + + if (agent_key_available (grip)) + return gpg_error (GPG_ERR_NO_SECKEY); + + line += 40; + while (*line && (*line == ' ' || *line == '\t')) + line++; + serialno = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + return gpg_error (GPG_ERR_MISSING_VALUE); + *line = '\0'; + line++; + while (*line && (*line == ' ' || *line == '\t')) + line++; + id = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + return gpg_error (GPG_ERR_MISSING_VALUE); + *line = '\0'; + line++; + while (*line && (*line == ' ' || *line == '\t')) + line++; + timestamp_str = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (*line) + *line = '\0'; + timestamplen = line - timestamp_str; + if (timestamplen != 15) + return gpg_error (GPG_ERR_INV_VALUE); + + err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip, + &shadow_info, CACHE_MODE_IGNORE, NULL, + &s_skey, NULL); + if (err) + { + xfree (shadow_info); + return err; + } + if (shadow_info) + { + /* Key is on a smartcard already. */ + xfree (shadow_info); + gcry_sexp_release (s_skey); + return gpg_error (GPG_ERR_UNUSABLE_SECKEY); + } + + keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); + keydata = xtrymalloc_secure (keydatalen + 30); + if (keydata == NULL) + { + gcry_sexp_release (s_skey); + return gpg_error_from_syserror (); + } + + gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, keydata, keydatalen); + gcry_sexp_release (s_skey); + keydatalen--; /* Decrement for last '\0'. */ + /* Add timestamp "created-at" in the private key */ + timestamp = isotime2epoch (timestamp_str); + snprintf (keydata+keydatalen-1, 30, "(10:created-at10:%010lu))", timestamp); + keydatalen += 10 + 19 - 1; + err = divert_writekey (ctrl, force, serialno, id, keydata, keydatalen); + xfree (keydata); + + return leave_cmd (ctx, err); +} + + + +static const char hlp_getval[] = + "GETVAL \n" + "\n" + "Return the value for KEY from the special environment as created by\n" + "PUTVAL."; +static gpg_error_t +cmd_getval (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc = 0; + char *key = NULL; + char *p; + struct putval_item_s *vl; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + for (p=line; *p == ' '; p++) + ; + key = p; + p = strchr (key, ' '); + if (p) + { + *p++ = 0; + for (; *p == ' '; p++) + ; + if (*p) + return set_error (GPG_ERR_ASS_PARAMETER, "too many arguments"); + } + if (!*key) + return set_error (GPG_ERR_ASS_PARAMETER, "no key given"); + + + for (vl=putval_list; vl; vl = vl->next) + if ( !strcmp (vl->d, key) ) + break; + + if (vl) /* Got an entry. */ + rc = assuan_send_data (ctx, vl->d+vl->off, vl->len); + else + return gpg_error (GPG_ERR_NO_DATA); + + return leave_cmd (ctx, rc); +} + + +static const char hlp_putval[] = + "PUTVAL []\n" + "\n" + "The gpg-agent maintains a kind of environment which may be used to\n" + "store key/value pairs in it, so that they can be retrieved later.\n" + "This may be used by helper daemons to daemonize themself on\n" + "invocation and register them with gpg-agent. Callers of the\n" + "daemon's service may now first try connect to get the information\n" + "for that service from gpg-agent through the GETVAL command and then\n" + "try to connect to that daemon. Only if that fails they may start\n" + "an own instance of the service daemon. \n" + "\n" + "KEY is an an arbitrary symbol with the same syntax rules as keys\n" + "for shell environment variables. PERCENT_ESCAPED_VALUE is the\n" + "corresponding value; they should be similar to the values of\n" + "envronment variables but gpg-agent does not enforce any\n" + "restrictions. If that value is not given any value under that KEY\n" + "is removed from this special environment."; +static gpg_error_t +cmd_putval (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc = 0; + char *key = NULL; + char *value = NULL; + size_t valuelen = 0; + char *p; + struct putval_item_s *vl, *vlprev; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + for (p=line; *p == ' '; p++) + ; + key = p; + p = strchr (key, ' '); + if (p) + { + *p++ = 0; + for (; *p == ' '; p++) + ; + if (*p) + { + value = p; + p = strchr (value, ' '); + if (p) + *p = 0; + valuelen = percent_plus_unescape_inplace (value, 0); + } + } + if (!*key) + return set_error (GPG_ERR_ASS_PARAMETER, "no key given"); + + + for (vl=putval_list,vlprev=NULL; vl; vlprev=vl, vl = vl->next) + if ( !strcmp (vl->d, key) ) + break; + + if (vl) /* Delete old entry. */ + { + if (vlprev) + vlprev->next = vl->next; + else + putval_list = vl->next; + xfree (vl); + } + + if (valuelen) /* Add entry. */ + { + vl = xtrymalloc (sizeof *vl + strlen (key) + valuelen); + if (!vl) + rc = gpg_error_from_syserror (); + else + { + vl->len = valuelen; + vl->off = strlen (key) + 1; + strcpy (vl->d, key); + memcpy (vl->d + vl->off, value, valuelen); + vl->next = putval_list; + putval_list = vl; + } + } + + return leave_cmd (ctx, rc); +} + + + + +static const char hlp_updatestartuptty[] = + "UPDATESTARTUPTTY\n" + "\n" + "Set startup TTY and X11 DISPLAY variables to the values of this\n" + "session. This command is useful to pull future pinentries to\n" + "another screen. It is only required because there is no way in the\n" + "ssh-agent protocol to convey this information."; +static gpg_error_t +cmd_updatestartuptty (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + session_env_t se; + char *lc_ctype = NULL; + char *lc_messages = NULL; + int iterator; + const char *name; + + (void)line; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + se = session_env_new (); + if (!se) + err = gpg_error_from_syserror (); + + iterator = 0; + while (!err && (name = session_env_list_stdenvnames (&iterator, NULL))) + { + const char *value = session_env_getenv (ctrl->session_env, name); + if (value) + err = session_env_setenv (se, name, value); + } + + if (!err && ctrl->lc_ctype) + if (!(lc_ctype = xtrystrdup (ctrl->lc_ctype))) + err = gpg_error_from_syserror (); + + if (!err && ctrl->lc_messages) + if (!(lc_messages = xtrystrdup (ctrl->lc_messages))) + err = gpg_error_from_syserror (); + + if (err) + { + session_env_release (se); + xfree (lc_ctype); + xfree (lc_messages); + } + else + { + session_env_release (opt.startup_env); + opt.startup_env = se; + xfree (opt.startup_lc_ctype); + opt.startup_lc_ctype = lc_ctype; + xfree (opt.startup_lc_messages); + opt.startup_lc_messages = lc_messages; + } + + return err; +} + + + +static const char hlp_killagent[] = + "KILLAGENT\n" + "\n" + "Stop the agent."; +static gpg_error_t +cmd_killagent (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void)line; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + ctrl->server_local->stopme = 1; + assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1); + return 0; +} + + +static const char hlp_reloadagent[] = + "RELOADAGENT\n" + "\n" + "This command is an alternative to SIGHUP\n" + "to reload the configuration."; +static gpg_error_t +cmd_reloadagent (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void)line; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + agent_sighup_action (); + return 0; +} + + + +static const char hlp_getinfo[] = + "GETINFO \n" + "\n" + "Multipurpose function to return a variety of information.\n" + "Supported values for WHAT are:\n" + "\n" + " version - Return the version of the program.\n" + " pid - Return the process id of the server.\n" + " socket_name - Return the name of the socket.\n" + " ssh_socket_name - Return the name of the ssh socket.\n" + " scd_running - Return OK if the SCdaemon is already running.\n" + " s2k_count - Return the calibrated S2K count.\n" + " std_env_names - List the names of the standard environment.\n" + " std_session_env - List the standard session environment.\n" + " std_startup_env - List the standard startup environment.\n" + " cmd_has_option\n" + " - Returns OK if the command CMD implements the option OPT.\n" + " connections - Return number of active connections.\n" + " restricted - Returns OK if the connection is in restricted mode.\n"; +static gpg_error_t +cmd_getinfo (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc = 0; + + if (!strcmp (line, "version")) + { + const char *s = VERSION; + rc = assuan_send_data (ctx, s, strlen (s)); + } + else if (!strncmp (line, "cmd_has_option", 14) + && (line[14] == ' ' || line[14] == '\t' || !line[14])) + { + char *cmd, *cmdopt; + line += 14; + while (*line == ' ' || *line == '\t') + line++; + if (!*line) + rc = gpg_error (GPG_ERR_MISSING_VALUE); + else + { + cmd = line; + while (*line && (*line != ' ' && *line != '\t')) + line++; + if (!*line) + rc = gpg_error (GPG_ERR_MISSING_VALUE); + else + { + *line++ = 0; + while (*line == ' ' || *line == '\t') + line++; + if (!*line) + rc = gpg_error (GPG_ERR_MISSING_VALUE); + else + { + cmdopt = line; + if (!command_has_option (cmd, cmdopt)) + rc = gpg_error (GPG_ERR_GENERAL); + } + } + } + } + else if (!strcmp (line, "s2k_count")) + { + char numbuf[50]; + + snprintf (numbuf, sizeof numbuf, "%lu", get_standard_s2k_count ()); + rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); + } + else if (!strcmp (line, "restricted")) + { + rc = ctrl->restricted? 0 : gpg_error (GPG_ERR_GENERAL); + } + else if (ctrl->restricted) + { + rc = gpg_error (GPG_ERR_FORBIDDEN); + } + /* All sub-commands below are not allowed in restricted mode. */ + else if (!strcmp (line, "pid")) + { + char numbuf[50]; + + snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ()); + rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); + } + else if (!strcmp (line, "socket_name")) + { + const char *s = get_agent_socket_name (); + + if (s) + rc = assuan_send_data (ctx, s, strlen (s)); + else + rc = gpg_error (GPG_ERR_NO_DATA); + } + else if (!strcmp (line, "ssh_socket_name")) + { + const char *s = get_agent_ssh_socket_name (); + + if (s) + rc = assuan_send_data (ctx, s, strlen (s)); + else + rc = gpg_error (GPG_ERR_NO_DATA); + } + else if (!strcmp (line, "scd_running")) + { + rc = agent_scd_check_running ()? 0 : gpg_error (GPG_ERR_GENERAL); + } + else if (!strcmp (line, "std_env_names")) + { + int iterator; + const char *name; + + iterator = 0; + while ((name = session_env_list_stdenvnames (&iterator, NULL))) + { + rc = assuan_send_data (ctx, name, strlen (name)+1); + if (!rc) + rc = assuan_send_data (ctx, NULL, 0); + if (rc) + break; + } + } + else if (!strcmp (line, "std_session_env") + || !strcmp (line, "std_startup_env")) + { + int iterator; + const char *name, *value; + char *string; + + iterator = 0; + while ((name = session_env_list_stdenvnames (&iterator, NULL))) + { + value = session_env_getenv_or_default + (line[5] == 't'? opt.startup_env:ctrl->session_env, name, NULL); + if (value) + { + string = xtryasprintf ("%s=%s", name, value); + if (!string) + rc = gpg_error_from_syserror (); + else + { + rc = assuan_send_data (ctx, string, strlen (string)+1); + if (!rc) + rc = assuan_send_data (ctx, NULL, 0); + } + if (rc) + break; + } + } + } + else if (!strcmp (line, "connections")) + { + char numbuf[20]; + + snprintf (numbuf, sizeof numbuf, "%d", + get_agent_active_connection_count ()); + rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); + } + else + rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); + return rc; +} + + + +/* This function is called by Libassuan to parse the OPTION command. + It has been registered similar to the other Assuan commands. */ +static gpg_error_t +option_handler (assuan_context_t ctx, const char *key, const char *value) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + + if (!strcmp (key, "agent-awareness")) + { + /* The value is a version string telling us of which agent + version the caller is aware of. */ + ctrl->server_local->allow_fully_canceled = + gnupg_compare_version (value, "2.1.0"); + } + else if (ctrl->restricted) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + } + /* All options below are not allowed in restricted mode. */ + else if (!strcmp (key, "putenv")) + { + /* Change the session's environment to be used for the + Pinentry. Valid values are: + Delete envvar NAME + = Set envvar NAME to the empty string + = Set envvar NAME to VALUE + */ + err = session_env_putenv (ctrl->session_env, value); + } + else if (!strcmp (key, "display")) + { + err = session_env_setenv (ctrl->session_env, "DISPLAY", value); + } + else if (!strcmp (key, "ttyname")) + { + if (!opt.keep_tty) + err = session_env_setenv (ctrl->session_env, "GPG_TTY", value); + } + else if (!strcmp (key, "ttytype")) + { + if (!opt.keep_tty) + err = session_env_setenv (ctrl->session_env, "TERM", value); + } + else if (!strcmp (key, "lc-ctype")) + { + if (ctrl->lc_ctype) + xfree (ctrl->lc_ctype); + ctrl->lc_ctype = xtrystrdup (value); + if (!ctrl->lc_ctype) + return out_of_core (); + } + else if (!strcmp (key, "lc-messages")) + { + if (ctrl->lc_messages) + xfree (ctrl->lc_messages); + ctrl->lc_messages = xtrystrdup (value); + if (!ctrl->lc_messages) + return out_of_core (); + } + else if (!strcmp (key, "xauthority")) + { + err = session_env_setenv (ctrl->session_env, "XAUTHORITY", value); + } + else if (!strcmp (key, "pinentry-user-data")) + { + err = session_env_setenv (ctrl->session_env, "PINENTRY_USER_DATA", value); + } + else if (!strcmp (key, "use-cache-for-signing")) + ctrl->server_local->use_cache_for_signing = *value? !!atoi (value) : 0; + else if (!strcmp (key, "allow-pinentry-notify")) + ctrl->server_local->allow_pinentry_notify = 1; + else if (!strcmp (key, "pinentry-mode")) + { + int tmp = parse_pinentry_mode (value); + if (tmp == -1) + err = gpg_error (GPG_ERR_INV_VALUE); + else if (tmp == PINENTRY_MODE_LOOPBACK && !opt.allow_loopback_pinentry) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + ctrl->pinentry_mode = tmp; + } + else if (!strcmp (key, "cache-ttl-opt-preset")) + { + ctrl->cache_ttl_opt_preset = *value? atoi (value) : 0; + } + else if (!strcmp (key, "s2k-count")) + { + ctrl->s2k_count = *value? strtoul(value, NULL, 10) : 0; + if (ctrl->s2k_count && ctrl->s2k_count < 65536) + { + ctrl->s2k_count = 0; + } + } + else + err = gpg_error (GPG_ERR_UNKNOWN_OPTION); + + return err; +} + + + + +/* Called by libassuan after all commands. ERR is the error from the + last assuan operation and not the one returned from the command. */ +static void +post_cmd_notify (assuan_context_t ctx, gpg_error_t err) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void)err; + + /* Switch off any I/O monitor controlled logging pausing. */ + ctrl->server_local->pause_io_logging = 0; +} + + +/* This function is called by libassuan for all I/O. We use it here + to disable logging for the GETEVENTCOUNTER commands. This is so + that the debug output won't get cluttered by this primitive + command. */ +static unsigned int +io_monitor (assuan_context_t ctx, void *hook, int direction, + const char *line, size_t linelen) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void) hook; + + /* We want to suppress all Assuan log messages for connections from + * self. However, assuan_get_pid works only after + * assuan_accept. Now, assuan_accept already logs a line ending with + * the process id. We use this hack here to get the peers pid so + * that we can compare it to our pid. We should add an assuan + * function to return the pid for a file descriptor and use that to + * detect connections to self. */ + if (ctx && !ctrl->server_local->greeting_seen + && direction == ASSUAN_IO_TO_PEER) + { + ctrl->server_local->greeting_seen = 1; + if (linelen > 32 + && !strncmp (line, "OK Pleased to meet you, process ", 32) + && strtoul (line+32, NULL, 10) == getpid ()) + return ASSUAN_IO_MONITOR_NOLOG; + } + + + /* Do not log self-connections. This makes the log cleaner because + * we won't see the check-our-own-socket calls. */ + if (ctx && ctrl->server_local->connect_from_self) + return ASSUAN_IO_MONITOR_NOLOG; + + /* Note that we only check for the uppercase name. This allows the user to + see the logging for debugging if using a non-upercase command + name. */ + if (ctx && direction == ASSUAN_IO_FROM_PEER + && linelen >= 15 + && !strncmp (line, "GETEVENTCOUNTER", 15) + && (linelen == 15 || spacep (line+15))) + { + ctrl->server_local->pause_io_logging = 1; + } + + return ctrl->server_local->pause_io_logging? ASSUAN_IO_MONITOR_NOLOG : 0; +} + + +/* Return true if the command CMD implements the option OPT. */ +static int +command_has_option (const char *cmd, const char *cmdopt) +{ + if (!strcmp (cmd, "GET_PASSPHRASE")) + { + if (!strcmp (cmdopt, "repeat")) + return 1; + } + + return 0; +} + + +/* Tell Libassuan about our commands. Also register the other Assuan + handlers. */ +static int +register_commands (assuan_context_t ctx) +{ + static struct { + const char *name; + assuan_handler_t handler; + const char * const help; + } table[] = { + { "GETEVENTCOUNTER",cmd_geteventcounter, hlp_geteventcounter }, + { "ISTRUSTED", cmd_istrusted, hlp_istrusted }, + { "HAVEKEY", cmd_havekey, hlp_havekey }, + { "KEYINFO", cmd_keyinfo, hlp_keyinfo }, + { "SIGKEY", cmd_sigkey, hlp_sigkey }, + { "SETKEY", cmd_sigkey, hlp_sigkey }, + { "SETKEYDESC", cmd_setkeydesc,hlp_setkeydesc }, + { "SETHASH", cmd_sethash, hlp_sethash }, + { "PKSIGN", cmd_pksign, hlp_pksign }, + { "PKDECRYPT", cmd_pkdecrypt, hlp_pkdecrypt }, + { "GENKEY", cmd_genkey, hlp_genkey }, + { "READKEY", cmd_readkey, hlp_readkey }, + { "GET_PASSPHRASE", cmd_get_passphrase, hlp_get_passphrase }, + { "PRESET_PASSPHRASE", cmd_preset_passphrase, hlp_preset_passphrase }, + { "CLEAR_PASSPHRASE", cmd_clear_passphrase, hlp_clear_passphrase }, + { "GET_CONFIRMATION", cmd_get_confirmation, hlp_get_confirmation }, + { "LISTTRUSTED", cmd_listtrusted, hlp_listtrusted }, + { "MARKTRUSTED", cmd_marktrusted, hlp_martrusted }, + { "LEARN", cmd_learn, hlp_learn }, + { "PASSWD", cmd_passwd, hlp_passwd }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { "SCD", cmd_scd, hlp_scd }, + { "KEYWRAP_KEY", cmd_keywrap_key, hlp_keywrap_key }, + { "IMPORT_KEY", cmd_import_key, hlp_import_key }, + { "EXPORT_KEY", cmd_export_key, hlp_export_key }, + { "DELETE_KEY", cmd_delete_key, hlp_delete_key }, + { "GETVAL", cmd_getval, hlp_getval }, + { "PUTVAL", cmd_putval, hlp_putval }, + { "UPDATESTARTUPTTY", cmd_updatestartuptty, hlp_updatestartuptty }, + { "KILLAGENT", cmd_killagent, hlp_killagent }, + { "RELOADAGENT", cmd_reloadagent,hlp_reloadagent }, + { "GETINFO", cmd_getinfo, hlp_getinfo }, + { "KEYTOCARD", cmd_keytocard, hlp_keytocard }, + { NULL } + }; + int i, rc; + + for (i=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler, + table[i].help); + if (rc) + return rc; + } + assuan_register_post_cmd_notify (ctx, post_cmd_notify); + assuan_register_reset_notify (ctx, reset_notify); + assuan_register_option_handler (ctx, option_handler); + return 0; +} + + +/* Startup the server. If LISTEN_FD and FD is given as -1, this is a + simple piper server, otherwise it is a regular server. CTRL is the + control structure for this connection; it has only the basic + initialization. */ +void +start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd) +{ + int rc; + assuan_context_t ctx = NULL; + + if (ctrl->restricted) + { + if (agent_copy_startup_env (ctrl)) + return; + } + + rc = assuan_new (&ctx); + if (rc) + { + log_error ("failed to allocate assuan context: %s\n", gpg_strerror (rc)); + agent_exit (2); + } + + if (listen_fd == GNUPG_INVALID_FD && fd == GNUPG_INVALID_FD) + { + assuan_fd_t filedes[2]; + + filedes[0] = assuan_fdopen (0); + filedes[1] = assuan_fdopen (1); + rc = assuan_init_pipe_server (ctx, filedes); + } + else if (listen_fd != GNUPG_INVALID_FD) + { + rc = assuan_init_socket_server (ctx, listen_fd, 0); + /* FIXME: Need to call assuan_sock_set_nonce for Windows. But + this branch is currently not used. */ + } + else + { + rc = assuan_init_socket_server (ctx, fd, ASSUAN_SOCKET_SERVER_ACCEPTED); + } + if (rc) + { + log_error ("failed to initialize the server: %s\n", + gpg_strerror(rc)); + agent_exit (2); + } + rc = register_commands (ctx); + if (rc) + { + log_error ("failed to register commands with Assuan: %s\n", + gpg_strerror(rc)); + agent_exit (2); + } + + assuan_set_pointer (ctx, ctrl); + ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local); + ctrl->server_local->assuan_ctx = ctx; + ctrl->server_local->use_cache_for_signing = 1; + + ctrl->digest.raw_value = 0; + + assuan_set_io_monitor (ctx, io_monitor, NULL); + agent_set_progress_cb (progress_cb, ctrl); + + for (;;) + { + rc = assuan_accept (ctx); + if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1) + { + break; + } + else if (rc) + { + log_info ("Assuan accept problem: %s\n", gpg_strerror (rc)); + break; + } + + ctrl->server_local->connect_from_self = (assuan_get_pid (ctx)==getpid ()); + + rc = assuan_process (ctx); + if (rc) + { + log_info ("Assuan processing failed: %s\n", gpg_strerror (rc)); + continue; + } + } + + /* Reset the nonce caches. */ + clear_nonce_cache (ctrl); + + /* Reset the SCD if needed. */ + agent_reset_scd (ctrl); + + /* Reset the pinentry (in case of popup messages). */ + agent_reset_query (ctrl); + + /* Cleanup. */ + assuan_release (ctx); + xfree (ctrl->server_local->keydesc); + xfree (ctrl->server_local->import_key); + xfree (ctrl->server_local->export_key); + if (ctrl->server_local->stopme) + agent_exit (0); + xfree (ctrl->server_local); + ctrl->server_local = NULL; +} + + +/* Helper for the pinentry loopback mode. It merely passes the + parameters on to the client. */ +gpg_error_t +pinentry_loopback(ctrl_t ctrl, const char *keyword, + unsigned char **buffer, size_t *size, + size_t max_length) +{ + gpg_error_t rc; + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + + rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%zu", max_length); + if (rc) + return rc; + + assuan_begin_confidential (ctx); + rc = assuan_inquire (ctx, keyword, buffer, size, max_length); + assuan_end_confidential (ctx); + return rc; +} diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c new file mode 100644 index 0000000..510b6ff --- /dev/null +++ b/agent/cvt-openpgp.c @@ -0,0 +1,1410 @@ +/* cvt-openpgp.c - Convert an OpenPGP key to our internal format. + * Copyright (C) 1998-2002, 2006, 2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 2013, 2014 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include + +#include "agent.h" +#include "i18n.h" +#include "cvt-openpgp.h" +#include "host2net.h" + + +/* Helper to pass data via the callback to do_unprotect. */ +struct try_do_unprotect_arg_s +{ + int is_v4; + int is_protected; + int pubkey_algo; + const char *curve; + int protect_algo; + char *iv; + int ivlen; + int s2k_mode; + int s2k_algo; + byte *s2k_salt; + u32 s2k_count; + u16 desired_csum; + gcry_mpi_t *skey; + size_t skeysize; + int skeyidx; + gcry_sexp_t *r_key; +}; + + + +/* Compute the keygrip from the public key and store it at GRIP. */ +static gpg_error_t +get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey, + unsigned char *grip) +{ + gpg_error_t err; + gcry_sexp_t s_pkey = NULL; + + switch (pubkey_algo) + { + case GCRY_PK_DSA: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", + pkey[0], pkey[1], pkey[2], pkey[3]); + break; + + case GCRY_PK_ELG: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(elg(p%m)(g%m)(y%m)))", + pkey[0], pkey[1], pkey[2]); + break; + + case GCRY_PK_RSA: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); + break; + + case GCRY_PK_ECC: + if (!curve) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else + { + const char *format; + + if (!strcmp (curve, "Ed25519")) + format = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))"; + else if (!strcmp (curve, "Curve25519")) + format = "(public-key(ecc(curve %s)(flags djb-tweak)(q%m)))"; + else + format = "(public-key(ecc(curve %s)(q%m)))"; + + err = gcry_sexp_build (&s_pkey, NULL, format, curve, pkey[0]); + } + break; + + default: + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + break; + } + + if (!err && !gcry_pk_get_keygrip (s_pkey, grip)) + err = gpg_error (GPG_ERR_INTERNAL); + + gcry_sexp_release (s_pkey); + return err; +} + + +/* Convert a secret key given as algorithm id and an array of key + parameters into our s-expression based format. Note that + PUBKEY_ALGO has an gcrypt algorithm number. */ +static gpg_error_t +convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, + const char *curve) +{ + gpg_error_t err; + gcry_sexp_t s_skey = NULL; + + *r_key = NULL; + + switch (pubkey_algo) + { + case GCRY_PK_DSA: + err = gcry_sexp_build (&s_skey, NULL, + "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", + skey[0], skey[1], skey[2], skey[3], skey[4]); + break; + + case GCRY_PK_ELG: + case GCRY_PK_ELG_E: + err = gcry_sexp_build (&s_skey, NULL, + "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", + skey[0], skey[1], skey[2], skey[3]); + break; + + + case GCRY_PK_RSA: + case GCRY_PK_RSA_E: + case GCRY_PK_RSA_S: + err = gcry_sexp_build (&s_skey, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + skey[0], skey[1], skey[2], skey[3], skey[4], + skey[5]); + break; + + case GCRY_PK_ECC: + if (!curve) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else + { + const char *format; + + if (!strcmp (curve, "Ed25519")) + /* Do not store the OID as name but the real name and the + EdDSA flag. */ + format = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))"; + else if (!strcmp (curve, "Curve25519")) + format = "(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))"; + else + format = "(private-key(ecc(curve %s)(q%m)(d%m)))"; + + err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], skey[1]); + } + break; + + default: + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + break; + } + + if (!err) + *r_key = s_skey; + return err; +} + + +/* Convert a secret key given as algorithm id, an array of key + parameters, and an S-expression of the original OpenPGP transfer + key into our s-expression based format. This is a variant of + convert_secret_key which is used for the openpgp-native protection + mode. Note that PUBKEY_ALGO has an gcrypt algorithm number. */ +static gpg_error_t +convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey, + const char *curve, gcry_sexp_t transfer_key) +{ + gpg_error_t err; + gcry_sexp_t s_skey = NULL; + + *r_key = NULL; + + switch (pubkey_algo) + { + case GCRY_PK_DSA: + err = gcry_sexp_build + (&s_skey, NULL, + "(protected-private-key(dsa(p%m)(q%m)(g%m)(y%m)" + "(protected openpgp-native%S)))", + skey[0], skey[1], skey[2], skey[3], transfer_key); + break; + + case GCRY_PK_ELG: + err = gcry_sexp_build + (&s_skey, NULL, + "(protected-private-key(elg(p%m)(g%m)(y%m)" + "(protected openpgp-native%S)))", + skey[0], skey[1], skey[2], transfer_key); + break; + + + case GCRY_PK_RSA: + err = gcry_sexp_build + (&s_skey, NULL, + "(protected-private-key(rsa(n%m)(e%m)" + "(protected openpgp-native%S)))", + skey[0], skey[1], transfer_key ); + break; + + case GCRY_PK_ECC: + if (!curve) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else + { + const char *format; + + if (!strcmp (curve, "Ed25519")) + /* Do not store the OID as name but the real name and the + EdDSA flag. */ + format = "(protected-private-key(ecc(curve %s)(flags eddsa)(q%m)" + "(protected openpgp-native%S)))"; + else if (!strcmp (curve, "Curve25519")) + format = "(protected-private-key(ecc(curve %s)(flags djb-tweak)(q%m)" + "(protected openpgp-native%S)))"; + else + format = "(protected-private-key(ecc(curve %s)(q%m)" + "(protected openpgp-native%S)))"; + + err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], transfer_key); + } + break; + + default: + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + break; + } + + if (!err) + *r_key = s_skey; + return err; +} + + +/* Hash the passphrase and set the key. */ +static gpg_error_t +hash_passphrase_and_set_key (const char *passphrase, + gcry_cipher_hd_t hd, int protect_algo, + int s2k_mode, int s2k_algo, + byte *s2k_salt, u32 s2k_count) +{ + gpg_error_t err; + unsigned char *key; + size_t keylen; + + keylen = gcry_cipher_get_algo_keylen (protect_algo); + if (!keylen) + return gpg_error (GPG_ERR_INTERNAL); + + key = xtrymalloc_secure (keylen); + if (!key) + return gpg_error_from_syserror (); + + err = s2k_hash_passphrase (passphrase, + s2k_algo, s2k_mode, s2k_salt, s2k_count, + key, keylen); + if (!err) + err = gcry_cipher_setkey (hd, key, keylen); + + xfree (key); + return err; +} + + +static u16 +checksum (const unsigned char *p, unsigned int n) +{ + u16 a; + + for (a=0; n; n-- ) + a += *p++; + return a; +} + + +/* Return the number of expected key parameters. */ +static void +get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey) +{ + switch (pubkey_algo) + { + case GCRY_PK_RSA: *npkey = 2; *nskey = 6; break; + case GCRY_PK_ELG: *npkey = 3; *nskey = 4; break; + case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break; + case GCRY_PK_DSA: *npkey = 4; *nskey = 5; break; + case GCRY_PK_ECC: *npkey = 1; *nskey = 2; break; + default: *npkey = 0; *nskey = 0; break; + } +} + + +/* Helper for do_unprotect. PUBKEY_ALOGO is the gcrypt algo number. + On success R_NPKEY and R_NSKEY receive the number or parameters for + the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of + SKEY. */ +static int +prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize, + int s2k_mode, + unsigned int *r_npkey, unsigned int *r_nskey, + unsigned int *r_skeylen) +{ + size_t npkey, nskey, skeylen; + int i; + + /* Count the actual number of MPIs is in the array and set the + remainder to NULL for easier processing later on. */ + for (skeylen = 0; skey[skeylen]; skeylen++) + ; + for (i=skeylen; i < skeysize; i++) + skey[i] = NULL; + + /* Check some args. */ + if (s2k_mode == 1001) + { + /* Stub key. */ + log_info (_("secret key parts are not available\n")); + return gpg_error (GPG_ERR_UNUSABLE_SECKEY); + } + + if (gcry_pk_test_algo (pubkey_algo)) + { + log_info (_("public key algorithm %d (%s) is not supported\n"), + pubkey_algo, gcry_pk_algo_name (pubkey_algo)); + return gpg_error (GPG_ERR_PUBKEY_ALGO); + } + + /* Get properties of the public key algorithm and do some + consistency checks. Note that we need at least NPKEY+1 elements + in the SKEY array. */ + get_npkey_nskey (pubkey_algo, &npkey, &nskey); + if (!npkey || !nskey || npkey >= nskey) + return gpg_error (GPG_ERR_INTERNAL); + if (skeylen <= npkey) + return gpg_error (GPG_ERR_MISSING_VALUE); + if (nskey+1 >= skeysize) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + + /* Check that the public key parameters are all available and not + encrypted. */ + for (i=0; i < npkey; i++) + { + if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + if (r_npkey) + *r_npkey = npkey; + if (r_nskey) + *r_nskey = nskey; + if (r_skeylen) + *r_skeylen = skeylen; + return 0; +} + + +/* Note that this function modifies SKEY. SKEYSIZE is the allocated + size of the array including the NULL item; this is used for a + bounds check. On success a converted key is stored at R_KEY. */ +static int +do_unprotect (const char *passphrase, + int pkt_version, int pubkey_algo, int is_protected, + const char *curve, gcry_mpi_t *skey, size_t skeysize, + int protect_algo, void *protect_iv, size_t protect_ivlen, + int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count, + u16 desired_csum, gcry_sexp_t *r_key) +{ + gpg_error_t err; + unsigned int npkey, nskey, skeylen; + gcry_cipher_hd_t cipher_hd = NULL; + u16 actual_csum; + size_t nbytes; + int i; + gcry_mpi_t tmpmpi; + + *r_key = NULL; + + err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode, + &npkey, &nskey, &skeylen); + if (err) + return err; + + /* Check whether SKEY is at all protected. If it is not protected + merely verify the checksum. */ + if (!is_protected) + { + actual_csum = 0; + for (i=npkey; i < nskey; i++) + { + if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_USER1)) + return gpg_error (GPG_ERR_BAD_SECKEY); + + if (gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits; + const unsigned char *buffer; + buffer = gcry_mpi_get_opaque (skey[i], &nbits); + nbytes = (nbits+7)/8; + actual_csum += checksum (buffer, nbytes); + } + else + { + unsigned char *buffer; + + err = gcry_mpi_aprint (GCRYMPI_FMT_PGP, &buffer, &nbytes, + skey[i]); + if (!err) + actual_csum += checksum (buffer, nbytes); + xfree (buffer); + } + if (err) + return err; + } + + if (actual_csum != desired_csum) + return gpg_error (GPG_ERR_CHECKSUM); + + goto do_convert; + } + + + if (gcry_cipher_test_algo (protect_algo)) + { + /* The algorithm numbers are Libgcrypt numbers but fortunately + the OpenPGP algorithm numbers map one-to-one to the Libgcrypt + numbers. */ + log_info (_("protection algorithm %d (%s) is not supported\n"), + protect_algo, gnupg_cipher_algo_name (protect_algo)); + return gpg_error (GPG_ERR_CIPHER_ALGO); + } + + if (gcry_md_test_algo (s2k_algo)) + { + log_info (_("protection hash algorithm %d (%s) is not supported\n"), + s2k_algo, gcry_md_algo_name (s2k_algo)); + return gpg_error (GPG_ERR_DIGEST_ALGO); + } + + err = gcry_cipher_open (&cipher_hd, protect_algo, + GCRY_CIPHER_MODE_CFB, + (GCRY_CIPHER_SECURE + | (protect_algo >= 100 ? + 0 : GCRY_CIPHER_ENABLE_SYNC))); + if (err) + { + log_error ("failed to open cipher_algo %d: %s\n", + protect_algo, gpg_strerror (err)); + return err; + } + + err = hash_passphrase_and_set_key (passphrase, cipher_hd, protect_algo, + s2k_mode, s2k_algo, s2k_salt, s2k_count); + if (err) + { + gcry_cipher_close (cipher_hd); + return err; + } + + gcry_cipher_setiv (cipher_hd, protect_iv, protect_ivlen); + + actual_csum = 0; + if (pkt_version >= 4) + { + int ndata; + unsigned int ndatabits; + const unsigned char *p; + unsigned char *data; + u16 csum_pgp7 = 0; + + if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE )) + { + gcry_cipher_close (cipher_hd); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + p = gcry_mpi_get_opaque (skey[npkey], &ndatabits); + ndata = (ndatabits+7)/8; + + if (ndata > 1) + csum_pgp7 = buf16_to_u16 (p+ndata-2); + data = xtrymalloc_secure (ndata); + if (!data) + { + err = gpg_error_from_syserror (); + gcry_cipher_close (cipher_hd); + return err; + } + gcry_cipher_decrypt (cipher_hd, data, ndata, p, ndata); + + p = data; + if (is_protected == 2) + { + /* This is the new SHA1 checksum method to detect tampering + with the key as used by the Klima/Rosa attack. */ + desired_csum = 0; + actual_csum = 1; /* Default to bad checksum. */ + + if (ndata < 20) + log_error ("not enough bytes for SHA-1 checksum\n"); + else + { + gcry_md_hd_t h; + + if (gcry_md_open (&h, GCRY_MD_SHA1, 1)) + BUG(); /* Algo not available. */ + gcry_md_write (h, data, ndata - 20); + gcry_md_final (h); + if (!memcmp (gcry_md_read (h, GCRY_MD_SHA1), data+ndata-20, 20)) + actual_csum = 0; /* Digest does match. */ + gcry_md_close (h); + } + } + else + { + /* Old 16 bit checksum method. */ + if (ndata < 2) + { + log_error ("not enough bytes for checksum\n"); + desired_csum = 0; + actual_csum = 1; /* Mark checksum bad. */ + } + else + { + desired_csum = buf16_to_u16 (data+ndata-2); + actual_csum = checksum (data, ndata-2); + if (desired_csum != actual_csum) + { + /* This is a PGP 7.0.0 workaround */ + desired_csum = csum_pgp7; /* Take the encrypted one. */ + } + } + } + + /* Better check it here. Otherwise the gcry_mpi_scan would fail + because the length may have an arbitrary value. */ + if (desired_csum == actual_csum) + { + for (i=npkey; i < nskey; i++ ) + { + if (gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_PGP, p, ndata, &nbytes)) + { + /* Checksum was okay, but not correctly decrypted. */ + desired_csum = 0; + actual_csum = 1; /* Mark checksum bad. */ + break; + } + gcry_mpi_release (skey[i]); + skey[i] = tmpmpi; + ndata -= nbytes; + p += nbytes; + } + skey[i] = NULL; + skeylen = i; + assert (skeylen <= skeysize); + + /* Note: at this point NDATA should be 2 for a simple + checksum or 20 for the sha1 digest. */ + } + xfree(data); + } + else /* Packet version <= 3. */ + { + unsigned char *buffer; + + for (i = npkey; i < nskey; i++) + { + const unsigned char *p; + size_t ndata; + unsigned int ndatabits; + + if (!skey[i] || !gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE)) + { + gcry_cipher_close (cipher_hd); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + p = gcry_mpi_get_opaque (skey[i], &ndatabits); + ndata = (ndatabits+7)/8; + + if (!(ndata >= 2) || !(ndata == (buf16_to_ushort (p) + 7)/8 + 2)) + { + gcry_cipher_close (cipher_hd); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + buffer = xtrymalloc_secure (ndata); + if (!buffer) + { + err = gpg_error_from_syserror (); + gcry_cipher_close (cipher_hd); + return err; + } + + gcry_cipher_sync (cipher_hd); + buffer[0] = p[0]; + buffer[1] = p[1]; + gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2); + actual_csum += checksum (buffer, ndata); + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_PGP, buffer, ndata, &ndata); + xfree (buffer); + if (err) + { + /* Checksum was okay, but not correctly decrypted. */ + desired_csum = 0; + actual_csum = 1; /* Mark checksum bad. */ + break; + } + gcry_mpi_release (skey[i]); + skey[i] = tmpmpi; + } + } + gcry_cipher_close (cipher_hd); + + /* Now let's see whether we have used the correct passphrase. */ + if (actual_csum != desired_csum) + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + + do_convert: + if (nskey != skeylen) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else + err = convert_secret_key (r_key, pubkey_algo, skey, curve); + if (err) + return err; + + /* The checksum may fail, thus we also check the key itself. */ + err = gcry_pk_testkey (*r_key); + if (err) + { + gcry_sexp_release (*r_key); + *r_key = NULL; + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + } + + return 0; +} + + +/* Callback function to try the unprotection from the passphrase query + code. */ +static gpg_error_t +try_do_unprotect_cb (struct pin_entry_info_s *pi) +{ + gpg_error_t err; + struct try_do_unprotect_arg_s *arg = pi->check_cb_arg; + + err = do_unprotect (pi->pin, + arg->is_v4? 4:3, + arg->pubkey_algo, arg->is_protected, + arg->curve, + arg->skey, arg->skeysize, + arg->protect_algo, arg->iv, arg->ivlen, + arg->s2k_mode, arg->s2k_algo, + arg->s2k_salt, arg->s2k_count, + arg->desired_csum, arg->r_key); + /* SKEY may be modified now, thus we need to re-compute SKEYIDX. */ + for (arg->skeyidx = 0; (arg->skeyidx < arg->skeysize + && arg->skey[arg->skeyidx]); arg->skeyidx++) + ; + return err; +} + + +/* See convert_from_openpgp for the core of the description. This + function adds an optional PASSPHRASE argument and uses this to + silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be + NULL in this mode. */ +static gpg_error_t +convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, + unsigned char *grip, const char *prompt, + const char *cache_nonce, const char *passphrase, + unsigned char **r_key, char **r_passphrase) +{ + gpg_error_t err; + int unattended; + int from_native; + gcry_sexp_t top_list; + gcry_sexp_t list = NULL; + const char *value; + size_t valuelen; + char *string; + int idx; + int is_v4, is_protected; + int pubkey_algo; + int protect_algo = 0; + char iv[16]; + int ivlen = 0; + int s2k_mode = 0; + int s2k_algo = 0; + byte s2k_salt[8]; + u32 s2k_count = 0; + size_t npkey, nskey; + gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ + char *curve = NULL; + u16 desired_csum; + int skeyidx = 0; + gcry_sexp_t s_skey = NULL; + + *r_key = NULL; + if (r_passphrase) + *r_passphrase = NULL; + unattended = !r_passphrase; + from_native = (!cache_nonce && passphrase && !r_passphrase); + + top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0); + if (!top_list) + goto bad_seckey; + + list = gcry_sexp_find_token (top_list, "version", 0); + if (!list) + goto bad_seckey; + value = gcry_sexp_nth_data (list, 1, &valuelen); + if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4')) + goto bad_seckey; + is_v4 = (value[0] == '4'); + + gcry_sexp_release (list); + list = gcry_sexp_find_token (top_list, "protection", 0); + if (!list) + goto bad_seckey; + value = gcry_sexp_nth_data (list, 1, &valuelen); + if (!value) + goto bad_seckey; + if (valuelen == 4 && !memcmp (value, "sha1", 4)) + is_protected = 2; + else if (valuelen == 3 && !memcmp (value, "sum", 3)) + is_protected = 1; + else if (valuelen == 4 && !memcmp (value, "none", 4)) + is_protected = 0; + else + goto bad_seckey; + + if (is_protected) + { + string = gcry_sexp_nth_string (list, 2); + if (!string) + goto bad_seckey; + protect_algo = gcry_cipher_map_name (string); + xfree (string); + + value = gcry_sexp_nth_data (list, 3, &valuelen); + if (!value || !valuelen || valuelen > sizeof iv) + goto bad_seckey; + memcpy (iv, value, valuelen); + ivlen = valuelen; + + string = gcry_sexp_nth_string (list, 4); + if (!string) + goto bad_seckey; + s2k_mode = strtol (string, NULL, 10); + xfree (string); + + string = gcry_sexp_nth_string (list, 5); + if (!string) + goto bad_seckey; + s2k_algo = gcry_md_map_name (string); + xfree (string); + + value = gcry_sexp_nth_data (list, 6, &valuelen); + if (!value || !valuelen || valuelen > sizeof s2k_salt) + goto bad_seckey; + memcpy (s2k_salt, value, valuelen); + + string = gcry_sexp_nth_string (list, 7); + if (!string) + goto bad_seckey; + s2k_count = strtoul (string, NULL, 10); + xfree (string); + } + + gcry_sexp_release (list); + list = gcry_sexp_find_token (top_list, "algo", 0); + if (!list) + goto bad_seckey; + string = gcry_sexp_nth_string (list, 1); + if (!string) + goto bad_seckey; + pubkey_algo = gcry_pk_map_name (string); + xfree (string); + + get_npkey_nskey (pubkey_algo, &npkey, &nskey); + if (!npkey || !nskey || npkey >= nskey) + goto bad_seckey; + + if (npkey == 1) /* This is ECC */ + { + gcry_sexp_release (list); + list = gcry_sexp_find_token (top_list, "curve", 0); + if (!list) + goto bad_seckey; + curve = gcry_sexp_nth_string (list, 1); + if (!curve) + goto bad_seckey; + } + + gcry_sexp_release (list); + list = gcry_sexp_find_token (top_list, "skey", 0); + if (!list) + goto bad_seckey; + for (idx=0;;) + { + int is_enc; + + value = gcry_sexp_nth_data (list, ++idx, &valuelen); + if (!value && skeyidx >= npkey) + break; /* Ready. */ + + /* Check for too many parameters. Note that depending on the + protection mode and version number we may see less than NSKEY + (but at least NPKEY+1) parameters. */ + if (idx >= 2*nskey) + goto bad_seckey; + if (skeyidx >= DIM (skey)-1) + goto bad_seckey; + + if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e')) + goto bad_seckey; + is_enc = (value[0] == 'e'); + value = gcry_sexp_nth_data (list, ++idx, &valuelen); + if (!value || !valuelen) + goto bad_seckey; + if (is_enc) + { + /* Encrypted parameters need to be stored as opaque. */ + skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8); + if (!skey[skeyidx]) + goto outofmem; + gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1); + } + else + { + if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD, + value, valuelen, NULL)) + goto bad_seckey; + } + skeyidx++; + } + skey[skeyidx++] = NULL; + + gcry_sexp_release (list); + list = gcry_sexp_find_token (top_list, "csum", 0); + if (list) + { + string = gcry_sexp_nth_string (list, 1); + if (!string) + goto bad_seckey; + desired_csum = strtoul (string, NULL, 10); + xfree (string); + } + else + desired_csum = 0; + + + gcry_sexp_release (list); list = NULL; + gcry_sexp_release (top_list); top_list = NULL; + +#if 0 + log_debug ("XXX is_v4=%d\n", is_v4); + log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); + log_debug ("XXX is_protected=%d\n", is_protected); + log_debug ("XXX protect_algo=%d\n", protect_algo); + log_printhex ("XXX iv", iv, ivlen); + log_debug ("XXX ivlen=%d\n", ivlen); + log_debug ("XXX s2k_mode=%d\n", s2k_mode); + log_debug ("XXX s2k_algo=%d\n", s2k_algo); + log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); + log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); + log_debug ("XXX curve='%s'\n", curve); + for (idx=0; skey[idx]; idx++) + gcry_log_debugmpi (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_USER1) + ? "skey(e)" : "skey(_)", skey[idx]); +#endif /*0*/ + + err = get_keygrip (pubkey_algo, curve, skey, grip); + if (err) + goto leave; + + if (!dontcare_exist && !from_native && !agent_key_available (grip)) + { + err = gpg_error (GPG_ERR_EEXIST); + goto leave; + } + + if (unattended && !from_native) + { + err = prepare_unprotect (pubkey_algo, skey, DIM(skey), s2k_mode, + NULL, NULL, NULL); + if (err) + goto leave; + + err = convert_transfer_key (&s_skey, pubkey_algo, skey, curve, s_pgp); + if (err) + goto leave; + } + else + { + struct pin_entry_info_s *pi; + struct try_do_unprotect_arg_s pi_arg; + + pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1); + if (!pi) + return gpg_error_from_syserror (); + pi->max_length = MAX_PASSPHRASE_LEN + 1; + pi->min_digits = 0; /* We want a real passphrase. */ + pi->max_digits = 16; + pi->max_tries = 3; + pi->check_cb = try_do_unprotect_cb; + pi->check_cb_arg = &pi_arg; + pi_arg.is_v4 = is_v4; + pi_arg.is_protected = is_protected; + pi_arg.pubkey_algo = pubkey_algo; + pi_arg.curve = curve; + pi_arg.protect_algo = protect_algo; + pi_arg.iv = iv; + pi_arg.ivlen = ivlen; + pi_arg.s2k_mode = s2k_mode; + pi_arg.s2k_algo = s2k_algo; + pi_arg.s2k_salt = s2k_salt; + pi_arg.s2k_count = s2k_count; + pi_arg.desired_csum = desired_csum; + pi_arg.skey = skey; + pi_arg.skeysize = DIM (skey); + pi_arg.skeyidx = skeyidx; + pi_arg.r_key = &s_skey; + + err = gpg_error (GPG_ERR_BAD_PASSPHRASE); + if (!is_protected) + { + err = try_do_unprotect_cb (pi); + } + else if (cache_nonce) + { + char *cache_value; + + cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); + if (cache_value) + { + if (strlen (cache_value) < pi->max_length) + strcpy (pi->pin, cache_value); + xfree (cache_value); + } + if (*pi->pin) + err = try_do_unprotect_cb (pi); + } + else if (from_native) + { + if (strlen (passphrase) < pi->max_length) + strcpy (pi->pin, passphrase); + err = try_do_unprotect_cb (pi); + } + if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native) + err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL, 0); + skeyidx = pi_arg.skeyidx; + if (!err && r_passphrase && is_protected) + { + *r_passphrase = xtrystrdup (pi->pin); + if (!*r_passphrase) + err = gpg_error_from_syserror (); + } + xfree (pi); + if (err) + goto leave; + } + + /* Save some memory and get rid of the SKEY array now. */ + for (idx=0; idx < skeyidx; idx++) + gcry_mpi_release (skey[idx]); + skeyidx = 0; + + /* Note that the padding is not required - we use it only because + that function allows us to create the result in secure memory. */ + err = make_canon_sexp_pad (s_skey, 1, r_key, NULL); + + leave: + xfree (curve); + gcry_sexp_release (s_skey); + gcry_sexp_release (list); + gcry_sexp_release (top_list); + for (idx=0; idx < skeyidx; idx++) + gcry_mpi_release (skey[idx]); + if (err && r_passphrase) + { + xfree (*r_passphrase); + *r_passphrase = NULL; + } + return err; + + bad_seckey: + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + + outofmem: + err = gpg_error (GPG_ERR_ENOMEM); + goto leave; + +} + + +/* Convert an OpenPGP transfer key into our internal format. Before + asking for a passphrase we check whether the key already exists in + our key storage. S_PGP is the OpenPGP key in transfer format. If + CACHE_NONCE is given the passphrase will be looked up in the cache. + On success R_KEY will receive a canonical encoded S-expression with + the unprotected key in our internal format; the caller needs to + release that memory. The passphrase used to decrypt the OpenPGP + key will be returned at R_PASSPHRASE; the caller must release this + passphrase. If R_PASSPHRASE is NULL the unattended conversion mode + will be used which uses the openpgp-native protection format for + the key. The keygrip will be stored at the 20 byte buffer pointed + to by GRIP. On error NULL is stored at all return arguments. */ +gpg_error_t +convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, + unsigned char *grip, const char *prompt, + const char *cache_nonce, + unsigned char **r_key, char **r_passphrase) +{ + return convert_from_openpgp_main (ctrl, s_pgp, dontcare_exist, grip, prompt, + cache_nonce, NULL, + r_key, r_passphrase); +} + +/* This function is called by agent_unprotect to re-protect an + openpgp-native protected private-key into the standard private-key + protection format. */ +gpg_error_t +convert_from_openpgp_native (ctrl_t ctrl, + gcry_sexp_t s_pgp, const char *passphrase, + unsigned char **r_key) +{ + gpg_error_t err; + unsigned char grip[20]; + + if (!passphrase) + return gpg_error (GPG_ERR_INTERNAL); + + err = convert_from_openpgp_main (ctrl, s_pgp, 0, grip, NULL, + NULL, passphrase, + r_key, NULL); + + /* On success try to re-write the key. */ + if (!err) + { + if (*passphrase) + { + unsigned char *protectedkey = NULL; + size_t protectedkeylen; + + if (!agent_protect (*r_key, passphrase, + &protectedkey, &protectedkeylen, + ctrl->s2k_count, -1)) + agent_write_private_key (grip, protectedkey, protectedkeylen, 1); + xfree (protectedkey); + } + else + { + /* Empty passphrase: write key without protection. */ + agent_write_private_key (grip, + *r_key, + gcry_sexp_canon_len (*r_key, 0, NULL,NULL), + 1); + } + } + + return err; +} + + +/* Given an ARRAY of mpis with the key parameters, protect the secret + parameters in that array and replace them by one opaque encoded + mpi. NPKEY is the number of public key parameters and NSKEY is + the number of secret key parameters (including the public ones). + On success the array will have NPKEY+1 elements. */ +static gpg_error_t +apply_protection (gcry_mpi_t *array, int npkey, int nskey, + const char *passphrase, + int protect_algo, void *protect_iv, size_t protect_ivlen, + int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count) +{ + gpg_error_t err; + int i, j; + gcry_cipher_hd_t cipherhd; + unsigned char *bufarr[10]; + size_t narr[10]; + unsigned int nbits[10]; + int ndata; + unsigned char *p, *data; + + assert (npkey < nskey); + assert (nskey < DIM (bufarr)); + + /* Collect only the secret key parameters into BUFARR et al and + compute the required size of the data buffer. */ + ndata = 20; /* Space for the SHA-1 checksum. */ + for (i = npkey, j = 0; i < nskey; i++, j++ ) + { + err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]); + if (err) + { + for (i = 0; i < j; i++) + xfree (bufarr[i]); + return err; + } + nbits[j] = gcry_mpi_get_nbits (array[i]); + ndata += 2 + narr[j]; + } + + /* Allocate data buffer and stuff it with the secret key parameters. */ + data = xtrymalloc_secure (ndata); + if (!data) + { + err = gpg_error_from_syserror (); + for (i = 0; i < (nskey-npkey); i++ ) + xfree (bufarr[i]); + return err; + } + p = data; + for (i = 0; i < (nskey-npkey); i++ ) + { + *p++ = nbits[i] >> 8 ; + *p++ = nbits[i]; + memcpy (p, bufarr[i], narr[i]); + p += narr[i]; + xfree (bufarr[i]); + bufarr[i] = NULL; + } + assert (p == data + ndata - 20); + + /* Append a hash of the secret key parameters. */ + gcry_md_hash_buffer (GCRY_MD_SHA1, p, data, ndata - 20); + + /* Encrypt it. */ + err = gcry_cipher_open (&cipherhd, protect_algo, + GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE); + if (!err) + err = hash_passphrase_and_set_key (passphrase, cipherhd, protect_algo, + s2k_mode, s2k_algo, s2k_salt, s2k_count); + if (!err) + err = gcry_cipher_setiv (cipherhd, protect_iv, protect_ivlen); + if (!err) + err = gcry_cipher_encrypt (cipherhd, data, ndata, NULL, 0); + gcry_cipher_close (cipherhd); + if (err) + { + xfree (data); + return err; + } + + /* Replace the secret key parameters in the array by one opaque value. */ + for (i = npkey; i < nskey; i++ ) + { + gcry_mpi_release (array[i]); + array[i] = NULL; + } + array[npkey] = gcry_mpi_set_opaque (NULL, data, ndata*8); + return 0; +} + + +/* + * Examining S_KEY in S-Expression and extract data. + * When REQ_PRIVATE_KEY_DATA == 1, S_KEY's CAR should be 'private-key', + * but it also allows shadowed or protected versions. + * On success, it returns 0, otherwise error number. + * R_ALGONAME is static string which is no need to free by caller. + * R_NPKEY is pointer to number of public key data. + * R_NSKEY is pointer to number of private key data. + * R_ELEMS is static string which is no need to free by caller. + * ARRAY contains public and private key data. + * ARRAYSIZE is the allocated size of the array for cross-checking. + * R_CURVE is pointer to S-Expression of the curve (can be NULL). + * R_FLAGS is pointer to S-Expression of the flags (can be NULL). + */ +gpg_error_t +extract_private_key (gcry_sexp_t s_key, int req_private_key_data, + const char **r_algoname, int *r_npkey, int *r_nskey, + const char **r_elems, + gcry_mpi_t *array, int arraysize, + gcry_sexp_t *r_curve, gcry_sexp_t *r_flags) +{ + gpg_error_t err; + gcry_sexp_t list, l2; + char *name; + const char *algoname, *format; + int npkey, nskey; + gcry_sexp_t curve = NULL; + gcry_sexp_t flags = NULL; + + *r_curve = NULL; + *r_flags = NULL; + + if (!req_private_key_data) + { + list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 ); + if (!list) + list = gcry_sexp_find_token (s_key, "protected-private-key", 0 ); + if (!list) + list = gcry_sexp_find_token (s_key, "private-key", 0 ); + } + else + list = gcry_sexp_find_token (s_key, "private-key", 0); + + if (!list) + { + log_error ("invalid private key format\n"); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + name = gcry_sexp_nth_string (list, 0); + if (!name) + { + gcry_sexp_release (list); + return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */ + } + + if (arraysize < 7) + BUG (); + + /* Map NAME to a name as used by Libgcrypt. We do not use the + Libgcrypt function here because we need a lowercase name and + require special treatment for some algorithms. */ + strlwr (name); + if (!strcmp (name, "rsa")) + { + algoname = "rsa"; + format = "ned?p?q?u?"; + npkey = 2; + nskey = 6; + err = gcry_sexp_extract_param (list, NULL, format, + array+0, array+1, array+2, array+3, + array+4, array+5, NULL); + } + else if (!strcmp (name, "elg")) + { + algoname = "elg"; + format = "pgyx?"; + npkey = 3; + nskey = 4; + err = gcry_sexp_extract_param (list, NULL, format, + array+0, array+1, array+2, array+3, + NULL); + } + else if (!strcmp (name, "dsa")) + { + algoname = "dsa"; + format = "pqgyx?"; + npkey = 4; + nskey = 5; + err = gcry_sexp_extract_param (list, NULL, format, + array+0, array+1, array+2, array+3, + array+4, NULL); + } + else if (!strcmp (name, "ecc") || !strcmp (name, "ecdsa")) + { + algoname = "ecc"; + format = "qd?"; + npkey = 1; + nskey = 2; + curve = gcry_sexp_find_token (list, "curve", 0); + flags = gcry_sexp_find_token (list, "flags", 0); + err = gcry_sexp_extract_param (list, NULL, format, + array+0, array+1, NULL); + } + else + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + } + xfree (name); + gcry_sexp_release (list); + if (err) + { + gcry_sexp_release (curve); + gcry_sexp_release (flags); + return err; + } + else + { + *r_algoname = algoname; + if (r_elems) + *r_elems = format; + *r_npkey = npkey; + if (r_nskey) + *r_nskey = nskey; + *r_curve = curve; + *r_flags = flags; + + return 0; + } +} + +/* Convert our key S_KEY into an OpenPGP key transfer format. On + success a canonical encoded S-expression is stored at R_TRANSFERKEY + and its length at R_TRANSFERKEYLEN; this S-expression is also + padded to a multiple of 64 bits. */ +gpg_error_t +convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, + unsigned char **r_transferkey, size_t *r_transferkeylen) +{ + gpg_error_t err; + const char *algoname; + int npkey, nskey; + gcry_mpi_t array[10]; + gcry_sexp_t curve = NULL; + gcry_sexp_t flags = NULL; + char protect_iv[16]; + char salt[8]; + unsigned long s2k_count; + int i, j; + + (void)ctrl; + + *r_transferkey = NULL; + + for (i=0; i < DIM (array); i++) + array[i] = NULL; + + err = extract_private_key (s_key, 1, &algoname, &npkey, &nskey, NULL, + array, DIM (array), &curve, &flags); + if (err) + return err; + + gcry_create_nonce (protect_iv, sizeof protect_iv); + gcry_create_nonce (salt, sizeof salt); + /* We need to use the encoded S2k count. It is not possible to + encode it after it has been used because the encoding procedure + may round the value up. */ + s2k_count = get_standard_s2k_count_rfc4880 (); + err = apply_protection (array, npkey, nskey, passphrase, + GCRY_CIPHER_AES, protect_iv, sizeof protect_iv, + 3, GCRY_MD_SHA1, salt, s2k_count); + /* Turn it into the transfer key S-expression. Note that we always + return a protected key. */ + if (!err) + { + char countbuf[35]; + membuf_t mbuf; + void *format_args[10+2]; + gcry_sexp_t tmpkey; + gcry_sexp_t tmpsexp = NULL; + + snprintf (countbuf, sizeof countbuf, "%lu", s2k_count); + + init_membuf (&mbuf, 50); + put_membuf_str (&mbuf, "(skey"); + for (i=j=0; i < npkey; i++) + { + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = array + i; + } + put_membuf_str (&mbuf, " e %m"); + format_args[j++] = array + npkey; + put_membuf_str (&mbuf, ")\n"); + put_membuf (&mbuf, "", 1); + + tmpkey = NULL; + { + char *format = get_membuf (&mbuf, NULL); + if (!format) + err = gpg_error_from_syserror (); + else + err = gcry_sexp_build_array (&tmpkey, NULL, format, format_args); + xfree (format); + } + if (!err) + err = gcry_sexp_build (&tmpsexp, NULL, + "(openpgp-private-key\n" + " (version 1:4)\n" + " (algo %s)\n" + " %S%S\n" + " (protection sha1 aes %b 1:3 sha1 %b %s))\n", + algoname, + curve, + tmpkey, + (int)sizeof protect_iv, protect_iv, + (int)sizeof salt, salt, + countbuf); + gcry_sexp_release (tmpkey); + if (!err) + err = make_canon_sexp_pad (tmpsexp, 0, r_transferkey, r_transferkeylen); + gcry_sexp_release (tmpsexp); + } + + for (i=0; i < DIM (array); i++) + gcry_mpi_release (array[i]); + gcry_sexp_release (curve); + gcry_sexp_release (flags); + + return err; +} diff --git a/agent/cvt-openpgp.h b/agent/cvt-openpgp.h new file mode 100644 index 0000000..23092f6 --- /dev/null +++ b/agent/cvt-openpgp.h @@ -0,0 +1,37 @@ +/* cvt-openpgp.h - Convert an OpenPGP key to our internal format. + * Copyright (C) 2010 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 3 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, see . + */ +#ifndef GNUPG_AGENT_CVT_OPENPGP_H +#define GNUPG_AGENT_CVT_OPENPGP_H + +gpg_error_t convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, + int dontcare_exist, + unsigned char *grip, const char *prompt, + const char *cache_nonce, + unsigned char **r_key, char **r_passphrase); +gpg_error_t convert_from_openpgp_native (ctrl_t ctrl, + gcry_sexp_t s_pgp, + const char *passphrase, + unsigned char **r_key); + +gpg_error_t convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, + const char *passphrase, + unsigned char **r_transferkey, + size_t *r_transferkeylen); + +#endif /*GNUPG_AGENT_CVT_OPENPGP_H*/ diff --git a/agent/divert-scd.c b/agent/divert-scd.c new file mode 100644 index 0000000..7b07008 --- /dev/null +++ b/agent/divert-scd.c @@ -0,0 +1,492 @@ +/* divert-scd.c - divert operations to the scdaemon + * Copyright (C) 2002, 2003, 2009 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include "i18n.h" +#include "sexp-parse.h" + + +static int +ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) +{ + int rc, i; + char *serialno; + int no_card = 0; + char *desc; + char *want_sn, *want_kid; + int want_sn_displen; + + *r_kid = NULL; + + rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL); + if (rc) + return rc; + + /* We assume that a 20 byte serial number is a standard one which + has the property to have a zero in the last nibble (Due to BCD + representation). We don't display this '0' because it may + confuse the user. */ + want_sn_displen = strlen (want_sn); + if (want_sn_displen == 20 && want_sn[19] == '0') + want_sn_displen--; + + for (;;) + { + rc = agent_card_serialno (ctrl, &serialno); + if (!rc) + { + log_debug ("detected card with S/N %s\n", serialno); + i = strcmp (serialno, want_sn); + xfree (serialno); + serialno = NULL; + if (!i) + { + xfree (want_sn); + *r_kid = want_kid; + return 0; /* yes, we have the correct card */ + } + } + else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) + { + log_debug ("no card present\n"); + rc = 0; + no_card = 1; + } + else + { + log_error ("error accessing card: %s\n", gpg_strerror (rc)); + } + + if (!rc) + { + if (asprintf (&desc, + "%s:%%0A%%0A" + " \"%.*s\"", + no_card + ? L_("Please insert the card with serial number") + : L_("Please remove the current card and " + "insert the one with serial number"), + want_sn_displen, want_sn) < 0) + { + rc = out_of_core (); + } + else + { + rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK && + gpg_err_code (rc) == GPG_ERR_NO_PIN_ENTRY) + rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT); + + xfree (desc); + } + } + if (rc) + { + xfree (want_sn); + xfree (want_kid); + return rc; + } + } +} + + +/* Put the DIGEST into an DER encoded container and return it in R_VAL. */ +static int +encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo, + unsigned char **r_val, size_t *r_len) +{ + unsigned char *frame; + unsigned char asn[100]; + size_t asnlen; + + *r_val = NULL; + *r_len = 0; + + asnlen = DIM(asn); + if (!algo || gcry_md_test_algo (algo)) + return gpg_error (GPG_ERR_DIGEST_ALGO); + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + log_error ("no object identifier for algo %d\n", algo); + return gpg_error (GPG_ERR_INTERNAL); + } + + frame = xtrymalloc (asnlen + digestlen); + if (!frame) + return out_of_core (); + memcpy (frame, asn, asnlen); + memcpy (frame+asnlen, digest, digestlen); + if (DBG_CRYPTO) + log_printhex ("encoded hash:", frame, asnlen+digestlen); + + *r_val = frame; + *r_len = asnlen+digestlen; + return 0; +} + + +/* Callback used to ask for the PIN which should be set into BUF. The + buf has been allocated by the caller and is of size MAXBUF which + includes the terminating null. The function should return an UTF-8 + string with the passphrase, the buffer may optionally be padded + with arbitrary characters. + + INFO gets displayed as part of a generic string. However if the + first character of INFO is a vertical bar all up to the next + verical bar are considered flags and only everything after the + second vertical bar gets displayed as the full prompt. + + Flags: + + 'N' = New PIN, this requests a second prompt to repeat the + PIN. If the PIN is not correctly repeated it starts from + all over. + 'A' = The PIN is an Admin PIN, SO-PIN or alike. + 'P' = The PIN is a PUK (Personal Unblocking Key). + 'R' = The PIN is a Reset Code. + + Example: + + "|AN|Please enter the new security officer's PIN" + + The text "Please ..." will get displayed and the flags 'A' and 'N' + are considered. + */ +static int +getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) +{ + struct pin_entry_info_s *pi; + int rc; + ctrl_t ctrl = opaque; + const char *ends, *s; + int any_flags = 0; + int newpin = 0; + int resetcode = 0; + int is_puk = 0; + const char *again_text = NULL; + const char *prompt = "PIN"; + + if (buf && maxbuf < 2) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Parse the flags. */ + if (info && *info =='|' && (ends=strchr (info+1, '|'))) + { + for (s=info+1; s < ends; s++) + { + if (*s == 'A') + prompt = L_("Admin PIN"); + else if (*s == 'P') + { + /* TRANSLATORS: A PUK is the Personal Unblocking Code + used to unblock a PIN. */ + prompt = L_("PUK"); + is_puk = 1; + } + else if (*s == 'N') + newpin = 1; + else if (*s == 'R') + { + prompt = L_("Reset Code"); + resetcode = 1; + } + } + info = ends+1; + any_flags = 1; + } + else if (info && *info == '|') + log_debug ("pin_cb called without proper PIN info hack\n"); + + /* If BUF has been passed as NULL, we are in pinpad mode: The + callback opens the popup and immediately returns. */ + if (!buf) + { + if (maxbuf == 0) /* Close the pinentry. */ + { + agent_popup_message_stop (ctrl); + rc = 0; + } + else if (maxbuf == 1) /* Open the pinentry. */ + { + if (info) + { + char *desc; + + if ( asprintf (&desc, + L_("%s%%0A%%0AUse the reader's pinpad for input."), + info) < 0 ) + rc = gpg_error_from_syserror (); + else + { + rc = agent_popup_message_start (ctrl, desc, NULL); + xfree (desc); + } + } + else + rc = agent_popup_message_start (ctrl, NULL, NULL); + } + else + rc = gpg_error (GPG_ERR_INV_VALUE); + return rc; + } + + /* FIXME: keep PI and TRIES in OPAQUE. Frankly this is a whole + mess because we should call the card's verify function from the + pinentry check pin CB. */ + again: + pi = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10); + if (!pi) + return gpg_error_from_syserror (); + pi->max_length = maxbuf-1; + pi->min_digits = 0; /* we want a real passphrase */ + pi->max_digits = 16; + pi->max_tries = 3; + + if (any_flags) + { + rc = agent_askpin (ctrl, info, prompt, again_text, pi, NULL, 0); + again_text = NULL; + if (!rc && newpin) + { + struct pin_entry_info_s *pi2; + pi2 = gcry_calloc_secure (1, sizeof (*pi) + maxbuf + 10); + if (!pi2) + { + rc = gpg_error_from_syserror (); + xfree (pi); + return rc; + } + pi2->max_length = maxbuf-1; + pi2->min_digits = 0; + pi2->max_digits = 16; + pi2->max_tries = 1; + rc = agent_askpin (ctrl, + (resetcode? + L_("Repeat this Reset Code"): + is_puk? + L_("Repeat this PUK"): + L_("Repeat this PIN")), + prompt, NULL, pi2, NULL, 0); + if (!rc && strcmp (pi->pin, pi2->pin)) + { + again_text = (resetcode? + L_("Reset Code not correctly repeated; try again"): + is_puk? + L_("PUK not correctly repeated; try again"): + L_("PIN not correctly repeated; try again")); + xfree (pi2); + xfree (pi); + goto again; + } + xfree (pi2); + } + } + else + { + char *desc; + if ( asprintf (&desc, + L_("Please enter the PIN%s%s%s to unlock the card"), + info? " (":"", + info? info:"", + info? ")":"") < 0) + desc = NULL; + rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi, NULL, 0); + xfree (desc); + } + + if (!rc) + { + strncpy (buf, pi->pin, maxbuf-1); + buf[maxbuf-1] = 0; + } + xfree (pi); + return rc; +} + + + + +int +divert_pksign (ctrl_t ctrl, + const unsigned char *digest, size_t digestlen, int algo, + const unsigned char *shadow_info, unsigned char **r_sig, + size_t *r_siglen) +{ + int rc; + char *kid; + size_t siglen; + unsigned char *sigval = NULL; + + rc = ask_for_card (ctrl, shadow_info, &kid); + if (rc) + return rc; + + if (algo == MD_USER_TLS_MD5SHA1) + { + int save = ctrl->use_auth_call; + ctrl->use_auth_call = 1; + rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, + algo, digest, digestlen, &sigval, &siglen); + ctrl->use_auth_call = save; + } + else + { + unsigned char *data; + size_t ndata; + + rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata); + if (!rc) + { + rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, + algo, data, ndata, &sigval, &siglen); + xfree (data); + } + } + + if (!rc) + { + *r_sig = sigval; + *r_siglen = siglen; + } + + xfree (kid); + + return rc; +} + + +/* Decrypt the the value given asn an S-expression in CIPHER using the + key identified by SHADOW_INFO and return the plaintext in an + allocated buffer in R_BUF. The padding information is stored at + R_PADDING with -1 for not known. */ +int +divert_pkdecrypt (ctrl_t ctrl, + const unsigned char *cipher, + const unsigned char *shadow_info, + char **r_buf, size_t *r_len, int *r_padding) +{ + int rc; + char *kid; + const unsigned char *s; + size_t n; + const unsigned char *ciphertext; + size_t ciphertextlen; + char *plaintext; + size_t plaintextlen; + + *r_padding = -1; + + s = cipher; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "enc-val")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "rsa")) + { + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "a")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + n = snext (&s); + } + else if (smatch (&s, n, "ecdh")) + { + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "s")) + { + n = snext (&s); + s += n; + if (*s++ != ')') + return gpg_error (GPG_ERR_INV_SEXP); + if (*s++ != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + } + if (!smatch (&s, n, "e")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + n = snext (&s); + } + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + if (!n) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + ciphertext = s; + ciphertextlen = n; + + rc = ask_for_card (ctrl, shadow_info, &kid); + if (rc) + return rc; + + rc = agent_card_pkdecrypt (ctrl, kid, getpin_cb, ctrl, + ciphertext, ciphertextlen, + &plaintext, &plaintextlen, r_padding); + if (!rc) + { + *r_buf = plaintext; + *r_len = plaintextlen; + } + xfree (kid); + return rc; +} + +int +divert_writekey (ctrl_t ctrl, int force, const char *serialno, + const char *id, const char *keydata, size_t keydatalen) +{ + return agent_card_writekey (ctrl, force, serialno, id, keydata, keydatalen, + getpin_cb, ctrl); +} + +int +divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context) +{ + return agent_card_scd (ctrl, cmdline, getpin_cb, ctrl, assuan_context); +} diff --git a/agent/findkey.c b/agent/findkey.c new file mode 100644 index 0000000..1b187ba --- /dev/null +++ b/agent/findkey.c @@ -0,0 +1,1539 @@ +/* findkey.c - Locate the secret key + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, + * 2010, 2011 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* (we use pth_sleep) */ + +#include "agent.h" +#include "i18n.h" +#include "../common/ssh-utils.h" +#include "../common/name-value.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* Helper to pass data to the check callback of the unprotect function. */ +struct try_unprotect_arg_s +{ + ctrl_t ctrl; + const unsigned char *protected_key; + unsigned char *unprotected_key; + int change_required; /* Set by the callback to indicate that the + user should change the passphrase. */ +}; + + +static gpg_error_t +write_extended_private_key (char *fname, estream_t fp, + const void *buf, size_t len) +{ + gpg_error_t err; + nvc_t pk = NULL; + gcry_sexp_t key = NULL; + int remove = 0; + int line; + + err = nvc_parse_private_key (&pk, &line, fp); + if (err) + { + log_error ("error parsing '%s' line %d: %s\n", + fname, line, gpg_strerror (err)); + goto leave; + } + + err = gcry_sexp_sscan (&key, NULL, buf, len); + if (err) + goto leave; + + err = nvc_set_private_key (pk, key); + if (err) + goto leave; + + err = es_fseek (fp, 0, SEEK_SET); + if (err) + goto leave; + + err = nvc_write (pk, fp); + if (err) + { + log_error ("error writing '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + + if (ftruncate (es_fileno (fp), es_ftello (fp))) + { + err = gpg_error_from_syserror (); + log_error ("error truncating '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + + if (es_fclose (fp)) + { + err = gpg_error_from_syserror (); + log_error ("error closing '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + else + fp = NULL; + + bump_key_eventcounter (); + + leave: + if (fp) + es_fclose (fp); + if (remove) + gnupg_remove (fname); + xfree (fname); + gcry_sexp_release (key); + nvc_release (pk); + return err; +} + +/* Write an S-expression formatted key to our key storage. With FORCE + passed as true an existing key with the given GRIP will get + overwritten. */ +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force) +{ + char *fname; + estream_t fp; + char hexgrip[40+4+1]; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + + /* FIXME: Write to a temp file first so that write failures during + key updates won't lead to a key loss. */ + + if (!force && !access (fname, F_OK)) + { + log_error ("secret key file '%s' already exists\n", fname); + xfree (fname); + return gpg_error (GPG_ERR_EEXIST); + } + + fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); + if (!fp) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + + if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT) + { + fp = es_fopen (fname, "wbx,mode=-rw"); + if (!fp) + tmperr = gpg_error_from_syserror (); + } + if (!fp) + { + log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); + xfree (fname); + return tmperr; + } + } + else if (force) + { + gpg_error_t rc; + char first; + + /* See if an existing key is in extended format. */ + if (es_fread (&first, 1, 1, fp) != 1) + { + rc = gpg_error_from_syserror (); + log_error ("error reading first byte from '%s': %s\n", + fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + rc = es_fseek (fp, 0, SEEK_SET); + if (rc) + { + log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + if (first != '(') + { + /* Key is in extended format. */ + return write_extended_private_key (fname, fp, buffer, length); + } + } + + if (es_fwrite (buffer, length, 1, fp) != 1) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); + es_fclose (fp); + gnupg_remove (fname); + xfree (fname); + return tmperr; + } + + /* When force is given, the file might have to be truncated. */ + if (force && ftruncate (es_fileno (fp), es_ftello (fp))) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr)); + es_fclose (fp); + gnupg_remove (fname); + xfree (fname); + return tmperr; + } + + if (es_fclose (fp)) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); + gnupg_remove (fname); + xfree (fname); + return tmperr; + } + bump_key_eventcounter (); + xfree (fname); + return 0; +} + + +/* Callback function to try the unprotection from the passphrase query + code. */ +static gpg_error_t +try_unprotect_cb (struct pin_entry_info_s *pi) +{ + struct try_unprotect_arg_s *arg = pi->check_cb_arg; + ctrl_t ctrl = arg->ctrl; + size_t dummy; + gpg_error_t err; + gnupg_isotime_t now, protected_at, tmptime; + char *desc = NULL; + + assert (!arg->unprotected_key); + + arg->change_required = 0; + err = agent_unprotect (ctrl, arg->protected_key, pi->pin, protected_at, + &arg->unprotected_key, &dummy); + if (err) + return err; + if (!opt.max_passphrase_days || ctrl->in_passwd) + return 0; /* No regular passphrase change required. */ + + if (!*protected_at) + { + /* No protection date known - must force passphrase change. */ + desc = xtrystrdup (L_("Note: This passphrase has never been changed.%0A" + "Please change it now.")); + if (!desc) + return gpg_error_from_syserror (); + } + else + { + gnupg_get_isotime (now); + gnupg_copy_time (tmptime, protected_at); + err = add_days_to_isotime (tmptime, opt.max_passphrase_days); + if (err) + return err; + if (strcmp (now, tmptime) > 0 ) + { + /* Passphrase "expired". */ + desc = xtryasprintf + (L_("This passphrase has not been changed%%0A" + "since %.4s-%.2s-%.2s. Please change it now."), + protected_at, protected_at+4, protected_at+6); + if (!desc) + return gpg_error_from_syserror (); + } + } + + if (desc) + { + /* Change required. */ + if (opt.enforce_passphrase_constraints) + { + err = agent_get_confirmation (ctrl, desc, + L_("Change passphrase"), NULL, 0); + if (!err) + arg->change_required = 1; + } + else + { + err = agent_get_confirmation (ctrl, desc, + L_("Change passphrase"), + L_("I'll change it later"), 0); + if (!err) + arg->change_required = 1; + else if (gpg_err_code (err) == GPG_ERR_CANCELED + || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) + err = 0; + } + xfree (desc); + } + + return 0; +} + + +/* Modify a Key description, replacing certain special format + characters. List of currently supported replacements: + + %% - Replaced by a single % + %c - Replaced by the content of COMMENT. + %C - Same as %c but put into parentheses. + %F - Replaced by an ssh style fingerprint computed from KEY. + + The functions returns 0 on success or an error code. On success a + newly allocated string is stored at the address of RESULT. + */ +static gpg_error_t +modify_description (const char *in, const char *comment, const gcry_sexp_t key, + char **result) +{ + size_t comment_length; + size_t in_len; + size_t out_len; + char *out; + size_t i; + int special, pass; + char *ssh_fpr = NULL; + + comment_length = strlen (comment); + in_len = strlen (in); + + /* First pass calculates the length, second pass does the actual + copying. */ + out = NULL; + out_len = 0; + for (pass=0; pass < 2; pass++) + { + special = 0; + for (i = 0; i < in_len; i++) + { + if (special) + { + special = 0; + switch (in[i]) + { + case '%': + if (out) + *out++ = '%'; + else + out_len++; + break; + + case 'c': /* Comment. */ + if (out) + { + memcpy (out, comment, comment_length); + out += comment_length; + } + else + out_len += comment_length; + break; + + case 'C': /* Comment. */ + if (!comment_length) + ; + else if (out) + { + *out++ = '('; + memcpy (out, comment, comment_length); + out += comment_length; + *out++ = ')'; + } + else + out_len += comment_length + 2; + break; + + case 'F': /* SSH style fingerprint. */ + if (!ssh_fpr && key) + ssh_get_fingerprint_string (key, &ssh_fpr); + if (ssh_fpr) + { + if (out) + out = stpcpy (out, ssh_fpr); + else + out_len += strlen (ssh_fpr); + } + break; + + default: /* Invalid special sequences are kept as they are. */ + if (out) + { + *out++ = '%'; + *out++ = in[i]; + } + else + out_len+=2; + break; + } + } + else if (in[i] == '%') + special = 1; + else + { + if (out) + *out++ = in[i]; + else + out_len++; + } + } + + if (!pass) + { + *result = out = xtrymalloc (out_len + 1); + if (!out) + { + xfree (ssh_fpr); + return gpg_error_from_syserror (); + } + } + } + + *out = 0; + assert (*result + out_len == out); + xfree (ssh_fpr); + return 0; +} + + + +/* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP + should be the hex encoded keygrip of that key to be used with the + caching mechanism. DESC_TEXT may be set to override the default + description used for the pinentry. If LOOKUP_TTL is given this + function is used to lookup the default ttl. If R_PASSPHRASE is not + NULL, the function succeeded and the key was protected the used + passphrase (entered or from the cache) is stored there; if not NULL + will be stored. The caller needs to free the returned + passphrase. */ +static int +unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, + unsigned char **keybuf, const unsigned char *grip, + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + char **r_passphrase) +{ + struct pin_entry_info_s *pi; + struct try_unprotect_arg_s arg; + int rc; + unsigned char *result; + size_t resultlen; + char hexgrip[40+1]; + + if (r_passphrase) + *r_passphrase = NULL; + + bin2hex (grip, 20, hexgrip); + + /* Initially try to get it using a cache nonce. */ + if (cache_nonce) + { + char *pw; + + pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); + if (pw) + { + rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen); + if (!rc) + { + if (r_passphrase) + *r_passphrase = pw; + else + xfree (pw); + xfree (*keybuf); + *keybuf = result; + return 0; + } + xfree (pw); + } + } + + /* First try to get it from the cache - if there is none or we can't + unprotect it, we fall back to ask the user */ + if (cache_mode != CACHE_MODE_IGNORE) + { + char *pw; + + retry: + pw = agent_get_cache (hexgrip, cache_mode); + if (pw) + { + rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen); + if (!rc) + { + if (cache_mode == CACHE_MODE_NORMAL) + agent_store_cache_hit (hexgrip); + if (r_passphrase) + *r_passphrase = pw; + else + xfree (pw); + xfree (*keybuf); + *keybuf = result; + return 0; + } + xfree (pw); + rc = 0; + } + else if (cache_mode == CACHE_MODE_NORMAL) + { + /* The standard use of GPG keys is to have a signing and an + encryption subkey. Commonly both use the same + passphrase. We try to help the user to enter the + passphrase only once by silently trying the last + correctly entered passphrase. Checking one additional + passphrase should be acceptable; despite the S2K + introduced delays. The assumed workflow is: + + 1. Read encrypted message in a MUA and thus enter a + passphrase for the encryption subkey. + + 2. Reply to that mail with an encrypted and signed + mail, thus entering the passphrase for the signing + subkey. + + We can often avoid the passphrase entry in the second + step. We do this only in normal mode, so not to + interfere with unrelated cache entries. */ + pw = agent_get_cache (NULL, cache_mode); + if (pw) + { + rc = agent_unprotect (ctrl, *keybuf, pw, NULL, + &result, &resultlen); + if (!rc) + { + if (r_passphrase) + *r_passphrase = pw; + else + xfree (pw); + xfree (*keybuf); + *keybuf = result; + return 0; + } + xfree (pw); + rc = 0; + } + } + + /* If the pinentry is currently in use, we wait up to 60 seconds + for it to close and check the cache again. This solves a common + situation where several requests for unprotecting a key have + been made but the user is still entering the passphrase for + the first request. Because all requests to agent_askpin are + serialized they would then pop up one after the other to + request the passphrase - despite that the user has already + entered it and is then available in the cache. This + implementation is not race free but in the worst case the + user has to enter the passphrase only once more. */ + if (pinentry_active_p (ctrl, 0)) + { + /* Active - wait */ + if (!pinentry_active_p (ctrl, 60)) + { + /* We need to give the other thread a chance to actually put + it into the cache. */ + npth_sleep (1); + goto retry; + } + /* Timeout - better call pinentry now the plain way. */ + } + } + + pi = gcry_calloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1); + if (!pi) + return gpg_error_from_syserror (); + pi->max_length = MAX_PASSPHRASE_LEN + 1; + pi->min_digits = 0; /* we want a real passphrase */ + pi->max_digits = 16; + pi->max_tries = 3; + pi->check_cb = try_unprotect_cb; + arg.ctrl = ctrl; + arg.protected_key = *keybuf; + arg.unprotected_key = NULL; + arg.change_required = 0; + pi->check_cb_arg = &arg; + + rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi, hexgrip, cache_mode); + if (!rc) + { + assert (arg.unprotected_key); + if (arg.change_required) + { + /* The callback told as that the user should change their + passphrase. Present the dialog to do. */ + size_t canlen, erroff; + gcry_sexp_t s_skey; + + assert (arg.unprotected_key); + canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL); + rc = gcry_sexp_sscan (&s_skey, &erroff, + (char*)arg.unprotected_key, canlen); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + wipememory (arg.unprotected_key, canlen); + xfree (arg.unprotected_key); + xfree (pi); + return rc; + } + rc = agent_protect_and_store (ctrl, s_skey, NULL); + gcry_sexp_release (s_skey); + if (rc) + { + log_error ("changing the passphrase failed: %s\n", + gpg_strerror (rc)); + wipememory (arg.unprotected_key, canlen); + xfree (arg.unprotected_key); + xfree (pi); + return rc; + } + } + else + { + /* Passphrase is fine. */ + agent_put_cache (hexgrip, cache_mode, pi->pin, + lookup_ttl? lookup_ttl (hexgrip) : 0); + agent_store_cache_hit (hexgrip); + if (r_passphrase && *pi->pin) + *r_passphrase = xtrystrdup (pi->pin); + } + xfree (*keybuf); + *keybuf = arg.unprotected_key; + } + xfree (pi); + return rc; +} + + +/* Read the key identified by GRIP from the private key directory and + return it as an gcrypt S-expression object in RESULT. On failure + returns an error code and stores NULL at RESULT. */ +static gpg_error_t +read_key_file (const unsigned char *grip, gcry_sexp_t *result) +{ + int rc; + char *fname; + estream_t fp; + struct stat st; + unsigned char *buf; + size_t buflen, erroff; + gcry_sexp_t s_skey; + char hexgrip[40+4+1]; + char first; + + *result = NULL; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + fp = es_fopen (fname, "rb"); + if (!fp) + { + rc = gpg_error_from_syserror (); + if (gpg_err_code (rc) != GPG_ERR_ENOENT) + log_error ("can't open '%s': %s\n", fname, strerror (errno)); + xfree (fname); + return rc; + } + + if (es_fread (&first, 1, 1, fp) != 1) + { + rc = gpg_error_from_syserror (); + log_error ("error reading first byte from '%s': %s\n", + fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + rc = es_fseek (fp, 0, SEEK_SET); + if (rc) + { + log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + if (first != '(') + { + /* Key is in extended format. */ + nvc_t pk; + int line; + + rc = nvc_parse_private_key (&pk, &line, fp); + es_fclose (fp); + + if (rc) + log_error ("error parsing '%s' line %d: %s\n", + fname, line, gpg_strerror (rc)); + else + { + rc = nvc_get_private_key (pk, result); + nvc_release (pk); + if (rc) + log_error ("error getting private key from '%s': %s\n", + fname, gpg_strerror (rc)); + } + + xfree (fname); + return rc; + } + + if (fstat (es_fileno (fp), &st)) + { + rc = gpg_error_from_syserror (); + log_error ("can't stat '%s': %s\n", fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + return rc; + } + + buflen = st.st_size; + buf = xtrymalloc (buflen+1); + if (!buf) + { + rc = gpg_error_from_syserror (); + log_error ("error allocating %zu bytes for '%s': %s\n", + buflen, fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + xfree (buf); + return rc; + + } + + if (es_fread (buf, buflen, 1, fp) != 1) + { + rc = gpg_error_from_syserror (); + log_error ("error reading %zu bytes from '%s': %s\n", + buflen, fname, strerror (errno)); + xfree (fname); + es_fclose (fp); + xfree (buf); + return rc; + } + + /* Convert the file into a gcrypt S-expression object. */ + rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); + xfree (fname); + es_fclose (fp); + xfree (buf); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + return rc; + } + *result = s_skey; + return 0; +} + + +/* Remove the key identified by GRIP from the private key directory. */ +static gpg_error_t +remove_key_file (const unsigned char *grip) +{ + gpg_error_t err = 0; + char *fname; + char hexgrip[40+4+1]; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + if (gnupg_remove (fname)) + err = gpg_error_from_syserror (); + xfree (fname); + return err; +} + + +/* Return the secret key as an S-Exp in RESULT after locating it using + the GRIP. If the operation shall be diverted to a token, an + allocated S-expression with the shadow_info part from the file is + stored at SHADOW_INFO; if not NULL will be stored at SHADOW_INFO. + CACHE_MODE defines now the cache shall be used. DESC_TEXT may be + set to present a custom description for the pinentry. LOOKUP_TTL + is an optional function to convey a TTL to the cache manager; we do + not simply pass the TTL value because the value is only needed if + an unprotect action was needed and looking up the TTL may have some + overhead (e.g. scanning the sshcontrol file). If a CACHE_NONCE is + given that cache item is first tried to get a passphrase. If + R_PASSPHRASE is not NULL, the function succeeded and the key was + protected the used passphrase (entered or from the cache) is stored + there; if not NULL will be stored. The caller needs to free the + returned passphrase. */ +gpg_error_t +agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, + const char *desc_text, + const unsigned char *grip, unsigned char **shadow_info, + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + gcry_sexp_t *result, char **r_passphrase) +{ + int rc; + unsigned char *buf; + size_t len, buflen, erroff; + gcry_sexp_t s_skey; + + *result = NULL; + if (shadow_info) + *shadow_info = NULL; + if (r_passphrase) + *r_passphrase = NULL; + + rc = read_key_file (grip, &s_skey); + if (rc) + { + if (gpg_err_code (rc) == GPG_ERR_ENOENT) + rc = gpg_error (GPG_ERR_NO_SECKEY); + return rc; + } + + /* For use with the protection functions we also need the key as an + canonical encoded S-expression in a buffer. Create this buffer + now. */ + rc = make_canon_sexp (s_skey, &buf, &len); + if (rc) + return rc; + + switch (agent_private_key_type (buf)) + { + case PRIVATE_KEY_CLEAR: + break; /* no unprotection needed */ + case PRIVATE_KEY_OPENPGP_NONE: + { + unsigned char *buf_new; + size_t buf_newlen; + + rc = agent_unprotect (ctrl, buf, "", NULL, &buf_new, &buf_newlen); + if (rc) + log_error ("failed to convert unprotected openpgp key: %s\n", + gpg_strerror (rc)); + else + { + xfree (buf); + buf = buf_new; + } + } + break; + case PRIVATE_KEY_PROTECTED: + { + char *desc_text_final; + char *comment = NULL; + + /* Note, that we will take the comment as a C string for + display purposes; i.e. all stuff beyond a Nul character is + ignored. */ + { + gcry_sexp_t comment_sexp; + + comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); + if (comment_sexp) + comment = gcry_sexp_nth_string (comment_sexp, 1); + gcry_sexp_release (comment_sexp); + } + + desc_text_final = NULL; + if (desc_text) + rc = modify_description (desc_text, comment? comment:"", s_skey, + &desc_text_final); + gcry_free (comment); + + if (!rc) + { + rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip, + cache_mode, lookup_ttl, r_passphrase); + if (rc) + log_error ("failed to unprotect the secret key: %s\n", + gpg_strerror (rc)); + } + + xfree (desc_text_final); + } + break; + case PRIVATE_KEY_SHADOWED: + if (shadow_info) + { + const unsigned char *s; + size_t n; + + rc = agent_get_shadow_info (buf, &s); + if (!rc) + { + n = gcry_sexp_canon_len (s, 0, NULL,NULL); + assert (n); + *shadow_info = xtrymalloc (n); + if (!*shadow_info) + rc = out_of_core (); + else + { + memcpy (*shadow_info, s, n); + rc = 0; + } + } + if (rc) + log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); + } + else + rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY); + break; + default: + log_error ("invalid private key format\n"); + rc = gpg_error (GPG_ERR_BAD_SECKEY); + break; + } + gcry_sexp_release (s_skey); + s_skey = NULL; + if (rc) + { + xfree (buf); + if (r_passphrase) + { + xfree (*r_passphrase); + *r_passphrase = NULL; + } + return rc; + } + + buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL); + rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); + wipememory (buf, buflen); + xfree (buf); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + if (r_passphrase) + { + xfree (*r_passphrase); + *r_passphrase = NULL; + } + return rc; + } + + *result = s_skey; + return 0; +} + + +/* Return the string name from the S-expression S_KEY as well as a + string describing the names of the parameters. ALGONAMESIZE and + ELEMSSIZE give the allocated size of the provided buffers. The + buffers may be NULL if not required. If R_LIST is not NULL the top + level list will be stored there; the caller needs to release it in + this case. */ +static gpg_error_t +key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, + char *r_algoname, size_t algonamesize, + char *r_elems, size_t elemssize) +{ + gcry_sexp_t list, l2; + const char *name, *algoname, *elems; + size_t n; + + if (r_list) + *r_list = NULL; + + list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 ); + if (!list) + list = gcry_sexp_find_token (s_key, "protected-private-key", 0 ); + if (!list) + list = gcry_sexp_find_token (s_key, "private-key", 0 ); + if (!list) + { + log_error ("invalid private key format\n"); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + name = gcry_sexp_nth_data (list, 0, &n); + if (n==3 && !memcmp (name, "rsa", 3)) + { + algoname = "rsa"; + elems = "ne"; + } + else if (n==3 && !memcmp (name, "dsa", 3)) + { + algoname = "dsa"; + elems = "pqgy"; + } + else if (n==3 && !memcmp (name, "ecc", 3)) + { + algoname = "ecc"; + elems = "pabgnq"; + } + else if (n==5 && !memcmp (name, "ecdsa", 5)) + { + algoname = "ecdsa"; + elems = "pabgnq"; + } + else if (n==4 && !memcmp (name, "ecdh", 4)) + { + algoname = "ecdh"; + elems = "pabgnq"; + } + else if (n==3 && !memcmp (name, "elg", 3)) + { + algoname = "elg"; + elems = "pgy"; + } + else + { + log_error ("unknown private key algorithm\n"); + gcry_sexp_release (list); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + if (r_algoname) + { + if (strlen (algoname) >= algonamesize) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + strcpy (r_algoname, algoname); + } + if (r_elems) + { + if (strlen (elems) >= elemssize) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + strcpy (r_elems, elems); + } + + if (r_list) + *r_list = list; + else + gcry_sexp_release (list); + + return 0; +} + + +/* Return true if KEYPARMS holds an EdDSA key. */ +static int +is_eddsa (gcry_sexp_t keyparms) +{ + int result = 0; + gcry_sexp_t list; + const char *s; + size_t n; + int i; + + list = gcry_sexp_find_token (keyparms, "flags", 0); + for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (list, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + result = 1; + break; + } + } + gcry_sexp_release (list); + return result; +} + + +/* Return the public key algorithm number if S_KEY is a DSA style key. + If it is not a DSA style key, return 0. */ +int +agent_is_dsa_key (gcry_sexp_t s_key) +{ + int result; + gcry_sexp_t list; + char algoname[6]; + + if (!s_key) + return 0; + + if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0)) + return 0; /* Error - assume it is not an DSA key. */ + + if (!strcmp (algoname, "dsa")) + result = GCRY_PK_DSA; + else if (!strcmp (algoname, "ecc")) + { + if (is_eddsa (list)) + result = 0; + else + result = GCRY_PK_ECDSA; + } + else if (!strcmp (algoname, "ecdsa")) + result = GCRY_PK_ECDSA; + else + result = 0; + + gcry_sexp_release (list); + return result; +} + + +/* Return true if S_KEY is an EdDSA key as used with curve Ed25519. */ +int +agent_is_eddsa_key (gcry_sexp_t s_key) +{ + int result; + gcry_sexp_t list; + char algoname[6]; + + if (!s_key) + return 0; + + if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0)) + return 0; /* Error - assume it is not an EdDSA key. */ + + if (!strcmp (algoname, "ecc") && is_eddsa (list)) + result = 1; + else if (!strcmp (algoname, "eddsa")) /* backward compatibility. */ + result = 1; + else + result = 0; + + gcry_sexp_release (list); + return result; +} + + +/* Return the key for the keygrip GRIP. The result is stored at + RESULT. This function extracts the key from the private key + database and returns it as an S-expression object as it is. On + failure an error code is returned and NULL stored at RESULT. */ +gpg_error_t +agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, + gcry_sexp_t *result) +{ + gpg_error_t err; + gcry_sexp_t s_skey; + + (void)ctrl; + + *result = NULL; + + err = read_key_file (grip, &s_skey); + if (!err) + *result = s_skey; + return err; +} + + +/* Return the public key for the keygrip GRIP. The result is stored + at RESULT. This function extracts the public key from the private + key database. On failure an error code is returned and NULL stored + at RESULT. */ +gpg_error_t +agent_public_key_from_file (ctrl_t ctrl, + const unsigned char *grip, + gcry_sexp_t *result) +{ + gpg_error_t err; + int i, idx; + gcry_sexp_t s_skey; + const char *algoname, *elems; + int npkey; + gcry_mpi_t array[10]; + gcry_sexp_t curve = NULL; + gcry_sexp_t flags = NULL; + gcry_sexp_t uri_sexp, comment_sexp; + const char *uri, *comment; + size_t uri_length, comment_length; + char *format, *p; + void *args[2+7+2+2+1]; /* Size is 2 + max. # of elements + 2 for uri + 2 + for comment + end-of-list. */ + int argidx; + gcry_sexp_t list = NULL; + const char *s; + + (void)ctrl; + + *result = NULL; + + err = read_key_file (grip, &s_skey); + if (err) + return err; + + for (i=0; i < DIM (array); i++) + array[i] = NULL; + + err = extract_private_key (s_skey, 0, &algoname, &npkey, NULL, &elems, + array, DIM (array), &curve, &flags); + if (err) + { + gcry_sexp_release (s_skey); + return err; + } + + uri = NULL; + uri_length = 0; + uri_sexp = gcry_sexp_find_token (s_skey, "uri", 0); + if (uri_sexp) + uri = gcry_sexp_nth_data (uri_sexp, 1, &uri_length); + + comment = NULL; + comment_length = 0; + comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); + if (comment_sexp) + comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length); + + gcry_sexp_release (s_skey); + s_skey = NULL; + + + /* FIXME: The following thing is pretty ugly code; we should + investigate how to make it cleaner. Probably code to handle + canonical S-expressions in a memory buffer is better suited for + such a task. After all that is what we do in protect.c. Neeed + to find common patterns and write a straightformward API to use + them. */ + assert (sizeof (size_t) <= sizeof (void*)); + + format = xtrymalloc (15+4+7*npkey+10+15+1+1); + if (!format) + { + err = gpg_error_from_syserror (); + for (i=0; array[i]; i++) + gcry_mpi_release (array[i]); + gcry_sexp_release (curve); + gcry_sexp_release (flags); + gcry_sexp_release (uri_sexp); + gcry_sexp_release (comment_sexp); + return err; + } + + argidx = 0; + p = stpcpy (stpcpy (format, "(public-key("), algoname); + p = stpcpy (p, "%S%S"); /* curve name and flags. */ + args[argidx++] = &curve; + args[argidx++] = &flags; + for (idx=0, s=elems; idx < npkey; idx++) + { + *p++ = '('; + *p++ = *s++; + p = stpcpy (p, " %m)"); + assert (argidx < DIM (args)); + args[argidx++] = &array[idx]; + } + *p++ = ')'; + if (uri) + { + p = stpcpy (p, "(uri %b)"); + assert (argidx+1 < DIM (args)); + args[argidx++] = (void *)&uri_length; + args[argidx++] = (void *)&uri; + } + if (comment) + { + p = stpcpy (p, "(comment %b)"); + assert (argidx+1 < DIM (args)); + args[argidx++] = (void *)&comment_length; + args[argidx++] = (void*)&comment; + } + *p++ = ')'; + *p = 0; + assert (argidx < DIM (args)); + args[argidx] = NULL; + + err = gcry_sexp_build_array (&list, NULL, format, args); + xfree (format); + for (i=0; array[i]; i++) + gcry_mpi_release (array[i]); + gcry_sexp_release (curve); + gcry_sexp_release (flags); + gcry_sexp_release (uri_sexp); + gcry_sexp_release (comment_sexp); + + if (!err) + *result = list; + return err; +} + + + +/* Check whether the the secret key identified by GRIP is available. + Returns 0 is the key is available. */ +int +agent_key_available (const unsigned char *grip) +{ + int result; + char *fname; + char hexgrip[40+4+1]; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, + hexgrip, NULL); + result = !access (fname, R_OK)? 0 : -1; + xfree (fname); + return result; +} + + + +/* Return the information about the secret key specified by the binary + keygrip GRIP. If the key is a shadowed one the shadow information + will be stored at the address R_SHADOW_INFO as an allocated + S-expression. */ +gpg_error_t +agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, + int *r_keytype, unsigned char **r_shadow_info) +{ + gpg_error_t err; + unsigned char *buf; + size_t len; + int keytype; + + (void)ctrl; + + if (r_keytype) + *r_keytype = PRIVATE_KEY_UNKNOWN; + if (r_shadow_info) + *r_shadow_info = NULL; + + { + gcry_sexp_t sexp; + + err = read_key_file (grip, &sexp); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_ENOENT) + return gpg_error (GPG_ERR_NOT_FOUND); + else + return err; + } + err = make_canon_sexp (sexp, &buf, &len); + gcry_sexp_release (sexp); + if (err) + return err; + } + + keytype = agent_private_key_type (buf); + switch (keytype) + { + case PRIVATE_KEY_CLEAR: + case PRIVATE_KEY_OPENPGP_NONE: + break; + case PRIVATE_KEY_PROTECTED: + /* If we ever require it we could retrieve the comment fields + from such a key. */ + break; + case PRIVATE_KEY_SHADOWED: + if (r_shadow_info) + { + const unsigned char *s; + size_t n; + + err = agent_get_shadow_info (buf, &s); + if (!err) + { + n = gcry_sexp_canon_len (s, 0, NULL, NULL); + assert (n); + *r_shadow_info = xtrymalloc (n); + if (!*r_shadow_info) + err = gpg_error_from_syserror (); + else + memcpy (*r_shadow_info, s, n); + } + } + break; + default: + err = gpg_error (GPG_ERR_BAD_SECKEY); + break; + } + + if (!err && r_keytype) + *r_keytype = keytype; + + xfree (buf); + return err; +} + + + +/* Delete the key with GRIP from the disk after having asked for + confirmation using DESC_TEXT. If FORCE is set the function won't + require a confirmation via Pinentry or warns if the key is also + used by ssh. + + Common error codes are: + GPG_ERR_NO_SECKEY + GPG_ERR_KEY_ON_CARD + GPG_ERR_NOT_CONFIRMED +*/ +gpg_error_t +agent_delete_key (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, int force) +{ + gpg_error_t err; + gcry_sexp_t s_skey = NULL; + unsigned char *buf = NULL; + size_t len; + char *desc_text_final = NULL; + char *comment = NULL; + ssh_control_file_t cf = NULL; + char hexgrip[40+4+1]; + char *default_desc = NULL; + + err = read_key_file (grip, &s_skey); + if (gpg_err_code (err) == GPG_ERR_ENOENT) + err = gpg_error (GPG_ERR_NO_SECKEY); + if (err) + goto leave; + + err = make_canon_sexp (s_skey, &buf, &len); + if (err) + goto leave; + + switch (agent_private_key_type (buf)) + { + case PRIVATE_KEY_CLEAR: + case PRIVATE_KEY_OPENPGP_NONE: + case PRIVATE_KEY_PROTECTED: + bin2hex (grip, 20, hexgrip); + if (!force) + { + if (!desc_text) + { + default_desc = xtryasprintf + (L_("Do you really want to delete the key identified by keygrip%%0A" + " %s%%0A %%C%%0A?"), hexgrip); + desc_text = default_desc; + } + + /* Note, that we will take the comment as a C string for + display purposes; i.e. all stuff beyond a Nul character is + ignored. */ + { + gcry_sexp_t comment_sexp; + + comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); + if (comment_sexp) + comment = gcry_sexp_nth_string (comment_sexp, 1); + gcry_sexp_release (comment_sexp); + } + + if (desc_text) + err = modify_description (desc_text, comment? comment:"", s_skey, + &desc_text_final); + if (err) + goto leave; + + err = agent_get_confirmation (ctrl, desc_text_final, + L_("Delete key"), L_("No"), 0); + if (err) + goto leave; + + cf = ssh_open_control_file (); + if (cf) + { + if (!ssh_search_control_file (cf, hexgrip, NULL, NULL, NULL)) + { + err = agent_get_confirmation + (ctrl, + L_("Warning: This key is also listed for use with SSH!\n" + "Deleting the key might remove your ability to " + "access remote machines."), + L_("Delete key"), L_("No"), 0); + if (err) + goto leave; + } + } + } + err = remove_key_file (grip); + break; + + case PRIVATE_KEY_SHADOWED: + err = remove_key_file (grip); + break; + + default: + log_error ("invalid private key format\n"); + err = gpg_error (GPG_ERR_BAD_SECKEY); + break; + } + + leave: + ssh_close_control_file (cf); + gcry_free (comment); + xfree (desc_text_final); + xfree (default_desc); + xfree (buf); + gcry_sexp_release (s_skey); + return err; +} + + +/* Write an S-expression formatted shadow key to our key storage. + Shadow key is created by an S-expression public key in PKBUF and + card's SERIALNO and the IDSTRING. With FORCE passed as true an + existing key with the given GRIP will get overwritten. */ +gpg_error_t +agent_write_shadow_key (const unsigned char *grip, + const char *serialno, const char *keyid, + const unsigned char *pkbuf, int force) +{ + gpg_error_t err; + unsigned char *shadow_info; + unsigned char *shdkey; + size_t len; + + shadow_info = make_shadow_info (serialno, keyid); + if (!shadow_info) + return gpg_error_from_syserror (); + + err = agent_shadow_key (pkbuf, shadow_info, &shdkey); + xfree (shadow_info); + if (err) + { + log_error ("shadowing the key failed: %s\n", gpg_strerror (err)); + return err; + } + + len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); + err = agent_write_private_key (grip, shdkey, len, force); + xfree (shdkey); + if (err) + log_error ("error writing key: %s\n", gpg_strerror (err)); + + return err; +} diff --git a/agent/genkey.c b/agent/genkey.c new file mode 100644 index 0000000..8a43d89 --- /dev/null +++ b/agent/genkey.c @@ -0,0 +1,617 @@ +/* genkey.c - Generate a keypair + * Copyright (C) 2002, 2003, 2004, 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH. + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include "i18n.h" +#include "exechelp.h" +#include "sysutils.h" + +static int +store_key (gcry_sexp_t private, const char *passphrase, int force, + unsigned long s2k_count) +{ + int rc; + unsigned char *buf; + size_t len; + unsigned char grip[20]; + + if ( !gcry_pk_get_keygrip (private, grip) ) + { + log_error ("can't calculate keygrip\n"); + return gpg_error (GPG_ERR_GENERAL); + } + + len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = gcry_malloc_secure (len); + if (!buf) + return out_of_core (); + len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + + if (passphrase) + { + unsigned char *p; + + rc = agent_protect (buf, passphrase, &p, &len, s2k_count, -1); + if (rc) + { + xfree (buf); + return rc; + } + xfree (buf); + buf = p; + } + + rc = agent_write_private_key (grip, buf, len, force); + xfree (buf); + return rc; +} + + +/* Count the number of non-alpha characters in S. Control characters + and non-ascii characters are not considered. */ +static size_t +nonalpha_count (const char *s) +{ + size_t n; + + for (n=0; *s; s++) + if (isascii (*s) && ( isdigit (*s) || ispunct (*s) )) + n++; + + return n; +} + + +/* Check PW against a list of pattern. Return 0 if PW does not match + these pattern. */ +static int +check_passphrase_pattern (ctrl_t ctrl, const char *pw) +{ + gpg_error_t err = 0; + const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN); + FILE *infp; + const char *argv[10]; + pid_t pid; + int result, i; + + (void)ctrl; + + infp = gnupg_tmpfile (); + if (!infp) + { + err = gpg_error_from_syserror (); + log_error (_("error creating temporary file: %s\n"), gpg_strerror (err)); + return 1; /* Error - assume password should not be used. */ + } + + if (fwrite (pw, strlen (pw), 1, infp) != 1) + { + err = gpg_error_from_syserror (); + log_error (_("error writing to temporary file: %s\n"), + gpg_strerror (err)); + fclose (infp); + return 1; /* Error - assume password should not be used. */ + } + fseek (infp, 0, SEEK_SET); + clearerr (infp); + + i = 0; + argv[i++] = "--null"; + argv[i++] = "--", + argv[i++] = opt.check_passphrase_pattern, + argv[i] = NULL; + assert (i < sizeof argv); + + if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid)) + result = 1; /* Execute error - assume password should no be used. */ + else if (gnupg_wait_process (pgmname, pid, 1, NULL)) + result = 1; /* Helper returned an error - probably a match. */ + else + result = 0; /* Success; i.e. no match. */ + gnupg_release_process (pid); + + /* Overwrite our temporary file. */ + fseek (infp, 0, SEEK_SET); + clearerr (infp); + for (i=((strlen (pw)+99)/100)*100; i > 0; i--) + putc ('\xff', infp); + fflush (infp); + fclose (infp); + return result; +} + + +static int +take_this_one_anyway2 (ctrl_t ctrl, const char *desc, const char *anyway_btn) +{ + gpg_error_t err; + + if (opt.enforce_passphrase_constraints) + { + err = agent_show_message (ctrl, desc, L_("Enter new passphrase")); + if (!err) + err = gpg_error (GPG_ERR_CANCELED); + } + else + err = agent_get_confirmation (ctrl, desc, + anyway_btn, L_("Enter new passphrase"), 0); + return err; +} + + +static int +take_this_one_anyway (ctrl_t ctrl, const char *desc) +{ + return take_this_one_anyway2 (ctrl, desc, L_("Take this one anyway")); +} + + +/* Check whether the passphrase PW is suitable. Returns 0 if the + passphrase is suitable and true if it is not and the user should be + asked to provide a different one. If FAILED_CONSTRAINT is set, a + message describing the problem is returned in + *FAILED_CONSTRAINT. */ +int +check_passphrase_constraints (ctrl_t ctrl, const char *pw, + char **failed_constraint) +{ + gpg_error_t err = 0; + unsigned int minlen = opt.min_passphrase_len; + unsigned int minnonalpha = opt.min_passphrase_nonalpha; + char *msg1 = NULL; + char *msg2 = NULL; + char *msg3 = NULL; + + if (ctrl && ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + return 0; + + if (!pw) + pw = ""; + + /* The first check is to warn about an empty passphrase. */ + if (!*pw) + { + const char *desc = (opt.enforce_passphrase_constraints? + L_("You have not entered a passphrase!%0A" + "An empty passphrase is not allowed.") : + L_("You have not entered a passphrase - " + "this is in general a bad idea!%0A" + "Please confirm that you do not want to " + "have any protection on your key.")); + + err = 1; + if (failed_constraint) + { + if (opt.enforce_passphrase_constraints) + *failed_constraint = xstrdup (desc); + else + err = take_this_one_anyway2 (ctrl, desc, + L_("Yes, protection is not needed")); + } + + goto leave; + } + + /* Now check the constraints and collect the error messages unless + in in silent mode which returns immediately. */ + if (utf8_charcount (pw, -1) < minlen ) + { + if (!failed_constraint) + { + err = gpg_error (GPG_ERR_INV_PASSPHRASE); + goto leave; + } + + msg1 = xtryasprintf + ( ngettext ("A passphrase should be at least %u character long.", + "A passphrase should be at least %u characters long.", + minlen), minlen ); + if (!msg1) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + if (nonalpha_count (pw) < minnonalpha ) + { + if (!failed_constraint) + { + err = gpg_error (GPG_ERR_INV_PASSPHRASE); + goto leave; + } + + msg2 = xtryasprintf + ( ngettext ("A passphrase should contain at least %u digit or%%0A" + "special character.", + "A passphrase should contain at least %u digits or%%0A" + "special characters.", + minnonalpha), minnonalpha ); + if (!msg2) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + /* If configured check the passphrase against a list of known words + and pattern. The actual test is done by an external program. + The warning message is generic to give the user no hint on how to + circumvent this list. */ + if (*pw && opt.check_passphrase_pattern && + check_passphrase_pattern (ctrl, pw)) + { + if (!failed_constraint) + { + err = gpg_error (GPG_ERR_INV_PASSPHRASE); + goto leave; + } + + msg3 = xtryasprintf + (L_("A passphrase may not be a known term or match%%0A" + "certain pattern.")); + if (!msg3) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + if (failed_constraint && (msg1 || msg2 || msg3)) + { + char *msg; + size_t n; + + msg = strconcat + (L_("Warning: You have entered an insecure passphrase."), + "%0A%0A", + msg1? msg1 : "", msg1? "%0A" : "", + msg2? msg2 : "", msg2? "%0A" : "", + msg3? msg3 : "", msg3? "%0A" : "", + NULL); + if (!msg) + { + err = gpg_error_from_syserror (); + goto leave; + } + /* Strip a trailing "%0A". */ + n = strlen (msg); + if (n > 3 && !strcmp (msg + n - 3, "%0A")) + msg[n-3] = 0; + + err = 1; + if (opt.enforce_passphrase_constraints) + *failed_constraint = msg; + else + { + err = take_this_one_anyway (ctrl, msg); + xfree (msg); + } + } + + leave: + xfree (msg1); + xfree (msg2); + xfree (msg3); + return err; +} + + +/* Callback function to compare the first entered PIN with the one + currently being entered. */ +static gpg_error_t +reenter_compare_cb (struct pin_entry_info_s *pi) +{ + const char *pin1 = pi->check_cb_arg; + + if (!strcmp (pin1, pi->pin)) + return 0; /* okay */ + return gpg_error (GPG_ERR_BAD_PASSPHRASE); +} + + +/* Ask the user for a new passphrase using PROMPT. On success the + function returns 0 and store the passphrase at R_PASSPHRASE; if the + user opted not to use a passphrase NULL will be stored there. The + user needs to free the returned string. In case of an error and + error code is returned and NULL stored at R_PASSPHRASE. */ +gpg_error_t +agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, + char **r_passphrase) +{ + gpg_error_t err; + const char *text1 = prompt; + const char *text2 = L_("Please re-enter this passphrase"); + char *initial_errtext = NULL; + struct pin_entry_info_s *pi, *pi2; + + *r_passphrase = NULL; + + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + { + size_t size; + size_t len = 100; + unsigned char *buffer; + + err = pinentry_loopback(ctrl, "NEW_PASSPHRASE", &buffer, &size, len); + if (!err) + { + if (size) + { + buffer[size] = 0; + *r_passphrase = buffer; + } + else + *r_passphrase = NULL; + } + return err; + } + + pi = gcry_calloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1); + if (!pi) + return gpg_error_from_syserror (); + pi2 = gcry_calloc_secure (1, sizeof (*pi2) + MAX_PASSPHRASE_LEN + 1); + if (!pi2) + { + err = gpg_error_from_syserror (); + xfree (pi2); + return err; + } + pi->max_length = MAX_PASSPHRASE_LEN + 1; + pi->max_tries = 3; + pi->with_qualitybar = 1; + pi->with_repeat = 1; + pi2->max_length = MAX_PASSPHRASE_LEN + 1; + pi2->max_tries = 3; + pi2->check_cb = reenter_compare_cb; + pi2->check_cb_arg = pi->pin; + + next_try: + err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi, NULL, 0); + xfree (initial_errtext); + initial_errtext = NULL; + if (!err) + { + if (check_passphrase_constraints (ctrl, pi->pin, &initial_errtext)) + { + pi->failed_tries = 0; + pi2->failed_tries = 0; + goto next_try; + } + /* Unless the passphrase is empty or the pinentry told us that + it already did the repetition check, ask to confirm it. */ + if (*pi->pin && !pi->repeat_okay) + { + err = agent_askpin (ctrl, text2, NULL, NULL, pi2, NULL, 0); + if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) + { /* The re-entered one did not match and the user did not + hit cancel. */ + initial_errtext = xtrystrdup (L_("does not match - try again")); + if (initial_errtext) + goto next_try; + err = gpg_error_from_syserror (); + } + } + } + + if (!err && *pi->pin) + { + /* User wants a passphrase. */ + *r_passphrase = xtrystrdup (pi->pin); + if (!*r_passphrase) + err = gpg_error_from_syserror (); + } + + xfree (initial_errtext); + xfree (pi2); + xfree (pi); + return err; +} + + + +/* Generate a new keypair according to the parameters given in + KEYPARAM. If CACHE_NONCE is given first try to lookup a passphrase + using the cache nonce. If NO_PROTECTION is true the key will not + be protected by a passphrase. If OVERRIDE_PASSPHRASE is true that + passphrase will be used for the new key. */ +int +agent_genkey (ctrl_t ctrl, const char *cache_nonce, + const char *keyparam, size_t keyparamlen, int no_protection, + const char *override_passphrase, int preset, membuf_t *outbuf) +{ + gcry_sexp_t s_keyparam, s_key, s_private, s_public; + char *passphrase_buffer = NULL; + const char *passphrase; + int rc; + size_t len; + char *buf; + + rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen); + if (rc) + { + log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc)); + return gpg_error (GPG_ERR_INV_DATA); + } + + /* Get the passphrase now, cause key generation may take a while. */ + if (override_passphrase) + passphrase = override_passphrase; + else if (no_protection || !cache_nonce) + passphrase = NULL; + else + { + passphrase_buffer = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); + passphrase = passphrase_buffer; + } + + if (passphrase || no_protection) + ; + else + { + rc = agent_ask_new_passphrase (ctrl, + L_("Please enter the passphrase to%0A" + "protect your new key"), + &passphrase_buffer); + if (rc) + return rc; + passphrase = passphrase_buffer; + } + + rc = gcry_pk_genkey (&s_key, s_keyparam ); + gcry_sexp_release (s_keyparam); + if (rc) + { + log_error ("key generation failed: %s\n", gpg_strerror (rc)); + xfree (passphrase_buffer); + return rc; + } + + /* break out the parts */ + s_private = gcry_sexp_find_token (s_key, "private-key", 0); + if (!s_private) + { + log_error ("key generation failed: invalid return value\n"); + gcry_sexp_release (s_key); + xfree (passphrase_buffer); + return gpg_error (GPG_ERR_INV_DATA); + } + s_public = gcry_sexp_find_token (s_key, "public-key", 0); + if (!s_public) + { + log_error ("key generation failed: invalid return value\n"); + gcry_sexp_release (s_private); + gcry_sexp_release (s_key); + xfree (passphrase_buffer); + return gpg_error (GPG_ERR_INV_DATA); + } + gcry_sexp_release (s_key); s_key = NULL; + + /* store the secret key */ + if (DBG_CRYPTO) + log_debug ("storing private key\n"); + rc = store_key (s_private, passphrase, 0, ctrl->s2k_count); + if (!rc) + { + if (!cache_nonce) + { + char tmpbuf[12]; + gcry_create_nonce (tmpbuf, 12); + cache_nonce = bin2hex (tmpbuf, 12, NULL); + } + if (cache_nonce + && !no_protection + && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE, + passphrase, ctrl->cache_ttl_opt_preset)) + agent_write_status (ctrl, "CACHE_NONCE", cache_nonce, NULL); + if (preset && !no_protection) + { + unsigned char grip[20]; + char hexgrip[40+1]; + if (gcry_pk_get_keygrip (s_private, grip)) + { + bin2hex(grip, 20, hexgrip); + rc = agent_put_cache (hexgrip, CACHE_MODE_ANY, passphrase, + ctrl->cache_ttl_opt_preset); + } + } + } + xfree (passphrase_buffer); + passphrase_buffer = NULL; + passphrase = NULL; + gcry_sexp_release (s_private); + if (rc) + { + gcry_sexp_release (s_public); + return rc; + } + + /* return the public key */ + if (DBG_CRYPTO) + log_debug ("returning public key\n"); + len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xtrymalloc (len); + if (!buf) + { + gpg_error_t tmperr = out_of_core (); + gcry_sexp_release (s_private); + gcry_sexp_release (s_public); + return tmperr; + } + len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + put_membuf (outbuf, buf, len); + gcry_sexp_release (s_public); + xfree (buf); + + return 0; +} + + + +/* Apply a new passphrase to the key S_SKEY and store it. If + PASSPHRASE_ADDR and *PASSPHRASE_ADDR are not NULL, use that + passphrase. If PASSPHRASE_ADDR is not NULL store a newly entered + passphrase at that address. */ +gpg_error_t +agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, + char **passphrase_addr) +{ + gpg_error_t err; + + if (passphrase_addr && *passphrase_addr) + { + /* Take an empty string as request not to protect the key. */ + err = store_key (s_skey, **passphrase_addr? *passphrase_addr:NULL, 1, + ctrl->s2k_count); + } + else + { + char *pass = NULL; + + if (passphrase_addr) + { + xfree (*passphrase_addr); + *passphrase_addr = NULL; + } + err = agent_ask_new_passphrase (ctrl, + L_("Please enter the new passphrase"), + &pass); + if (!err) + err = store_key (s_skey, pass, 1, ctrl->s2k_count); + if (!err && passphrase_addr) + *passphrase_addr = pass; + else + xfree (pass); + } + + return err; +} diff --git a/agent/gpg-agent-w32info.rc b/agent/gpg-agent-w32info.rc new file mode 100644 index 0000000..d586cad --- /dev/null +++ b/agent/gpg-agent-w32info.rc @@ -0,0 +1,50 @@ +/* gpg-agent-w32info.rc -*- c -*- + * Copyright (C) 2013 g10 Code GmbH + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This 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 "afxres.h" +#include "../common/w32info-rc.h" + +1 ICON "../common/gnupg.ico" + +1 VERSIONINFO + FILEVERSION W32INFO_VI_FILEVERSION + PRODUCTVERSION W32INFO_VI_PRODUCTVERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/ +#else + FILEFLAGS 0x00L +#endif + FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */ + FILETYPE 0x1L /* VFT_APP (0x1) */ + FILESUBTYPE 0x0L /* VFT2_UNKNOWN */ + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" /* US English (0409), Unicode (04b0) */ + BEGIN + VALUE "FileDescription", L"GnuPG\x2019s private key daemon\0" + VALUE "InternalName", "gpg-agent\0" + VALUE "OriginalFilename", "gpg-agent.exe\0" + VALUE "ProductName", W32INFO_PRODUCTNAME + VALUE "ProductVersion", W32INFO_PRODUCTVERSION + VALUE "CompanyName", W32INFO_COMPANYNAME + VALUE "FileVersion", W32INFO_FILEVERSION + VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT + VALUE "Comments", W32INFO_COMMENTS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 0x4b0 + END + END diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c new file mode 100644 index 0000000..f4ed6c5 --- /dev/null +++ b/agent/gpg-agent.c @@ -0,0 +1,3104 @@ +/* gpg-agent.c - The GnuPG Agent + * Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc. + * Copyright (C) 2000-2016 Werner Koch + * + * 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# ifndef WINVER +# define WINVER 0x0500 /* Same as in common/sysutils.c */ +# endif +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +# include +#else /*!HAVE_W32_SYSTEM*/ +# include +# include +#endif /*!HAVE_W32_SYSTEM*/ +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include + +#define GNUPG_COMMON_NEED_AFLOCAL +#include "agent.h" +#include /* Malloc hooks and socket wrappers. */ + +#include "i18n.h" +#include "sysutils.h" +#include "gc-opt-flags.h" +#include "exechelp.h" +#include "asshelp.h" +#include "../common/init.h" + + +enum cmd_and_opt_values +{ aNull = 0, + oCsh = 'c', + oQuiet = 'q', + oSh = 's', + oVerbose = 'v', + + oNoVerbose = 500, + aGPGConfList, + aGPGConfTest, + aUseStandardSocketP, + oOptions, + oDebug, + oDebugAll, + oDebugLevel, + oDebugWait, + oDebugQuickRandom, + oDebugPinentry, + oNoGreeting, + oNoOptions, + oHomedir, + oNoDetach, + oNoGrab, + oLogFile, + oServer, + oDaemon, + oSupervised, + oBatch, + + oPinentryProgram, + oPinentryTouchFile, + oPinentryInvisibleChar, + oPinentryTimeout, + oDisplay, + oTTYname, + oTTYtype, + oLCctype, + oLCmessages, + oXauthority, + oScdaemonProgram, + oDefCacheTTL, + oDefCacheTTLSSH, + oMaxCacheTTL, + oMaxCacheTTLSSH, + oEnforcePassphraseConstraints, + oMinPassphraseLen, + oMinPassphraseNonalpha, + oCheckPassphrasePattern, + oMaxPassphraseDays, + oEnablePassphraseHistory, + oUseStandardSocket, + oNoUseStandardSocket, + oExtraSocket, + oBrowserSocket, + oFakedSystemTime, + + oIgnoreCacheForSigning, + oAllowMarkTrusted, + oNoAllowMarkTrusted, + oAllowPresetPassphrase, + oAllowLoopbackPinentry, + oNoAllowLoopbackPinentry, + oNoAllowExternalCache, + oAllowEmacsPinentry, + oKeepTTY, + oKeepDISPLAY, + oSSHSupport, + oPuttySupport, + oDisableScdaemon, + oDisableCheckOwnSocket, + oWriteEnvFile +}; + + +#ifndef ENAMETOOLONG +# define ENAMETOOLONG EINVAL +#endif + + +static ARGPARSE_OPTS opts[] = { + + ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), + ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), + ARGPARSE_c (aUseStandardSocketP, "use-standard-socket-p", "@"), + + ARGPARSE_group (301, N_("@Options:\n ")), + + ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")), + ARGPARSE_s_n (oServer, "server", N_("run in server mode (foreground)")), +#ifndef HAVE_W32_SYSTEM + ARGPARSE_s_n (oSupervised, "supervised", N_("run in supervised mode")), +#endif + ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), + ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), + ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")), + ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")), + ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")), + + ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_n (oDebugAll, "debug-all", "@"), + ARGPARSE_s_s (oDebugLevel, "debug-level", "@"), + ARGPARSE_s_i (oDebugWait," debug-wait", "@"), + ARGPARSE_s_n (oDebugQuickRandom, "debug-quick-random", "@"), + ARGPARSE_s_n (oDebugPinentry, "debug-pinentry", "@"), + + ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")), + ARGPARSE_s_n (oNoGrab, "no-grab", N_("do not grab keyboard and mouse")), + ARGPARSE_s_s (oLogFile, "log-file", N_("use a log file for the server")), + ARGPARSE_s_s (oPinentryProgram, "pinentry-program", + /* */ N_("|PGM|use PGM as the PIN-Entry program")), + ARGPARSE_s_s (oPinentryTouchFile, "pinentry-touch-file", "@"), + ARGPARSE_s_s (oPinentryInvisibleChar, "pinentry-invisible-char", "@"), + ARGPARSE_s_u (oPinentryTimeout, "pinentry-timeout", "@"), + ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program", + /* */ N_("|PGM|use PGM as the SCdaemon program") ), + ARGPARSE_s_n (oDisableScdaemon, "disable-scdaemon", + /* */ N_("do not use the SCdaemon") ), + ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"), + + ARGPARSE_s_s (oExtraSocket, "extra-socket", + /* */ N_("|NAME|accept some commands via NAME")), + + ARGPARSE_s_s (oBrowserSocket, "browser-socket", "@"), + + ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), + + ARGPARSE_s_n (oBatch, "batch", "@"), + ARGPARSE_s_s (oHomedir, "homedir", "@"), + + ARGPARSE_s_s (oDisplay, "display", "@"), + ARGPARSE_s_s (oTTYname, "ttyname", "@"), + ARGPARSE_s_s (oTTYtype, "ttytype", "@"), + ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), + ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), + ARGPARSE_s_s (oXauthority, "xauthority", "@"), + ARGPARSE_s_n (oKeepTTY, "keep-tty", + /* */ N_("ignore requests to change the TTY")), + ARGPARSE_s_n (oKeepDISPLAY, "keep-display", + /* */ N_("ignore requests to change the X display")), + + ARGPARSE_s_u (oDefCacheTTL, "default-cache-ttl", + N_("|N|expire cached PINs after N seconds")), + ARGPARSE_s_u (oDefCacheTTLSSH, "default-cache-ttl-ssh", "@" ), + ARGPARSE_s_u (oMaxCacheTTL, "max-cache-ttl", "@" ), + ARGPARSE_s_u (oMaxCacheTTLSSH, "max-cache-ttl-ssh", "@" ), + + ARGPARSE_s_n (oEnforcePassphraseConstraints, "enforce-passphrase-constraints", + /* */ "@"), + ARGPARSE_s_u (oMinPassphraseLen, "min-passphrase-len", "@"), + ARGPARSE_s_u (oMinPassphraseNonalpha, "min-passphrase-nonalpha", "@"), + ARGPARSE_s_s (oCheckPassphrasePattern, "check-passphrase-pattern", "@"), + ARGPARSE_s_u (oMaxPassphraseDays, "max-passphrase-days", "@"), + ARGPARSE_s_n (oEnablePassphraseHistory, "enable-passphrase-history", "@"), + + ARGPARSE_s_n (oIgnoreCacheForSigning, "ignore-cache-for-signing", + /* */ N_("do not use the PIN cache when signing")), + ARGPARSE_s_n (oNoAllowExternalCache, "no-allow-external-cache", + /* */ N_("disallow the use of an external password cache")), + ARGPARSE_s_n (oNoAllowMarkTrusted, "no-allow-mark-trusted", + /* */ N_("disallow clients to mark keys as \"trusted\"")), + ARGPARSE_s_n (oAllowMarkTrusted, "allow-mark-trusted", "@"), + ARGPARSE_s_n (oAllowPresetPassphrase, "allow-preset-passphrase", + /* */ N_("allow presetting passphrase")), + ARGPARSE_s_n (oNoAllowLoopbackPinentry, "no-allow-loopback-pinentry", + N_("disallow caller to override the pinentry")), + ARGPARSE_s_n (oAllowLoopbackPinentry, "allow-loopback-pinentry", "@"), + ARGPARSE_s_n (oAllowEmacsPinentry, "allow-emacs-pinentry", + /* */ N_("allow passphrase to be prompted through Emacs")), + + ARGPARSE_s_n (oSSHSupport, "enable-ssh-support", N_("enable ssh support")), + ARGPARSE_s_n (oPuttySupport, "enable-putty-support", +#ifdef HAVE_W32_SYSTEM + /* */ N_("enable putty support") +#else + /* */ "@" +#endif + ), + + /* Dummy options for backward compatibility. */ + ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"), + ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"), + ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"), + + {0} /* End of list */ +}; + + +/* The list of supported debug flags. */ +static struct debug_flags_s debug_flags [] = + { + { DBG_MPI_VALUE , "mpi" }, + { DBG_CRYPTO_VALUE , "crypto" }, + { DBG_MEMORY_VALUE , "memory" }, + { DBG_CACHE_VALUE , "cache" }, + { DBG_MEMSTAT_VALUE, "memstat" }, + { DBG_HASHING_VALUE, "hashing" }, + { DBG_IPC_VALUE , "ipc" }, + { 77, NULL } /* 77 := Do not exit on "help" or "?". */ + }; + + + +#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */ +#define DEFAULT_CACHE_TTL_SSH (30*60) /* 30 minutes */ +#define MAX_CACHE_TTL (120*60) /* 2 hours */ +#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */ +#define MIN_PASSPHRASE_LEN (8) +#define MIN_PASSPHRASE_NONALPHA (1) +#define MAX_PASSPHRASE_DAYS (0) + +/* The timer tick used for housekeeping stuff. For Windows we use a + longer period as the SetWaitableTimer seems to signal earlier than + the 2 seconds. CHECK_OWN_SOCKET_INTERVAL defines how often we + check our own socket in standard socket mode. If that value is 0 + we don't check at all. All values are in seconds. */ +#if defined(HAVE_W32CE_SYSTEM) +# define TIMERTICK_INTERVAL (60) +# define CHECK_OWN_SOCKET_INTERVAL (0) /* Never */ +#elif defined(HAVE_W32_SYSTEM) +# define TIMERTICK_INTERVAL (4) +# define CHECK_OWN_SOCKET_INTERVAL (60) +#else +# define TIMERTICK_INTERVAL (2) +# define CHECK_OWN_SOCKET_INTERVAL (60) +#endif + + +/* Flag indicating that the ssh-agent subsystem has been enabled. */ +static int ssh_support; + +#ifdef HAVE_W32_SYSTEM +/* Flag indicating that support for Putty has been enabled. */ +static int putty_support; +/* A magic value used with WM_COPYDATA. */ +#define PUTTY_IPC_MAGIC 0x804e50ba +/* To avoid surprises we limit the size of the mapped IPC file to this + value. Putty currently (0.62) uses 8k, thus 16k should be enough + for the foreseeable future. */ +#define PUTTY_IPC_MAXLEN 16384 +#endif /*HAVE_W32_SYSTEM*/ + +/* The list of open file descriptors at startup. Note that this list + has been allocated using the standard malloc. */ +static int *startup_fd_list; + +/* The signal mask at startup and a flag telling whether it is valid. */ +#ifdef HAVE_SIGPROCMASK +static sigset_t startup_signal_mask; +static int startup_signal_mask_valid; +#endif + +/* Flag to indicate that a shutdown was requested. */ +static int shutdown_pending; + +/* Counter for the currently running own socket checks. */ +static int check_own_socket_running; + +/* Flags to indicate that check_own_socket shall not be called. */ +static int disable_check_own_socket; + +/* Flag indicating that we are in supervised mode. */ +static int is_supervised; + +/* Flag to inhibit socket removal in cleanup. */ +static int inhibit_socket_removal; + +/* It is possible that we are currently running under setuid permissions */ +static int maybe_setuid = 1; + +/* Name of the communication socket used for native gpg-agent + requests. The second variable is either NULL or a malloced string + with the real socket name in case it has been redirected. */ +static char *socket_name; +static char *redir_socket_name; + +/* Name of the optional extra socket used for native gpg-agent requests. */ +static char *socket_name_extra; +static char *redir_socket_name_extra; + +/* Name of the optional browser socket used for native gpg-agent requests. */ +static char *socket_name_browser; +static char *redir_socket_name_browser; + +/* Name of the communication socket used for ssh-agent-emulation. */ +static char *socket_name_ssh; +static char *redir_socket_name_ssh; + +/* We need to keep track of the server's nonces (these are dummies for + POSIX systems). */ +static assuan_sock_nonce_t socket_nonce; +static assuan_sock_nonce_t socket_nonce_extra; +static assuan_sock_nonce_t socket_nonce_browser; +static assuan_sock_nonce_t socket_nonce_ssh; + + +/* Default values for options passed to the pinentry. */ +static char *default_display; +static char *default_ttyname; +static char *default_ttytype; +static char *default_lc_ctype; +static char *default_lc_messages; +static char *default_xauthority; + +/* Name of a config file, which will be reread on a HUP if it is not NULL. */ +static char *config_filename; + +/* Helper to implement --debug-level */ +static const char *debug_level; + +/* Keep track of the current log file so that we can avoid updating + the log file after a SIGHUP if it didn't changed. Malloced. */ +static char *current_logfile; + +/* The handle_tick() function may test whether a parent is still + running. We record the PID of the parent here or -1 if it should be + watched. */ +static pid_t parent_pid = (pid_t)(-1); + +/* Number of active connections. */ +static int active_connections; + +/* This object is used to dispatch progress messages from Libgcrypt to + * the right thread. Given that we will have at max only a few dozen + * connections at a time, using a linked list is the easiest way to + * handle this. */ +struct progress_dispatch_s +{ + struct progress_dispatch_s *next; + /* The control object of the connection. If this is NULL no + * connection is associated with this item and it is free for reuse + * by new connections. */ + ctrl_t ctrl; + + /* The thread id of (npth_self) of the connection. */ + npth_t tid; + + /* The callback set by the connection. This is similar to the + * Libgcrypt callback but with the control object passed as the + * first argument. */ + void (*cb)(ctrl_t ctrl, + const char *what, int printchar, + int current, int total); +}; +struct progress_dispatch_s *progress_dispatch_list; + + + + +/* + Local prototypes. + */ + +static char *create_socket_name (char *standard_name, int with_homedir); +static gnupg_fd_t create_server_socket (char *name, int primary, int cygwin, + char **r_redir_name, + assuan_sock_nonce_t *nonce); +static void create_directories (void); + +static void agent_libgcrypt_progress_cb (void *data, const char *what, + int printchar, + int current, int total); +static void agent_init_default_ctrl (ctrl_t ctrl); +static void agent_deinit_default_ctrl (ctrl_t ctrl); + +static void handle_connections (gnupg_fd_t listen_fd, + gnupg_fd_t listen_fd_extra, + gnupg_fd_t listen_fd_browser, + gnupg_fd_t listen_fd_ssh); +static void check_own_socket (void); +static int check_for_running_agent (int silent); + +/* Pth wrapper function definitions. */ +ASSUAN_SYSTEM_NPTH_IMPL; + + +/* + Functions. + */ + +/* Allocate a string describing a library version by calling a GETFNC. + This function is expected to be called only once. GETFNC is + expected to have a semantic like gcry_check_version (). */ +static char * +make_libversion (const char *libname, const char *(*getfnc)(const char*)) +{ + const char *s; + char *result; + + if (maybe_setuid) + { + gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */ + maybe_setuid = 0; + } + s = getfnc (NULL); + result = xmalloc (strlen (libname) + 1 + strlen (s) + 1); + strcpy (stpcpy (stpcpy (result, libname), " "), s); + return result; +} + +/* Return strings describing this program. The case values are + described in common/argparse.c:strusage. The values here override + the default values given by strusage. */ +static const char * +my_strusage (int level) +{ + static char *ver_gcry; + const char *p; + + switch (level) + { + case 11: p = "@GPG_AGENT@ (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + /* TRANSLATORS: @EMAIL@ will get replaced by the actual bug + reporting address. This is so that we can change the + reporting address without breaking the translations. */ + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + + case 20: + if (!ver_gcry) + ver_gcry = make_libversion ("libgcrypt", gcry_check_version); + p = ver_gcry; + break; + + case 1: + case 40: p = _("Usage: @GPG_AGENT@ [options] (-h for help)"); + break; + case 41: p = _("Syntax: @GPG_AGENT@ [options] [command [args]]\n" + "Secret key management for @GNUPG@\n"); + break; + + default: p = NULL; + } + return p; +} + + + +/* Setup the debugging. With the global variable DEBUG_LEVEL set to NULL + only the active debug flags are propagated to the subsystems. With + DEBUG_LEVEL set, a specific set of debug flags is set; thus overriding + all flags already set. Note that we don't fail here, because it is + important to keep gpg-agent running even after re-reading the + options due to a SIGHUP. */ +static void +set_debug (void) +{ + int numok = (debug_level && digitp (debug_level)); + int numlvl = numok? atoi (debug_level) : 0; + + if (!debug_level) + ; + else if (!strcmp (debug_level, "none") || (numok && numlvl < 1)) + opt.debug = 0; + else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2)) + opt.debug = DBG_IPC_VALUE; + else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5)) + opt.debug = DBG_IPC_VALUE; + else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) + opt.debug = (DBG_IPC_VALUE | DBG_CACHE_VALUE); + else if (!strcmp (debug_level, "guru") || numok) + { + opt.debug = ~0; + /* Unless the "guru" string has been used we don't want to allow + hashing debugging. The rationale is that people tend to + select the highest debug value and would then clutter their + disk with debug files which may reveal confidential data. */ + if (numok) + opt.debug &= ~(DBG_HASHING_VALUE); + } + else + { + log_error (_("invalid debug-level '%s' given\n"), debug_level); + opt.debug = 0; /* Reset debugging, so that prior debug + statements won't have an undesired effect. */ + } + + if (opt.debug && !opt.verbose) + opt.verbose = 1; + if (opt.debug && opt.quiet) + opt.quiet = 0; + + if (opt.debug & DBG_MPI_VALUE) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); + if (opt.debug & DBG_CRYPTO_VALUE ) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + + if (opt.debug) + parse_debug_flag (NULL, &opt.debug, debug_flags); +} + + +/* Helper for cleanup to remove one socket with NAME. REDIR_NAME is + the corresponding real name if the socket has been redirected. */ +static void +remove_socket (char *name, char *redir_name) +{ + if (name && *name) + { + if (redir_name) + name = redir_name; + + gnupg_remove (name); + *name = 0; + } +} + + +/* Discover which inherited file descriptors correspond to which + * services/sockets offered by gpg-agent, using the LISTEN_FDS and + * LISTEN_FDNAMES convention. The understood labels are "ssh", + * "extra", and "browser". "std" or other labels will be interpreted + * as the standard socket. + * + * This function is designed to log errors when the expected file + * descriptors don't make sense, but to do its best to continue to + * work even in the face of minor misconfigurations. + * + * For more information on the LISTEN_FDS convention, see + * sd_listen_fds(3) on certain Linux distributions. + */ +#ifndef HAVE_W32_SYSTEM +static void +map_supervised_sockets (gnupg_fd_t *r_fd, + gnupg_fd_t *r_fd_extra, + gnupg_fd_t *r_fd_browser, + gnupg_fd_t *r_fd_ssh) +{ + struct { + const char *label; + int **fdaddr; + char **nameaddr; + } tbl[] = { + { "ssh", &r_fd_ssh, &socket_name_ssh }, + { "browser", &r_fd_browser, &socket_name_browser }, + { "extra", &r_fd_extra, &socket_name_extra }, + { "std", &r_fd, &socket_name } /* (Must be the last item.) */ + }; + const char *envvar; + char **fdnames; + int nfdnames; + int fd_count; + + *r_fd = *r_fd_extra = *r_fd_browser = *r_fd_ssh = -1; + + /* Print a warning if LISTEN_PID does not match outr pid. */ + envvar = getenv ("LISTEN_PID"); + if (!envvar) + log_error ("no LISTEN_PID environment variable found in " + "--supervised mode (ignoring)\n"); + else if (strtoul (envvar, NULL, 10) != (unsigned long)getpid ()) + log_error ("environment variable LISTEN_PID (%lu) does not match" + " our pid (%lu) in --supervised mode (ignoring)\n", + (unsigned long)strtoul (envvar, NULL, 10), + (unsigned long)getpid ()); + + /* Parse LISTEN_FDNAMES into the array FDNAMES. */ + envvar = getenv ("LISTEN_FDNAMES"); + if (envvar) + { + fdnames = strtokenize (envvar, ":"); + if (!fdnames) + { + log_error ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + agent_exit (1); + } + for (nfdnames=0; fdnames[nfdnames]; nfdnames++) + ; + } + else + { + fdnames = NULL; + nfdnames = 0; + } + + /* Parse LISTEN_FDS into fd_count or provide a replacement. */ + envvar = getenv ("LISTEN_FDS"); + if (envvar) + fd_count = atoi (envvar); + else if (fdnames) + { + log_error ("no LISTEN_FDS environment variable found in --supervised" + " mode (relying on LISTEN_FDNAMES instead)\n"); + fd_count = nfdnames; + } + else + { + log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables " + "found in --supervised mode" + " (assuming 1 active descriptor)\n"); + fd_count = 1; + } + + if (fd_count < 1) + { + log_error ("--supervised mode expects at least one file descriptor" + " (was told %d, carrying on as though it were 1)\n", + fd_count); + fd_count = 1; + } + + /* Assign the descriptors to the return values. */ + if (!fdnames) + { + struct stat statbuf; + + if (fd_count != 1) + log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1" + " in --supervised mode." + " (ignoring all sockets but the first one)\n", + fd_count); + if (fstat (3, &statbuf) == -1 && errno ==EBADF) + log_fatal ("file descriptor 3 must be valid in --supervised mode" + " if LISTEN_FDNAMES is not set\n"); + *r_fd = 3; + socket_name = gnupg_get_socket_name (3); + } + else if (fd_count != nfdnames) + { + log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match " + "LISTEN_FDS (%d) in --supervised mode\n", + nfdnames, fd_count); + } + else + { + int i, j, fd; + char *name; + + for (i = 0; i < nfdnames; i++) + { + for (j = 0; j < DIM (tbl); j++) + { + if (!strcmp (fdnames[i], tbl[j].label) || j == DIM(tbl)-1) + { + fd = 3 + i; + if (**tbl[j].fdaddr == -1) + { + name = gnupg_get_socket_name (fd); + if (name) + { + **tbl[j].fdaddr = fd; + *tbl[j].nameaddr = name; + log_info ("using fd %d for %s socket (%s)\n", + fd, tbl[j].label, name); + } + else + { + log_error ("cannot listen on fd %d for %s socket\n", + fd, tbl[j].label); + close (fd); + } + } + else + { + log_error ("cannot listen on more than one %s socket\n", + tbl[j].label); + close (fd); + } + break; + } + } + } + } + + xfree (fdnames); +} +#endif /*!HAVE_W32_SYSTEM*/ + + +/* Cleanup code for this program. This is either called has an atexit + handler or directly. */ +static void +cleanup (void) +{ + static int done; + + if (done) + return; + done = 1; + deinitialize_module_cache (); + if (!is_supervised && !inhibit_socket_removal) + { + remove_socket (socket_name, redir_socket_name); + if (opt.extra_socket > 1) + remove_socket (socket_name_extra, redir_socket_name_extra); + if (opt.browser_socket > 1) + remove_socket (socket_name_browser, redir_socket_name_browser); + remove_socket (socket_name_ssh, redir_socket_name_ssh); + } +} + + + +/* Handle options which are allowed to be reset after program start. + Return true when the current option in PARGS could be handled and + false if not. As a special feature, passing a value of NULL for + PARGS, resets the options to the default. REREAD should be set + true if it is not the initial option parsing. */ +static int +parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) +{ + if (!pargs) + { /* reset mode */ + opt.quiet = 0; + opt.verbose = 0; + opt.debug = 0; + opt.no_grab = 0; + opt.debug_pinentry = 0; + opt.pinentry_program = NULL; + opt.pinentry_touch_file = NULL; + xfree (opt.pinentry_invisible_char); + opt.pinentry_invisible_char = NULL; + opt.pinentry_timeout = 0; + opt.scdaemon_program = NULL; + opt.def_cache_ttl = DEFAULT_CACHE_TTL; + opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH; + opt.max_cache_ttl = MAX_CACHE_TTL; + opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH; + opt.enforce_passphrase_constraints = 0; + opt.min_passphrase_len = MIN_PASSPHRASE_LEN; + opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA; + opt.check_passphrase_pattern = NULL; + opt.max_passphrase_days = MAX_PASSPHRASE_DAYS; + opt.enable_passphrase_history = 0; + opt.ignore_cache_for_signing = 0; + opt.allow_mark_trusted = 1; + opt.allow_external_cache = 1; + opt.allow_loopback_pinentry = 1; + opt.allow_emacs_pinentry = 0; + opt.disable_scdaemon = 0; + disable_check_own_socket = 0; + return 1; + } + + switch (pargs->r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + + case oDebug: + parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags); + break; + case oDebugAll: opt.debug = ~0; break; + case oDebugLevel: debug_level = pargs->r.ret_str; break; + case oDebugPinentry: opt.debug_pinentry = 1; break; + + case oLogFile: + if (!reread) + return 0; /* not handeld */ + if (!current_logfile || !pargs->r.ret_str + || strcmp (current_logfile, pargs->r.ret_str)) + { + log_set_file (pargs->r.ret_str); + xfree (current_logfile); + current_logfile = xtrystrdup (pargs->r.ret_str); + } + break; + + case oNoGrab: opt.no_grab = 1; break; + + case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break; + case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break; + case oPinentryInvisibleChar: + xfree (opt.pinentry_invisible_char); + opt.pinentry_invisible_char = xtrystrdup (pargs->r.ret_str); break; + break; + case oPinentryTimeout: opt.pinentry_timeout = pargs->r.ret_ulong; break; + case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break; + case oDisableScdaemon: opt.disable_scdaemon = 1; break; + case oDisableCheckOwnSocket: disable_check_own_socket = 1; break; + + case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break; + case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break; + case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break; + case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = pargs->r.ret_ulong; break; + + case oEnforcePassphraseConstraints: + opt.enforce_passphrase_constraints=1; + break; + case oMinPassphraseLen: opt.min_passphrase_len = pargs->r.ret_ulong; break; + case oMinPassphraseNonalpha: + opt.min_passphrase_nonalpha = pargs->r.ret_ulong; + break; + case oCheckPassphrasePattern: + opt.check_passphrase_pattern = pargs->r.ret_str; + break; + case oMaxPassphraseDays: + opt.max_passphrase_days = pargs->r.ret_ulong; + break; + case oEnablePassphraseHistory: + opt.enable_passphrase_history = 1; + break; + + case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; + + case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break; + case oNoAllowMarkTrusted: opt.allow_mark_trusted = 0; break; + + case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break; + + case oAllowLoopbackPinentry: opt.allow_loopback_pinentry = 1; break; + case oNoAllowLoopbackPinentry: opt.allow_loopback_pinentry = 0; break; + + case oNoAllowExternalCache: opt.allow_external_cache = 0; + break; + + case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1; + break; + + default: + return 0; /* not handled */ + } + + return 1; /* handled */ +} + + +/* Fixup some options after all have been processed. */ +static void +finalize_rereadable_options (void) +{ +} + + +static void +thread_init_once (void) +{ + static int npth_initialized = 0; + + if (!npth_initialized) + { + npth_initialized++; + npth_init (); + } + gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + /* Now that we have set the syscall clamp we need to tell Libgcrypt + * that it should get them from libgpg-error. Note that Libgcrypt + * has already been initialized but at that point nPth was not + * initialized and thus Libgcrypt could not set its system call + * clamp. */ +#if GCRYPT_VERSION_NUMBER >= 0x010800 /* 1.8.0 */ + gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0); +#endif +} + + +static void +initialize_modules (void) +{ + thread_init_once (); + assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); + initialize_module_cache (); + initialize_module_call_pinentry (); + initialize_module_call_scd (); + initialize_module_trustlist (); +} + + +/* The main entry point. */ +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + FILE *configfp = NULL; + char *configname = NULL; + const char *shell; + unsigned configlineno; + int parse_debug = 0; + int default_config =1; + int pipe_server = 0; + int is_daemon = 0; + int nodetach = 0; + int csh_style = 0; + char *logfile = NULL; + int debug_wait = 0; + int gpgconf_list = 0; + gpg_error_t err; + struct assuan_malloc_hooks malloc_hooks; + + early_system_init (); + + /* Before we do anything else we save the list of currently open + file descriptors and the signal mask. This info is required to + do the exec call properly. */ + startup_fd_list = get_all_open_fds (); +#ifdef HAVE_SIGPROCMASK + if (!sigprocmask (SIG_UNBLOCK, NULL, &startup_signal_mask)) + startup_signal_mask_valid = 1; +#endif /*HAVE_SIGPROCMASK*/ + + /* Set program name etc. */ + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + /* Please note that we may running SUID(ROOT), so be very CAREFUL + when adding any stuff between here and the call to INIT_SECMEM() + somewhere after the option parsing */ + log_set_prefix (GPG_AGENT_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID); + + /* Make sure that our subsystems are ready. */ + i18n_init (); + init_common_subsystems (&argc, &argv); + + malloc_hooks.malloc = gcry_malloc; + malloc_hooks.realloc = gcry_realloc; + malloc_hooks.free = gcry_free; + assuan_set_malloc_hooks (&malloc_hooks); + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + assuan_sock_init (); + setup_libassuan_logging (&opt.debug, NULL); + + setup_libgcrypt_logging (); + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + gcry_set_progress_handler (agent_libgcrypt_progress_cb, NULL); + + disable_core_dumps (); + + /* Set default options. */ + parse_rereadable_options (NULL, 0); /* Reset them to default values. */ + + shell = getenv ("SHELL"); + if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) + csh_style = 1; + + /* Record some of the original environment strings. */ + { + const char *s; + int idx; + static const char *names[] = + { "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL }; + + err = 0; + opt.startup_env = session_env_new (); + if (!opt.startup_env) + err = gpg_error_from_syserror (); + for (idx=0; !err && names[idx]; idx++) + { + s = getenv (names[idx]); + if (s) + err = session_env_setenv (opt.startup_env, names[idx], s); + } + if (!err) + { + s = gnupg_ttyname (0); + if (s) + err = session_env_setenv (opt.startup_env, "GPG_TTY", s); + } + if (err) + log_fatal ("error recording startup environment: %s\n", + gpg_strerror (err)); + + /* Fixme: Better use the locale function here. */ + opt.startup_lc_ctype = getenv ("LC_CTYPE"); + if (opt.startup_lc_ctype) + opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype); + opt.startup_lc_messages = getenv ("LC_MESSAGES"); + if (opt.startup_lc_messages) + opt.startup_lc_messages = xstrdup (opt.startup_lc_messages); + } + + /* Check whether we have a config file 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 == oDebug || pargs.r_opt == oDebugAll) + parse_debug++; + else 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) + gnupg_set_homedir (pargs.r.ret_str); + else if (pargs.r_opt == oDebugQuickRandom) + { + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); + } + + } + + /* Initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0); + maybe_setuid = 0; + + /* + Now we are now working under our real uid + */ + + if (default_config) + configname = make_filename (gnupg_homedir (), + GPG_AGENT_NAME EXTSEP_S "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) + { + if( parse_debug ) + log_info (_("Note: no default option file '%s'\n"), + configname ); + /* Save the default conf file name so that + reread_configuration is able to test whether the + config file has been created in the meantime. */ + xfree (config_filename); + config_filename = configname; + configname = NULL; + } + else + { + log_error (_("option file '%s': %s\n"), + configname, strerror(errno) ); + exit(2); + } + xfree (configname); + configname = NULL; + } + if (parse_debug && configname ) + log_info (_("reading options from '%s'\n"), configname ); + default_config = 0; + } + + while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) + { + if (parse_rereadable_options (&pargs, 0)) + continue; /* Already handled */ + switch (pargs.r_opt) + { + case aGPGConfList: gpgconf_list = 1; break; + case aGPGConfTest: gpgconf_list = 2; break; + case aUseStandardSocketP: gpgconf_list = 3; break; + case oBatch: opt.batch=1; break; + + case oDebugWait: debug_wait = pargs.r.ret_int; 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 oNoGreeting: /* Dummy option. */ break; + case oNoVerbose: opt.verbose = 0; break; + case oNoOptions: break; /* no-options */ + case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; + case oNoDetach: nodetach = 1; break; + case oLogFile: logfile = pargs.r.ret_str; break; + case oCsh: csh_style = 1; break; + case oSh: csh_style = 0; break; + case oServer: pipe_server = 1; break; + case oDaemon: is_daemon = 1; break; + case oSupervised: is_supervised = 1; break; + + case oDisplay: default_display = xstrdup (pargs.r.ret_str); break; + case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break; + case oTTYtype: default_ttytype = xstrdup (pargs.r.ret_str); break; + case oLCctype: default_lc_ctype = xstrdup (pargs.r.ret_str); break; + case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str); + break; + case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str); + break; + + case oUseStandardSocket: + case oNoUseStandardSocket: + obsolete_option (configname, configlineno, "use-standard-socket"); + break; + + case oFakedSystemTime: + { + time_t faked_time = isotime2epoch (pargs.r.ret_str); + if (faked_time == (time_t)(-1)) + faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); + gnupg_set_time (faked_time, 0); + } + break; + + case oKeepTTY: opt.keep_tty = 1; break; + case oKeepDISPLAY: opt.keep_display = 1; break; + + case oSSHSupport: + ssh_support = 1; + break; + case oPuttySupport: +# ifdef HAVE_W32_SYSTEM + putty_support = 1; +# endif + break; + + case oExtraSocket: + opt.extra_socket = 1; /* (1 = points into argv) */ + socket_name_extra = pargs.r.ret_str; + break; + + case oBrowserSocket: + opt.browser_socket = 1; /* (1 = points into argv) */ + socket_name_browser = pargs.r.ret_str; + break; + + case oDebugQuickRandom: + /* Only used by the first stage command line parser. */ + break; + + case oWriteEnvFile: + obsolete_option (configname, configlineno, "write-env-file"); + break; + + default : pargs.err = configfp? 1:2; break; + } + } + if (configfp) + { + fclose( configfp ); + configfp = NULL; + /* Keep a copy of the name so that it can be read on SIGHUP. */ + if (config_filename != configname) + { + xfree (config_filename); + config_filename = configname; + } + configname = NULL; + goto next_pass; + } + + xfree (configname); + configname = NULL; + if (log_get_errorcount(0)) + exit(2); + + finalize_rereadable_options (); + + /* Print a warning if an argument looks like an option. */ + if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) + { + int i; + + for (i=0; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '-') + log_info (_("Note: '%s' is not considered an option\n"), argv[i]); + } + +#ifdef ENABLE_NLS + /* gpg-agent usually does not output any messages because it runs in + the background. For log files it is acceptable to have messages + always encoded in utf-8. We switch here to utf-8, so that + commands like --help still give native messages. It is far + easier to switch only once instead of for every message and it + actually helps when more then one thread is active (avoids an + extra copy step). */ + bind_textdomain_codeset (PACKAGE_GT, "UTF-8"); +#endif + + if (!pipe_server && !is_daemon && !gpgconf_list && !is_supervised) + { + /* We have been called without any command and thus we merely + check whether an agent is already running. We do this right + here so that we don't clobber a logfile with this check but + print the status directly to stderr. */ + opt.debug = 0; + set_debug (); + check_for_running_agent (0); + agent_exit (0); + } + + if (is_supervised) + ; + else if (!opt.extra_socket) + opt.extra_socket = 1; + else if (socket_name_extra + && (!strcmp (socket_name_extra, "none") + || !strcmp (socket_name_extra, "/dev/null"))) + { + /* User requested not to create this socket. */ + opt.extra_socket = 0; + socket_name_extra = NULL; + } + + if (is_supervised) + ; + else if (!opt.browser_socket) + opt.browser_socket = 1; + else if (socket_name_browser + && (!strcmp (socket_name_browser, "none") + || !strcmp (socket_name_browser, "/dev/null"))) + { + /* User requested not to create this socket. */ + opt.browser_socket = 0; + socket_name_browser = NULL; + } + + set_debug (); + + if (atexit (cleanup)) + { + log_error ("atexit failed\n"); + cleanup (); + exit (1); + } + + /* Try to create missing directories. */ + create_directories (); + + if (debug_wait && pipe_server) + { + thread_init_once (); + log_debug ("waiting for debugger - my pid is %u .....\n", + (unsigned int)getpid()); + gnupg_sleep (debug_wait); + log_debug ("... okay\n"); + } + + if (gpgconf_list == 3) + { + /* We now use the standard socket always - return true for + backward compatibility. */ + agent_exit (0); + } + else if (gpgconf_list == 2) + agent_exit (0); + else if (gpgconf_list) + { + char *filename; + char *filename_esc; + + /* List options and default values in the GPG Conf format. */ + filename = make_filename (gnupg_homedir (), + GPG_AGENT_NAME EXTSEP_S "conf", NULL); + filename_esc = percent_escape (filename, NULL); + + es_printf ("%s-%s.conf:%lu:\"%s\n", + GPGCONF_NAME, GPG_AGENT_NAME, + GC_OPT_FLAG_DEFAULT, filename_esc); + xfree (filename); + xfree (filename_esc); + + es_printf ("verbose:%lu:\n" + "quiet:%lu:\n" + "debug-level:%lu:\"none:\n" + "log-file:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME ); + es_printf ("default-cache-ttl:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL ); + es_printf ("default-cache-ttl-ssh:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL_SSH ); + es_printf ("max-cache-ttl:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL ); + es_printf ("max-cache-ttl-ssh:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH ); + es_printf ("enforce-passphrase-constraints:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("min-passphrase-len:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN ); + es_printf ("min-passphrase-nonalpha:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, + MIN_PASSPHRASE_NONALPHA); + es_printf ("check-passphrase-pattern:%lu:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME); + es_printf ("max-passphrase-days:%lu:%d:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, + MAX_PASSPHRASE_DAYS); + es_printf ("enable-passphrase-history:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("no-grab:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("ignore-cache-for-signing:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("no-allow-external-cache:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("no-allow-mark-trusted:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("disable-scdaemon:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE); +#ifdef HAVE_W32_SYSTEM + es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE); +#endif + es_printf ("no-allow-loopback-pinentry:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("allow-emacs-pinentry:%lu:\n", + GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); + es_printf ("pinentry-timeout:%lu:0:\n", + GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME); + + agent_exit (0); + } + + /* Now start with logging to a file if this is desired. */ + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX + | GPGRT_LOG_WITH_TIME + | GPGRT_LOG_WITH_PID)); + current_logfile = xstrdup (logfile); + } + + /* Make sure that we have a default ttyname. */ + if (!default_ttyname && gnupg_ttyname (1)) + default_ttyname = xstrdup (gnupg_ttyname (1)); + if (!default_ttytype && getenv ("TERM")) + default_ttytype = xstrdup (getenv ("TERM")); + + + if (pipe_server) + { + /* This is the simple pipe based server */ + ctrl_t ctrl; + + initialize_modules (); + + ctrl = xtrycalloc (1, sizeof *ctrl); + if (!ctrl) + { + log_error ("error allocating connection control data: %s\n", + strerror (errno) ); + agent_exit (1); + } + ctrl->session_env = session_env_new (); + if (!ctrl->session_env) + { + log_error ("error allocating session environment block: %s\n", + strerror (errno) ); + xfree (ctrl); + agent_exit (1); + } + agent_init_default_ctrl (ctrl); + start_command_handler (ctrl, GNUPG_INVALID_FD, GNUPG_INVALID_FD); + agent_deinit_default_ctrl (ctrl); + xfree (ctrl); + } + else if (is_supervised) + { +#ifndef HAVE_W32_SYSTEM + gnupg_fd_t fd, fd_extra, fd_browser, fd_ssh; + + initialize_modules (); + + /* when supervised and sending logs to stderr, the process + supervisor should handle log entry metadata (pid, name, + timestamp) */ + if (!logfile) + log_set_prefix (NULL, 0); + + log_info ("%s %s starting in supervised mode.\n", + strusage(11), strusage(13) ); + + /* See below in "regular server mode" on why we remove certain + * envvars. */ + if (!opt.keep_display) + gnupg_unsetenv ("DISPLAY"); + gnupg_unsetenv ("INSIDE_EMACS"); + + /* Virtually create the sockets. Note that we use -1 here + * because the whole thing works only on Unix. */ + map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh); + if (fd == -1) + log_fatal ("no standard socket provided\n"); + +#ifdef HAVE_SIGPROCMASK + if (startup_signal_mask_valid) + { + if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL)) + log_error ("error restoring signal mask: %s\n", + strerror (errno)); + } + else + log_info ("no saved signal mask\n"); +#endif /*HAVE_SIGPROCMASK*/ + + log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n", + fd, fd_extra, fd_browser, fd_ssh); + handle_connections (fd, fd_extra, fd_browser, fd_ssh); +#endif /*!HAVE_W32_SYSTEM*/ + } + else if (!is_daemon) + ; /* NOTREACHED */ + else + { /* Regular server mode */ + gnupg_fd_t fd; + gnupg_fd_t fd_extra = GNUPG_INVALID_FD; + gnupg_fd_t fd_browser = GNUPG_INVALID_FD; + gnupg_fd_t fd_ssh = GNUPG_INVALID_FD; +#ifndef HAVE_W32_SYSTEM + pid_t pid; +#endif + + /* Remove the DISPLAY variable so that a pinentry does not + default to a specific display. There is still a default + display when gpg-agent was started using --display or a + client requested this using an OPTION command. Note, that we + don't do this when running in reverse daemon mode (i.e. when + exec the program given as arguments). */ +#ifndef HAVE_W32_SYSTEM + if (!opt.keep_display && !argc) + gnupg_unsetenv ("DISPLAY"); +#endif + + /* Remove the INSIDE_EMACS variable so that a pinentry does not + always try to interact with Emacs. The variable is set when + a client requested this using an OPTION command. */ + gnupg_unsetenv ("INSIDE_EMACS"); + + /* Create the sockets. */ + socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1); + fd = create_server_socket (socket_name, 1, 0, + &redir_socket_name, &socket_nonce); + + if (opt.extra_socket) + { + if (socket_name_extra) + socket_name_extra = create_socket_name (socket_name_extra, 0); + else + socket_name_extra = create_socket_name + /**/ (GPG_AGENT_EXTRA_SOCK_NAME, 1); + opt.extra_socket = 2; /* Indicate that it has been malloced. */ + fd_extra = create_server_socket (socket_name_extra, 0, 0, + &redir_socket_name_extra, + &socket_nonce_extra); + } + + if (opt.browser_socket) + { + if (socket_name_browser) + socket_name_browser = create_socket_name (socket_name_browser, 0); + else + socket_name_browser= create_socket_name + /**/ (GPG_AGENT_BROWSER_SOCK_NAME, 1); + opt.browser_socket = 2; /* Indicate that it has been malloced. */ + fd_browser = create_server_socket (socket_name_browser, 0, 0, + &redir_socket_name_browser, + &socket_nonce_browser); + } + + socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1); + fd_ssh = create_server_socket (socket_name_ssh, 0, 1, + &redir_socket_name_ssh, + &socket_nonce_ssh); + + /* If we are going to exec a program in the parent, we record + the PID, so that the child may check whether the program is + still alive. */ + if (argc) + parent_pid = getpid (); + + fflush (NULL); + +#ifdef HAVE_W32_SYSTEM + + (void)csh_style; + (void)nodetach; + initialize_modules (); + +#else /*!HAVE_W32_SYSTEM*/ + + pid = fork (); + if (pid == (pid_t)-1) + { + log_fatal ("fork failed: %s\n", strerror (errno) ); + exit (1); + } + else if (pid) + { /* We are the parent */ + char *infostr_ssh_sock, *infostr_ssh_valid; + + /* Close the socket FD. */ + close (fd); + + /* The signal mask might not be correct right now and thus + we restore it. That is not strictly necessary but some + programs falsely assume a cleared signal mask. */ + +#ifdef HAVE_SIGPROCMASK + if (startup_signal_mask_valid) + { + if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL)) + log_error ("error restoring signal mask: %s\n", + strerror (errno)); + } + else + log_info ("no saved signal mask\n"); +#endif /*HAVE_SIGPROCMASK*/ + + /* Create the SSH info string if enabled. */ + if (ssh_support) + { + if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s", + socket_name_ssh) < 0) + { + log_error ("out of core\n"); + kill (pid, SIGTERM); + exit (1); + } + if (asprintf (&infostr_ssh_valid, "gnupg_SSH_AUTH_SOCK_by=%lu", + (unsigned long)getpid()) < 0) + { + log_error ("out of core\n"); + kill (pid, SIGTERM); + exit (1); + } + } + + *socket_name = 0; /* Don't let cleanup() remove the socket - + the child should do this from now on */ + if (opt.extra_socket) + *socket_name_extra = 0; + if (opt.browser_socket) + *socket_name_browser = 0; + *socket_name_ssh = 0; + + if (argc) + { /* Run the program given on the commandline. */ + if (ssh_support && (putenv (infostr_ssh_sock) + || putenv (infostr_ssh_valid))) + { + log_error ("failed to set environment: %s\n", + strerror (errno) ); + kill (pid, SIGTERM ); + exit (1); + } + + /* Close all the file descriptors except the standard + ones and those open at startup. We explicitly don't + close 0,1,2 in case something went wrong collecting + them at startup. */ + close_all_fds (3, startup_fd_list); + + /* Run the command. */ + execvp (argv[0], argv); + log_error ("failed to run the command: %s\n", strerror (errno)); + kill (pid, SIGTERM); + exit (1); + } + else + { + /* Print the environment string, so that the caller can use + shell's eval to set it */ + if (csh_style) + { + if (ssh_support) + { + *strchr (infostr_ssh_sock, '=') = ' '; + es_printf ("setenv %s;\n", infostr_ssh_sock); + } + } + else + { + if (ssh_support) + { + es_printf ("%s; export SSH_AUTH_SOCK;\n", + infostr_ssh_sock); + } + } + if (ssh_support) + { + xfree (infostr_ssh_sock); + xfree (infostr_ssh_valid); + } + exit (0); + } + /*NOTREACHED*/ + } /* End parent */ + + /* + This is the child + */ + + initialize_modules (); + + /* Detach from tty and put process into a new session */ + if (!nodetach ) + { + int i; + unsigned int oldflags; + + /* Close stdin, stdout and stderr unless it is the log stream */ + for (i=0; i <= 2; i++) + { + if (!log_test_fd (i) && i != fd ) + { + if ( ! close (i) + && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1) + { + log_error ("failed to open '%s': %s\n", + "/dev/null", strerror (errno)); + cleanup (); + exit (1); + } + } + } + if (setsid() == -1) + { + log_error ("setsid() failed: %s\n", strerror(errno) ); + cleanup (); + exit (1); + } + + log_get_prefix (&oldflags); + log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED); + opt.running_detached = 1; + } + + if (chdir("/")) + { + log_error ("chdir to / failed: %s\n", strerror (errno)); + exit (1); + } + + { + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); + } +#endif /*!HAVE_W32_SYSTEM*/ + + log_info ("%s %s started\n", strusage(11), strusage(13) ); + handle_connections (fd, fd_extra, fd_browser, fd_ssh); + assuan_sock_close (fd); + } + + return 0; +} + + +/* Exit entry point. This function should be called instead of a + plain exit. */ +void +agent_exit (int rc) +{ + /*FIXME: update_random_seed_file();*/ + + /* We run our cleanup handler because that may close cipher contexts + stored in secure memory and thus this needs to be done before we + explicitly terminate secure memory. */ + cleanup (); + +#if 1 + /* at this time a bit annoying */ + if (opt.debug & DBG_MEMSTAT_VALUE) + { + gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); + gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + } + if (opt.debug) + gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); +#endif + gcry_control (GCRYCTL_TERM_SECMEM ); + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} + + +/* This is our callback function for gcrypt progress messages. It is + set once at startup and dispatches progress messages to the + corresponding threads of the agent. */ +static void +agent_libgcrypt_progress_cb (void *data, const char *what, int printchar, + int current, int total) +{ + struct progress_dispatch_s *dispatch; + npth_t mytid = npth_self (); + + (void)data; + + for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next) + if (dispatch->ctrl && dispatch->tid == mytid) + break; + if (dispatch && dispatch->cb) + dispatch->cb (dispatch->ctrl, what, printchar, current, total); + + /* Libgcrypt < 1.8 does not know about nPth and thus when it reads + * from /dev/random this will block the process. To mitigate this + * problem we take a short nap when Libgcrypt tells us that it needs + * more entropy. This way other threads have chance to run. */ +#if GCRYPT_VERSION_NUMBER < 0x010800 /* 1.8.0 */ + if (what && !strcmp (what, "need_entropy")) + npth_usleep (100000); /* 100ms */ +#endif +} + + +/* If a progress dispatcher callback has been associated with the + * current connection unregister it. */ +static void +unregister_progress_cb (void) +{ + struct progress_dispatch_s *dispatch; + npth_t mytid = npth_self (); + + for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next) + if (dispatch->ctrl && dispatch->tid == mytid) + break; + if (dispatch) + { + dispatch->ctrl = NULL; + dispatch->cb = NULL; + } +} + + +/* Setup a progress callback CB for the current connection. Using a + * CB of NULL disables the callback. */ +void +agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what, + int printchar, int current, int total), + ctrl_t ctrl) +{ + struct progress_dispatch_s *dispatch, *firstfree; + npth_t mytid = npth_self (); + + firstfree = NULL; + for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next) + { + if (dispatch->ctrl && dispatch->tid == mytid) + break; + if (!dispatch->ctrl && !firstfree) + firstfree = dispatch; + } + if (!dispatch) /* None allocated: Reuse or allocate a new one. */ + { + if (firstfree) + { + dispatch = firstfree; + } + else if ((dispatch = xtrycalloc (1, sizeof *dispatch))) + { + dispatch->next = progress_dispatch_list; + progress_dispatch_list = dispatch; + } + else + { + log_error ("error allocating new progress dispatcher slot: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + return; + } + dispatch->ctrl = ctrl; + dispatch->tid = mytid; + } + + dispatch->cb = cb; +} + + +/* Each thread has its own local variables conveyed by a control + structure usually identified by an argument named CTRL. This + function is called immediately after allocating the control + structure. Its purpose is to setup the default values for that + structure. Note that some values may have already been set. */ +static void +agent_init_default_ctrl (ctrl_t ctrl) +{ + assert (ctrl->session_env); + + /* Note we ignore malloc errors because we can't do much about it + and the request will fail anyway shortly after this + initialization. */ + session_env_setenv (ctrl->session_env, "DISPLAY", default_display); + session_env_setenv (ctrl->session_env, "GPG_TTY", default_ttyname); + session_env_setenv (ctrl->session_env, "TERM", default_ttytype); + session_env_setenv (ctrl->session_env, "XAUTHORITY", default_xauthority); + session_env_setenv (ctrl->session_env, "PINENTRY_USER_DATA", NULL); + + if (ctrl->lc_ctype) + xfree (ctrl->lc_ctype); + ctrl->lc_ctype = default_lc_ctype? xtrystrdup (default_lc_ctype) : NULL; + + if (ctrl->lc_messages) + xfree (ctrl->lc_messages); + ctrl->lc_messages = default_lc_messages? xtrystrdup (default_lc_messages) + /**/ : NULL; + ctrl->cache_ttl_opt_preset = CACHE_TTL_OPT_PRESET; +} + + +/* Release all resources allocated by default in the control + structure. This is the counterpart to agent_init_default_ctrl. */ +static void +agent_deinit_default_ctrl (ctrl_t ctrl) +{ + unregister_progress_cb (); + session_env_release (ctrl->session_env); + + if (ctrl->lc_ctype) + xfree (ctrl->lc_ctype); + if (ctrl->lc_messages) + xfree (ctrl->lc_messages); +} + + +/* Because the ssh protocol does not send us information about the + current TTY setting, we use this function to use those from startup + or those explicitly set. This is also used for the restricted mode + where we ignore requests to change the environment. */ +gpg_error_t +agent_copy_startup_env (ctrl_t ctrl) +{ + static const char *names[] = + {"GPG_TTY", "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL}; + gpg_error_t err = 0; + int idx; + const char *value; + + for (idx=0; !err && names[idx]; idx++) + if ((value = session_env_getenv (opt.startup_env, names[idx]))) + err = session_env_setenv (ctrl->session_env, names[idx], value); + + if (!err && !ctrl->lc_ctype && opt.startup_lc_ctype) + if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype))) + err = gpg_error_from_syserror (); + + if (!err && !ctrl->lc_messages && opt.startup_lc_messages) + if (!(ctrl->lc_messages = xtrystrdup (opt.startup_lc_messages))) + err = gpg_error_from_syserror (); + + if (err) + log_error ("error setting default session environment: %s\n", + gpg_strerror (err)); + + return err; +} + + +/* Reread parts of the configuration. Note, that this function is + obviously not thread-safe and should only be called from the PTH + signal handler. + + Fixme: Due to the way the argument parsing works, we create a + memory leak here for all string type arguments. There is currently + no clean way to tell whether the memory for the argument has been + allocated or points into the process' original arguments. Unless + we have a mechanism to tell this, we need to live on with this. */ +static void +reread_configuration (void) +{ + ARGPARSE_ARGS pargs; + FILE *fp; + unsigned int configlineno = 0; + int dummy; + + if (!config_filename) + return; /* No config file. */ + + fp = fopen (config_filename, "r"); + if (!fp) + { + log_info (_("option file '%s': %s\n"), + config_filename, strerror(errno) ); + return; + } + + parse_rereadable_options (NULL, 1); /* Start from the default values. */ + + memset (&pargs, 0, sizeof pargs); + dummy = 0; + pargs.argc = &dummy; + pargs.flags = 1; /* do not remove the args */ + while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) ) + { + if (pargs.r_opt < -1) + pargs.err = 1; /* Print a warning. */ + else /* Try to parse this option - ignore unchangeable ones. */ + parse_rereadable_options (&pargs, 1); + } + fclose (fp); + finalize_rereadable_options (); + set_debug (); +} + + +/* Return the file name of the socket we are using for native + requests. */ +const char * +get_agent_socket_name (void) +{ + const char *s = socket_name; + + return (s && *s)? s : NULL; +} + +/* Return the file name of the socket we are using for SSH + requests. */ +const char * +get_agent_ssh_socket_name (void) +{ + const char *s = socket_name_ssh; + + return (s && *s)? s : NULL; +} + + +/* Return the number of active connections. */ +int +get_agent_active_connection_count (void) +{ + return active_connections; +} + + +/* Under W32, this function returns the handle of the scdaemon + notification event. Calling it the first time creates that + event. */ +#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM) +void * +get_agent_scd_notify_event (void) +{ + static HANDLE the_event = INVALID_HANDLE_VALUE; + + if (the_event == INVALID_HANDLE_VALUE) + { + HANDLE h, h2; + SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; + + /* We need to use a manual reset event object due to the way our + w32-pth wait function works: If we would use an automatic + reset event we are not able to figure out which handle has + been signaled because at the time we single out the signaled + handles using WFSO the event has already been reset due to + the WFMO. */ + h = CreateEvent (&sa, TRUE, FALSE, NULL); + if (!h) + log_error ("can't create scd notify event: %s\n", w32_strerror (-1) ); + else if (!DuplicateHandle (GetCurrentProcess(), h, + GetCurrentProcess(), &h2, + EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) + { + log_error ("setting syncronize for scd notify event failed: %s\n", + w32_strerror (-1) ); + CloseHandle (h); + } + else + { + CloseHandle (h); + the_event = h2; + } + } + + return the_event; +} +#endif /*HAVE_W32_SYSTEM && !HAVE_W32CE_SYSTEM*/ + + + +/* Create a name for the socket in the home directory as using + STANDARD_NAME. We also check for valid characters as well as + against a maximum allowed length for a unix domain socket is done. + The function terminates the process in case of an error. Returns: + Pointer to an allocated string with the absolute name of the socket + used. */ +static char * +create_socket_name (char *standard_name, int with_homedir) +{ + char *name; + + if (with_homedir) + name = make_filename (gnupg_socketdir (), standard_name, NULL); + else + name = make_filename (standard_name, NULL); + if (strchr (name, PATHSEP_C)) + { + log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S); + agent_exit (2); + } + return name; +} + + + +/* Create a Unix domain socket with NAME. Returns the file descriptor + or terminates the process in case of an error. Note that this + function needs to be used for the regular socket first (indicated + by PRIMARY) and only then for the extra and the ssh sockets. If + the socket has been redirected the name of the real socket is + stored as a malloced string at R_REDIR_NAME. If CYGWIN is set a + Cygwin compatible socket is created (Windows only). */ +static gnupg_fd_t +create_server_socket (char *name, int primary, int cygwin, + char **r_redir_name, assuan_sock_nonce_t *nonce) +{ + struct sockaddr *addr; + struct sockaddr_un *unaddr; + socklen_t len; + gnupg_fd_t fd; + int rc; + + xfree (*r_redir_name); + *r_redir_name = NULL; + + fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0); + if (fd == ASSUAN_INVALID_FD) + { + log_error (_("can't create socket: %s\n"), strerror (errno)); + *name = 0; /* Inhibit removal of the socket by cleanup(). */ + agent_exit (2); + } + + if (cygwin) + assuan_sock_set_flag (fd, "cygwin", 1); + + unaddr = xmalloc (sizeof *unaddr); + addr = (struct sockaddr*)unaddr; + + { + int redirected; + + if (assuan_sock_set_sockaddr_un (name, addr, &redirected)) + { + if (errno == ENAMETOOLONG) + log_error (_("socket name '%s' is too long\n"), name); + else + log_error ("error preparing socket '%s': %s\n", + name, gpg_strerror (gpg_error_from_syserror ())); + *name = 0; /* Inhibit removal of the socket by cleanup(). */ + agent_exit (2); + } + if (redirected) + { + *r_redir_name = xstrdup (unaddr->sun_path); + if (opt.verbose) + log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name); + } + } + + len = SUN_LEN (unaddr); + rc = assuan_sock_bind (fd, addr, len); + + /* Our error code mapping on W32CE returns EEXIST thus we also test + for this. */ + if (rc == -1 + && (errno == EADDRINUSE +#ifdef HAVE_W32_SYSTEM + || errno == EEXIST +#endif + )) + { + /* Check whether a gpg-agent is already running. We do this + test only if this is the primary socket. For secondary + sockets we assume that a test for gpg-agent has already been + done and reuse the requested socket. Testing the ssh-socket + is not possible because at this point, though we know the new + Assuan socket, the Assuan server and thus the ssh-agent + server is not yet operational; this would lead to a hang. */ + if (primary && !check_for_running_agent (1)) + { + log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX); + log_set_file (NULL); + log_error (_("a gpg-agent is already running - " + "not starting a new one\n")); + *name = 0; /* Inhibit removal of the socket by cleanup(). */ + assuan_sock_close (fd); + agent_exit (2); + } + gnupg_remove (unaddr->sun_path); + rc = assuan_sock_bind (fd, addr, len); + } + if (rc != -1 && (rc=assuan_sock_get_nonce (addr, len, nonce))) + log_error (_("error getting nonce for the socket\n")); + if (rc == -1) + { + /* We use gpg_strerror here because it allows us to get strings + for some W32 socket error codes. */ + log_error (_("error binding socket to '%s': %s\n"), + unaddr->sun_path, + gpg_strerror (gpg_error_from_syserror ())); + + assuan_sock_close (fd); + *name = 0; /* Inhibit removal of the socket by cleanup(). */ + agent_exit (2); + } + + if (gnupg_chmod (unaddr->sun_path, "-rwx")) + log_error (_("can't set permissions of '%s': %s\n"), + unaddr->sun_path, strerror (errno)); + + if (listen (FD2INT(fd), 5 ) == -1) + { + log_error (_("listen() failed: %s\n"), strerror (errno)); + *name = 0; /* Inhibit removal of the socket by cleanup(). */ + assuan_sock_close (fd); + agent_exit (2); + } + + if (opt.verbose) + log_info (_("listening on socket '%s'\n"), unaddr->sun_path); + + return fd; +} + + +/* Check that the directory for storing the private keys exists and + create it if not. This function won't fail as it is only a + convenience function and not strictly necessary. */ +static void +create_private_keys_directory (const char *home) +{ + char *fname; + struct stat statbuf; + + fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL); + if (stat (fname, &statbuf) && errno == ENOENT) + { + if (gnupg_mkdir (fname, "-rwx")) + log_error (_("can't create directory '%s': %s\n"), + fname, strerror (errno) ); + else if (!opt.quiet) + log_info (_("directory '%s' created\n"), fname); + } + if (gnupg_chmod (fname, "-rwx")) + log_error (_("can't set permissions of '%s': %s\n"), + fname, strerror (errno)); + xfree (fname); +} + + +/* Create the directory only if the supplied directory name is the + same as the default one. This way we avoid to create arbitrary + directories when a non-default home directory is used. To cope + with HOME, we compare only the suffix if we see that the default + homedir does start with a tilde. We don't stop here in case of + problems because other functions will throw an error anyway.*/ +static void +create_directories (void) +{ + struct stat statbuf; + const char *defhome = standard_homedir (); + char *home; + + home = make_filename (gnupg_homedir (), NULL); + if ( stat (home, &statbuf) ) + { + if (errno == ENOENT) + { + if ( +#ifdef HAVE_W32_SYSTEM + ( !compare_filenames (home, defhome) ) +#else + (*defhome == '~' + && (strlen (home) >= strlen (defhome+1) + && !strcmp (home + strlen(home) + - strlen (defhome+1), defhome+1))) + || (*defhome != '~' && !strcmp (home, defhome) ) +#endif + ) + { + if (gnupg_mkdir (home, "-rwx")) + log_error (_("can't create directory '%s': %s\n"), + home, strerror (errno) ); + else + { + if (!opt.quiet) + log_info (_("directory '%s' created\n"), home); + create_private_keys_directory (home); + } + } + } + else + log_error (_("stat() failed for '%s': %s\n"), home, strerror (errno)); + } + else if ( !S_ISDIR(statbuf.st_mode)) + { + log_error (_("can't use '%s' as home directory\n"), home); + } + else /* exists and is a directory. */ + { + create_private_keys_directory (home); + } + xfree (home); +} + + + +/* This is the worker for the ticker. It is called every few seconds + and may only do fast operations. */ +static void +handle_tick (void) +{ + static time_t last_minute; + + if (!last_minute) + last_minute = time (NULL); + + /* Check whether the scdaemon has died and cleanup in this case. */ + agent_scd_check_aliveness (); + + /* If we are running as a child of another process, check whether + the parent is still alive and shutdown if not. */ +#ifndef HAVE_W32_SYSTEM + if (parent_pid != (pid_t)(-1)) + { + if (kill (parent_pid, 0)) + { + shutdown_pending = 2; + log_info ("parent process died - shutting down\n"); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + cleanup (); + agent_exit (0); + } + } +#endif /*HAVE_W32_SYSTEM*/ + + /* Code to be run from time to time. */ +#if CHECK_OWN_SOCKET_INTERVAL > 0 + if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL)) + { + check_own_socket (); + last_minute = time (NULL); + } +#endif + +} + + +/* A global function which allows us to call the reload stuff from + other places too. This is only used when build for W32. */ +void +agent_sighup_action (void) +{ + log_info ("SIGHUP received - " + "re-reading configuration and flushing cache\n"); + + agent_flush_cache (); + reread_configuration (); + agent_reload_trustlist (); + /* We flush the module name cache so that after installing a + "pinentry" binary that one can be used in case the + "pinentry-basic" fallback was in use. */ + gnupg_module_name_flush_some (); +} + + +/* A helper function to handle SIGUSR2. */ +static void +agent_sigusr2_action (void) +{ + if (opt.verbose) + log_info ("SIGUSR2 received - updating card event counter\n"); + /* Nothing to check right now. We only increment a counter. */ + bump_card_eventcounter (); +} + + +#ifndef HAVE_W32_SYSTEM +/* The signal handler for this program. It is expected to be run in + its own trhead and not in the context of a signal handler. */ +static void +handle_signal (int signo) +{ + switch (signo) + { +#ifndef HAVE_W32_SYSTEM + case SIGHUP: + agent_sighup_action (); + break; + + case SIGUSR1: + log_info ("SIGUSR1 received - printing internal information:\n"); + /* Fixme: We need to see how to integrate pth dumping into our + logging system. */ + /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */ + agent_query_dump_state (); + agent_scd_dump_state (); + break; + + case SIGUSR2: + agent_sigusr2_action (); + break; + + case SIGTERM: + if (!shutdown_pending) + log_info ("SIGTERM received - shutting down ...\n"); + else + log_info ("SIGTERM received - still %i open connections\n", + active_connections); + shutdown_pending++; + if (shutdown_pending > 2) + { + log_info ("shutdown forced\n"); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + cleanup (); + agent_exit (0); + } + break; + + case SIGINT: + log_info ("SIGINT received - immediate shutdown\n"); + log_info( "%s %s stopped\n", strusage(11), strusage(13)); + cleanup (); + agent_exit (0); + break; +#endif + default: + log_info ("signal %d received - no action defined\n", signo); + } +} +#endif + +/* Check the nonce on a new connection. This is a NOP unless we we + are using our Unix domain socket emulation under Windows. */ +static int +check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce) +{ + if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce)) + { + log_info (_("error reading nonce on fd %d: %s\n"), + FD2INT(ctrl->thread_startup.fd), strerror (errno)); + assuan_sock_close (ctrl->thread_startup.fd); + xfree (ctrl); + return -1; + } + else + return 0; +} + + +#ifdef HAVE_W32_SYSTEM +/* The window message processing function for Putty. Warning: This + code runs as a native Windows thread. Use of our own functions + needs to be bracket with pth_leave/pth_enter. */ +static LRESULT CALLBACK +putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + int ret = 0; + int w32rc; + COPYDATASTRUCT *cds; + const char *mapfile; + HANDLE maphd; + PSID mysid = NULL; + PSID mapsid = NULL; + void *data = NULL; + PSECURITY_DESCRIPTOR psd = NULL; + ctrl_t ctrl = NULL; + + if (msg != WM_COPYDATA) + { + return DefWindowProc (hwnd, msg, wparam, lparam); + } + + cds = (COPYDATASTRUCT*)lparam; + if (cds->dwData != PUTTY_IPC_MAGIC) + return 0; /* Ignore data with the wrong magic. */ + mapfile = cds->lpData; + if (!cds->cbData || mapfile[cds->cbData - 1]) + return 0; /* Ignore empty and non-properly terminated strings. */ + + if (DBG_IPC) + { + npth_protect (); + log_debug ("ssh map file '%s'", mapfile); + npth_unprotect (); + } + + maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile); + if (DBG_IPC) + { + npth_protect (); + log_debug ("ssh map handle %p\n", maphd); + npth_unprotect (); + } + + if (!maphd || maphd == INVALID_HANDLE_VALUE) + return 0; + + npth_protect (); + + mysid = w32_get_user_sid (); + if (!mysid) + { + log_error ("error getting my sid\n"); + goto leave; + } + + w32rc = GetSecurityInfo (maphd, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION, + &mapsid, NULL, NULL, NULL, + &psd); + if (w32rc) + { + log_error ("error getting sid of ssh map file: rc=%d", w32rc); + goto leave; + } + + if (DBG_IPC) + { + char *sidstr; + + if (!ConvertSidToStringSid (mysid, &sidstr)) + sidstr = NULL; + log_debug (" my sid: '%s'", sidstr? sidstr: "[error]"); + LocalFree (sidstr); + if (!ConvertSidToStringSid (mapsid, &sidstr)) + sidstr = NULL; + log_debug ("ssh map file sid: '%s'", sidstr? sidstr: "[error]"); + LocalFree (sidstr); + } + + if (!EqualSid (mysid, mapsid)) + { + log_error ("ssh map file has a non-matching sid\n"); + goto leave; + } + + data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (DBG_IPC) + log_debug ("ssh IPC buffer at %p\n", data); + if (!data) + goto leave; + + /* log_printhex ("request:", data, 20); */ + + ctrl = xtrycalloc (1, sizeof *ctrl); + if (!ctrl) + { + log_error ("error allocating connection control data: %s\n", + strerror (errno) ); + goto leave; + } + ctrl->session_env = session_env_new (); + if (!ctrl->session_env) + { + log_error ("error allocating session environment block: %s\n", + strerror (errno) ); + goto leave; + } + + agent_init_default_ctrl (ctrl); + if (!serve_mmapped_ssh_request (ctrl, data, PUTTY_IPC_MAXLEN)) + ret = 1; /* Valid ssh message has been constructed. */ + agent_deinit_default_ctrl (ctrl); + /* log_printhex (" reply:", data, 20); */ + + leave: + xfree (ctrl); + if (data) + UnmapViewOfFile (data); + xfree (mapsid); + if (psd) + LocalFree (psd); + xfree (mysid); + CloseHandle (maphd); + + npth_unprotect (); + + return ret; +} +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* The thread handling Putty's IPC requests. */ +static void * +putty_message_thread (void *arg) +{ + WNDCLASS wndwclass = {0, putty_message_proc, 0, 0, + NULL, NULL, NULL, NULL, NULL, "Pageant"}; + HWND hwnd; + MSG msg; + + (void)arg; + + if (opt.verbose) + log_info ("putty message loop thread started\n"); + + /* The message loop runs as thread independent from our nPth system. + This also means that we need to make sure that we switch back to + our system before calling any no-windows function. */ + npth_unprotect (); + + /* First create a window to make sure that a message queue exists + for this thread. */ + if (!RegisterClass (&wndwclass)) + { + npth_protect (); + log_error ("error registering Pageant window class"); + return NULL; + } + hwnd = CreateWindowEx (0, "Pageant", "Pageant", 0, + 0, 0, 0, 0, + HWND_MESSAGE, /* hWndParent */ + NULL, /* hWndMenu */ + NULL, /* hInstance */ + NULL); /* lpParm */ + if (!hwnd) + { + npth_protect (); + log_error ("error creating Pageant window"); + return NULL; + } + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* Back to nPth. */ + npth_protect (); + + if (opt.verbose) + log_info ("putty message loop thread stopped\n"); + return NULL; +} +#endif /*HAVE_W32_SYSTEM*/ + + +static void * +do_start_connection_thread (ctrl_t ctrl) +{ + active_connections++; + agent_init_default_ctrl (ctrl); + if (opt.verbose && !DBG_IPC) + log_info (_("handler 0x%lx for fd %d started\n"), + (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + + start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd); + if (opt.verbose && !DBG_IPC) + log_info (_("handler 0x%lx for fd %d terminated\n"), + (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + + agent_deinit_default_ctrl (ctrl); + xfree (ctrl); + active_connections--; + return NULL; +} + + +/* This is the standard connection thread's main function. */ +static void * +start_connection_thread_std (void *arg) +{ + ctrl_t ctrl = arg; + + if (check_nonce (ctrl, &socket_nonce)) + { + log_error ("handler 0x%lx nonce check FAILED\n", + (unsigned long) npth_self()); + return NULL; + } + + return do_start_connection_thread (ctrl); +} + + +/* This is the extra socket connection thread's main function. */ +static void * +start_connection_thread_extra (void *arg) +{ + ctrl_t ctrl = arg; + + if (check_nonce (ctrl, &socket_nonce_extra)) + { + log_error ("handler 0x%lx nonce check FAILED\n", + (unsigned long) npth_self()); + return NULL; + } + + ctrl->restricted = 1; + return do_start_connection_thread (ctrl); +} + + +/* This is the browser socket connection thread's main function. */ +static void * +start_connection_thread_browser (void *arg) +{ + ctrl_t ctrl = arg; + + if (check_nonce (ctrl, &socket_nonce_browser)) + { + log_error ("handler 0x%lx nonce check FAILED\n", + (unsigned long) npth_self()); + return NULL; + } + + ctrl->restricted = 2; + return do_start_connection_thread (ctrl); +} + + +/* This is the ssh connection thread's main function. */ +static void * +start_connection_thread_ssh (void *arg) +{ + ctrl_t ctrl = arg; + + if (check_nonce (ctrl, &socket_nonce_ssh)) + return NULL; + + active_connections++; + agent_init_default_ctrl (ctrl); + if (opt.verbose) + log_info (_("ssh handler 0x%lx for fd %d started\n"), + (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + + start_command_handler_ssh (ctrl, ctrl->thread_startup.fd); + if (opt.verbose) + log_info (_("ssh handler 0x%lx for fd %d terminated\n"), + (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + + agent_deinit_default_ctrl (ctrl); + xfree (ctrl); + active_connections--; + return NULL; +} + + +/* Connection handler loop. Wait for connection requests and spawn a + thread after accepting a connection. */ +static void +handle_connections (gnupg_fd_t listen_fd, + gnupg_fd_t listen_fd_extra, + gnupg_fd_t listen_fd_browser, + gnupg_fd_t listen_fd_ssh) +{ + gpg_error_t err; + npth_attr_t tattr; + struct sockaddr_un paddr; + socklen_t plen; + fd_set fdset, read_fdset; + int ret; + gnupg_fd_t fd; + int nfd; + int saved_errno; + struct timespec abstime; + struct timespec curtime; + struct timespec timeout; +#ifdef HAVE_W32_SYSTEM + HANDLE events[2]; + unsigned int events_set; +#endif + int my_inotify_fd = -1; + struct { + const char *name; + void *(*func) (void *arg); + gnupg_fd_t l_fd; + } listentbl[] = { + { "std", start_connection_thread_std }, + { "extra", start_connection_thread_extra }, + { "browser", start_connection_thread_browser }, + { "ssh", start_connection_thread_ssh } + }; + + + ret = npth_attr_init(&tattr); + if (ret) + log_fatal ("error allocating thread attributes: %s\n", + strerror (ret)); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + +#ifndef HAVE_W32_SYSTEM + npth_sigev_init (); + npth_sigev_add (SIGHUP); + npth_sigev_add (SIGUSR1); + npth_sigev_add (SIGUSR2); + npth_sigev_add (SIGINT); + npth_sigev_add (SIGTERM); + npth_sigev_fini (); +#else +# ifdef HAVE_W32CE_SYSTEM + /* Use a dummy event. */ + sigs = 0; + ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); +# else + events[0] = get_agent_scd_notify_event (); + events[1] = INVALID_HANDLE_VALUE; +# endif +#endif + + if (disable_check_own_socket) + my_inotify_fd = -1; + else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name))) + { + if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED) + log_info ("error enabling fast daemon termination: %s\n", + gpg_strerror (err)); + } + + /* On Windows we need to fire up a separate thread to listen for + requests from Putty (an SSH client), so we can replace Putty's + Pageant (its ssh-agent implementation). */ +#ifdef HAVE_W32_SYSTEM + if (putty_support) + { + npth_t thread; + + ret = npth_create (&thread, &tattr, putty_message_thread, NULL); + if (ret) + { + log_error ("error spawning putty message loop: %s\n", strerror (ret)); + } + } +#endif /*HAVE_W32_SYSTEM*/ + + /* Set a flag to tell call-scd.c that it may enable event + notifications. */ + opt.sigusr2_enabled = 1; + + FD_ZERO (&fdset); + FD_SET (FD2INT (listen_fd), &fdset); + nfd = FD2INT (listen_fd); + if (listen_fd_extra != GNUPG_INVALID_FD) + { + FD_SET ( FD2INT(listen_fd_extra), &fdset); + if (FD2INT (listen_fd_extra) > nfd) + nfd = FD2INT (listen_fd_extra); + } + if (listen_fd_browser != GNUPG_INVALID_FD) + { + FD_SET ( FD2INT(listen_fd_browser), &fdset); + if (FD2INT (listen_fd_browser) > nfd) + nfd = FD2INT (listen_fd_browser); + } + if (listen_fd_ssh != GNUPG_INVALID_FD) + { + FD_SET ( FD2INT(listen_fd_ssh), &fdset); + if (FD2INT (listen_fd_ssh) > nfd) + nfd = FD2INT (listen_fd_ssh); + } + if (my_inotify_fd != -1) + { + FD_SET (my_inotify_fd, &fdset); + if (my_inotify_fd > nfd) + nfd = my_inotify_fd; + } + + listentbl[0].l_fd = listen_fd; + listentbl[1].l_fd = listen_fd_extra; + listentbl[2].l_fd = listen_fd_browser; + listentbl[3].l_fd = listen_fd_ssh; + + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + + for (;;) + { + /* Shutdown test. */ + if (shutdown_pending) + { + if (active_connections == 0) + break; /* ready */ + + /* Do not accept new connections but keep on running the + * loop to cope with the timer events. + * + * Note that we do not close the listening socket because a + * client trying to connect to that socket would instead + * restart a new dirmngr instance - which is unlikely the + * intention of a shutdown. */ + FD_ZERO (&fdset); + nfd = -1; + if (my_inotify_fd != -1) + { + FD_SET (my_inotify_fd, &fdset); + nfd = my_inotify_fd; + } + } + + /* POSIX says that fd_set should be implemented as a structure, + thus a simple assignment is fine to copy the entire set. */ + read_fdset = fdset; + + npth_clock_gettime (&curtime); + if (!(npth_timercmp (&curtime, &abstime, <))) + { + /* Timeout. */ + handle_tick (); + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + } + npth_timersub (&abstime, &curtime, &timeout); + +#ifndef HAVE_W32_SYSTEM + ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + npth_sigev_sigmask ()); + saved_errno = errno; + + { + int signo; + while (npth_sigev_get_pending (&signo)) + handle_signal (signo); + } +#else + ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + events, &events_set); + saved_errno = errno; + + /* This is valid even if npth_eselect returns an error. */ + if (events_set & 1) + agent_sigusr2_action (); +#endif + + if (ret == -1 && saved_errno != EINTR) + { + log_error (_("npth_pselect failed: %s - waiting 1s\n"), + strerror (saved_errno)); + npth_sleep (1); + continue; + } + if (ret <= 0) + /* Interrupt or timeout. Will be handled when calculating the + next timeout. */ + continue; + + if (!shutdown_pending) + { + int idx; + ctrl_t ctrl; + npth_t thread; + + if (my_inotify_fd != -1 + && FD_ISSET (my_inotify_fd, &read_fdset) + && gnupg_inotify_has_name (my_inotify_fd, GPG_AGENT_SOCK_NAME)) + { + shutdown_pending = 1; + log_info ("socket file has been removed - shutting down\n"); + } + + for (idx=0; idx < DIM(listentbl); idx++) + { + if (listentbl[idx].l_fd == GNUPG_INVALID_FD) + continue; + if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset)) + continue; + + plen = sizeof paddr; + fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd), + (struct sockaddr *)&paddr, &plen)); + if (fd == GNUPG_INVALID_FD) + { + log_error ("accept failed for %s: %s\n", + listentbl[idx].name, strerror (errno)); + } + else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl))) + { + log_error ("error allocating connection data for %s: %s\n", + listentbl[idx].name, strerror (errno) ); + assuan_sock_close (fd); + } + else if ( !(ctrl->session_env = session_env_new ())) + { + log_error ("error allocating session env block for %s: %s\n", + listentbl[idx].name, strerror (errno) ); + xfree (ctrl); + assuan_sock_close (fd); + } + else + { + ctrl->thread_startup.fd = fd; + ret = npth_create (&thread, &tattr, + listentbl[idx].func, ctrl); + if (ret) + { + log_error ("error spawning connection handler for %s:" + " %s\n", listentbl[idx].name, strerror (ret)); + assuan_sock_close (fd); + xfree (ctrl); + } + } + fd = GNUPG_INVALID_FD; + } + } + } + + if (my_inotify_fd != -1) + close (my_inotify_fd); + cleanup (); + log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); + npth_attr_destroy (&tattr); +} + + + +/* Helper for check_own_socket. */ +static gpg_error_t +check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length) +{ + membuf_t *mb = opaque; + put_membuf (mb, buffer, length); + return 0; +} + + +/* The thread running the actual check. We need to run this in a + separate thread so that check_own_thread can be called from the + timer tick. */ +static void * +check_own_socket_thread (void *arg) +{ + int rc; + char *sockname = arg; + assuan_context_t ctx = NULL; + membuf_t mb; + char *buffer; + + check_own_socket_running++; + + rc = assuan_new (&ctx); + if (rc) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc)); + goto leave; + } + assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1); + + rc = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0); + if (rc) + { + log_error ("can't connect my own socket: %s\n", gpg_strerror (rc)); + goto leave; + } + + init_membuf (&mb, 100); + rc = assuan_transact (ctx, "GETINFO pid", check_own_socket_pid_cb, &mb, + NULL, NULL, NULL, NULL); + put_membuf (&mb, "", 1); + buffer = get_membuf (&mb, NULL); + if (rc || !buffer) + { + log_error ("sending command \"%s\" to my own socket failed: %s\n", + "GETINFO pid", gpg_strerror (rc)); + rc = 1; + } + else if ( (pid_t)strtoul (buffer, NULL, 10) != getpid ()) + { + log_error ("socket is now serviced by another server\n"); + rc = 1; + } + else if (opt.verbose > 1) + log_error ("socket is still served by this server\n"); + + xfree (buffer); + + leave: + xfree (sockname); + if (ctx) + assuan_release (ctx); + if (rc) + { + /* We may not remove the socket as it is now in use by another + server. */ + inhibit_socket_removal = 1; + shutdown_pending = 2; + log_info ("this process is useless - shutting down\n"); + } + check_own_socket_running--; + return NULL; +} + + +/* Check whether we are still listening on our own socket. In case + another gpg-agent process started after us has taken ownership of + our socket, we would linger around without any real task. Thus we + better check once in a while whether we are really needed. */ +static void +check_own_socket (void) +{ + char *sockname; + npth_t thread; + npth_attr_t tattr; + int err; + + if (disable_check_own_socket) + return; + + if (check_own_socket_running || shutdown_pending) + return; /* Still running or already shutting down. */ + + sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + if (!sockname) + return; /* Out of memory. */ + + err = npth_attr_init (&tattr); + if (err) + return; + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + err = npth_create (&thread, &tattr, check_own_socket_thread, sockname); + if (err) + log_error ("error spawning check_own_socket_thread: %s\n", strerror (err)); + npth_attr_destroy (&tattr); +} + + + +/* Figure out whether an agent is available and running. Prints an + error if not. If SILENT is true, no messages are printed. + Returns 0 if the agent is running. */ +static int +check_for_running_agent (int silent) +{ + gpg_error_t err; + char *sockname; + assuan_context_t ctx = NULL; + + sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + if (!sockname) + return gpg_error_from_syserror (); + + err = assuan_new (&ctx); + if (!err) + err = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0); + xfree (sockname); + if (err) + { + if (!silent) + log_error (_("no gpg-agent running in this session\n")); + + if (ctx) + assuan_release (ctx); + return -1; + } + + if (!opt.quiet && !silent) + log_info ("gpg-agent running and available\n"); + + assuan_release (ctx); + return 0; +} diff --git a/agent/learncard.c b/agent/learncard.c new file mode 100644 index 0000000..57bce7a --- /dev/null +++ b/agent/learncard.c @@ -0,0 +1,446 @@ +/* learncard.c - Handle the LEARN command + * Copyright (C) 2002, 2003, 2004, 2009 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include + +/* Structures used by the callback mechanism to convey information + pertaining to key pairs. */ +struct keypair_info_s +{ + struct keypair_info_s *next; + int no_cert; + char *id; /* points into grip */ + char hexgrip[1]; /* The keygrip (i.e. a hash over the public key + parameters) formatted as a hex string. + Allocated somewhat large to also act as + memeory for the above ID field. */ +}; +typedef struct keypair_info_s *KEYPAIR_INFO; + +struct kpinfo_cb_parm_s +{ + ctrl_t ctrl; + int error; + KEYPAIR_INFO info; +}; + + +/* Structures used by the callback mechanism to convey information + pertaining to certificates. */ +struct certinfo_s { + struct certinfo_s *next; + int type; + int done; + char id[1]; +}; +typedef struct certinfo_s *CERTINFO; + +struct certinfo_cb_parm_s +{ + ctrl_t ctrl; + int error; + CERTINFO info; +}; + + +/* Structures used by the callback mechanism to convey assuan status + lines. */ +struct sinfo_s { + struct sinfo_s *next; + char *data; /* Points into keyword. */ + char keyword[1]; +}; +typedef struct sinfo_s *SINFO; + +struct sinfo_cb_parm_s { + int error; + SINFO info; +}; + + +/* Destructor for key information objects. */ +static void +release_keypair_info (KEYPAIR_INFO info) +{ + while (info) + { + KEYPAIR_INFO tmp = info->next; + xfree (info); + info = tmp; + } +} + +/* Destructor for certificate information objects. */ +static void +release_certinfo (CERTINFO info) +{ + while (info) + { + CERTINFO tmp = info->next; + xfree (info); + info = tmp; + } +} + +/* Destructor for status information objects. */ +static void +release_sinfo (SINFO info) +{ + while (info) + { + SINFO tmp = info->next; + xfree (info); + info = tmp; + } +} + + + +/* This callback is used by agent_card_learn and passed the content of + all KEYPAIRINFO lines. It merely stores this data away */ +static void +kpinfo_cb (void *opaque, const char *line) +{ + struct kpinfo_cb_parm_s *parm = opaque; + KEYPAIR_INFO item; + char *p; + + if (parm->error) + return; /* no need to gather data after an error occurred */ + + if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS", + "learncard", "k", "0", "0", NULL))) + return; + + item = xtrycalloc (1, sizeof *item + strlen (line)); + if (!item) + { + parm->error = out_of_core (); + return; + } + strcpy (item->hexgrip, line); + for (p = item->hexgrip; hexdigitp (p); p++) + ; + if (p == item->hexgrip && *p == 'X' && spacep (p+1)) + { + item->no_cert = 1; + p++; + } + else if ((p - item->hexgrip) != 40 || !spacep (p)) + { /* not a 20 byte hex keygrip or not followed by a space */ + parm->error = gpg_error (GPG_ERR_INV_RESPONSE); + xfree (item); + return; + } + *p++ = 0; + while (spacep (p)) + p++; + item->id = p; + while (*p && !spacep (p)) + p++; + if (p == item->id) + { /* invalid ID string */ + parm->error = gpg_error (GPG_ERR_INV_RESPONSE); + xfree (item); + return; + } + *p = 0; /* ignore trailing stuff */ + + /* store it */ + item->next = parm->info; + parm->info = item; +} + + +/* This callback is used by agent_card_learn and passed the content of + all CERTINFO lines. It merely stores this data away */ +static void +certinfo_cb (void *opaque, const char *line) +{ + struct certinfo_cb_parm_s *parm = opaque; + CERTINFO item; + int type; + char *p, *pend; + + if (parm->error) + return; /* no need to gather data after an error occurred */ + + if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS", + "learncard", "c", "0", "0", NULL))) + return; + + type = strtol (line, &p, 10); + while (spacep (p)) + p++; + for (pend = p; *pend && !spacep (pend); pend++) + ; + if (p == pend || !*p) + { + parm->error = gpg_error (GPG_ERR_INV_RESPONSE); + return; + } + *pend = 0; /* ignore trailing stuff */ + + item = xtrycalloc (1, sizeof *item + strlen (p)); + if (!item) + { + parm->error = out_of_core (); + return; + } + item->type = type; + strcpy (item->id, p); + /* store it */ + item->next = parm->info; + parm->info = item; +} + + +/* This callback is used by agent_card_learn and passed the content of + all SINFO lines. It merely stores this data away */ +static void +sinfo_cb (void *opaque, const char *keyword, size_t keywordlen, + const char *data) +{ + struct sinfo_cb_parm_s *sparm = opaque; + SINFO item; + + if (sparm->error) + return; /* no need to gather data after an error occurred */ + + item = xtrycalloc (1, sizeof *item + keywordlen + 1 + strlen (data)); + if (!item) + { + sparm->error = out_of_core (); + return; + } + memcpy (item->keyword, keyword, keywordlen); + item->data = item->keyword + keywordlen; + *item->data = 0; + item->data++; + strcpy (item->data, data); + /* store it */ + item->next = sparm->info; + sparm->info = item; +} + + + +static int +send_cert_back (ctrl_t ctrl, const char *id, void *assuan_context) +{ + int rc; + char *derbuf; + size_t derbuflen; + + rc = agent_card_readcert (ctrl, id, &derbuf, &derbuflen); + if (rc) + { + const char *action; + + switch (gpg_err_code (rc)) + { + case GPG_ERR_INV_ID: + case GPG_ERR_NOT_FOUND: + action = " - ignored"; + break; + default: + action = ""; + break; + } + if (opt.verbose || !*action) + log_info ("error reading certificate '%s': %s%s\n", + id? id:"?", gpg_strerror (rc), action); + + return *action? 0 : rc; + } + + rc = assuan_send_data (assuan_context, derbuf, derbuflen); + xfree (derbuf); + if (!rc) + rc = assuan_send_data (assuan_context, NULL, 0); + if (!rc) + rc = assuan_write_line (assuan_context, "END"); + if (rc) + { + log_error ("sending certificate failed: %s\n", + gpg_strerror (rc)); + return rc; + } + return 0; +} + +/* Perform the learn operation. If ASSUAN_CONTEXT is not NULL and + SEND is true all new certificates are send back via Assuan. */ +int +agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force) +{ + int rc; + + struct kpinfo_cb_parm_s parm; + struct certinfo_cb_parm_s cparm; + struct sinfo_cb_parm_s sparm; + char *serialno = NULL; + KEYPAIR_INFO item; + SINFO sitem; + unsigned char grip[20]; + char *p; + int i; + static int certtype_list[] = { + 111, /* Root CA */ + 101, /* trusted */ + 102, /* useful */ + 100, /* regular */ + /* We don't include 110 here because gpgsm can't handle that + special root CA format. */ + -1 /* end of list */ + }; + + + memset (&parm, 0, sizeof parm); + memset (&cparm, 0, sizeof cparm); + memset (&sparm, 0, sizeof sparm); + parm.ctrl = ctrl; + cparm.ctrl = ctrl; + + /* Check whether a card is present and get the serial number */ + rc = agent_card_serialno (ctrl, &serialno); + if (rc) + goto leave; + + /* Now gather all the available info. */ + rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm, + sinfo_cb, &sparm); + if (!rc && (parm.error || cparm.error || sparm.error)) + rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error; + if (rc) + { + log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + log_info ("card has S/N: %s\n", serialno); + + /* Pass on all the collected status information. */ + if (assuan_context) + { + for (sitem = sparm.info; sitem; sitem = sitem->next) + { + assuan_write_status (assuan_context, sitem->keyword, sitem->data); + } + } + + /* Write out the certificates in a standard order. */ + for (i=0; certtype_list[i] != -1; i++) + { + CERTINFO citem; + for (citem = cparm.info; citem; citem = citem->next) + { + if (certtype_list[i] != citem->type) + continue; + + if (opt.verbose) + log_info (" id: %s (type=%d)\n", + citem->id, citem->type); + + if (assuan_context && send) + { + rc = send_cert_back (ctrl, citem->id, assuan_context); + if (rc) + goto leave; + citem->done = 1; + } + } + } + + for (item = parm.info; item; item = item->next) + { + unsigned char *pubkey; + + if (opt.verbose) + log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip); + + if (item->no_cert) + continue; /* No public key yet available. */ + + if (assuan_context) + { + agent_write_status (ctrl, "KEYPAIRINFO", + item->hexgrip, item->id, NULL); + } + + for (p=item->hexgrip, i=0; i < 20; p += 2, i++) + grip[i] = xtoi_2 (p); + + if (!force && !agent_key_available (grip)) + continue; /* The key is already available. */ + + /* Unknown key - store it. */ + rc = agent_card_readkey (ctrl, item->id, &pubkey); + if (rc) + { + log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force); + xfree (pubkey); + if (rc) + goto leave; + + if (opt.verbose) + log_info (" id: %s - shadow key created\n", item->id); + + if (assuan_context && send) + { + CERTINFO citem; + + /* only send the certificate if we have not done so before */ + for (citem = cparm.info; citem; citem = citem->next) + { + if (!strcmp (citem->id, item->id)) + break; + } + if (!citem) + { + rc = send_cert_back (ctrl, item->id, assuan_context); + if (rc) + goto leave; + } + } + } + + + leave: + xfree (serialno); + release_keypair_info (parm.info); + release_certinfo (cparm.info); + release_sinfo (sparm.info); + return rc; +} diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c new file mode 100644 index 0000000..3d0f5aa --- /dev/null +++ b/agent/pkdecrypt.c @@ -0,0 +1,146 @@ +/* pkdecrypt.c - public key decryption (well, acually using a secret key) + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" + + +/* DECRYPT the stuff in ciphertext which is expected to be a S-Exp. + Try to get the key from CTRL and write the decoded stuff back to + OUTFP. The padding information is stored at R_PADDING with -1 + for not known. */ +int +agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, + const unsigned char *ciphertext, size_t ciphertextlen, + membuf_t *outbuf, int *r_padding) +{ + gcry_sexp_t s_skey = NULL, s_cipher = NULL, s_plain = NULL; + unsigned char *shadow_info = NULL; + int rc; + char *buf = NULL; + size_t len; + + *r_padding = -1; + + if (!ctrl->have_keygrip) + { + log_error ("speculative decryption not yet supported\n"); + rc = gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + + rc = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen); + if (rc) + { + log_error ("failed to convert ciphertext: %s\n", gpg_strerror (rc)); + rc = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + if (DBG_CRYPTO) + { + log_printhex ("keygrip:", ctrl->keygrip, 20); + log_printhex ("cipher: ", ciphertext, ciphertextlen); + } + rc = agent_key_from_file (ctrl, NULL, desc_text, + ctrl->keygrip, &shadow_info, + CACHE_MODE_NORMAL, NULL, &s_skey, NULL); + if (rc) + { + if (gpg_err_code (rc) != GPG_ERR_NO_SECKEY) + log_error ("failed to read the secret key\n"); + goto leave; + } + + if (shadow_info) + { /* divert operation to the smartcard */ + + if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL)) + { + rc = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + + rc = divert_pkdecrypt (ctrl, ciphertext, shadow_info, + &buf, &len, r_padding); + if (rc) + { + log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + put_membuf_printf (outbuf, "(5:value%u:", (unsigned int)len); + put_membuf (outbuf, buf, len); + put_membuf (outbuf, ")", 2); + } + else + { /* No smartcard, but a private key */ +/* if (DBG_CRYPTO ) */ +/* { */ +/* log_debug ("skey: "); */ +/* gcry_sexp_dump (s_skey); */ +/* } */ + + rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey); + if (rc) + { + log_error ("decryption failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (DBG_CRYPTO) + { + log_debug ("plain: "); + gcry_sexp_dump (s_plain); + } + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + if (*buf == '(') + put_membuf (outbuf, buf, len); + else + { + /* Old style libgcrypt: This is only an S-expression + part. Turn it into a complete S-expression. */ + put_membuf (outbuf, "(5:value", 8); + put_membuf (outbuf, buf, len); + put_membuf (outbuf, ")", 2); + } + } + + + leave: + gcry_sexp_release (s_skey); + gcry_sexp_release (s_plain); + gcry_sexp_release (s_cipher); + xfree (buf); + xfree (shadow_info); + return rc; +} diff --git a/agent/pksign.c b/agent/pksign.c new file mode 100644 index 0000000..b347608 --- /dev/null +++ b/agent/pksign.c @@ -0,0 +1,557 @@ +/* pksign.c - public key signing (well, actually using a secret key) + * Copyright (C) 2001-2004, 2010 Free Software Foundation, Inc. + * Copyright (C) 2001-2004, 2010, 2013 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include "i18n.h" + + +static int +do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash, + int raw_value) +{ + gcry_sexp_t hash; + int rc; + + if (!raw_value) + { + const char *s; + char tmp[16+1]; + int i; + + s = gcry_md_algo_name (algo); + if (s && strlen (s) < 16) + { + for (i=0; i < strlen (s); i++) + tmp[i] = tolower (s[i]); + tmp[i] = '\0'; + } + + rc = gcry_sexp_build (&hash, NULL, + "(data (flags pkcs1) (hash %s %b))", + tmp, (int)mdlen, md); + } + else + { + gcry_mpi_t mpi; + + rc = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, md, mdlen, NULL); + if (!rc) + { + rc = gcry_sexp_build (&hash, NULL, + "(data (flags raw) (value %m))", + mpi); + gcry_mpi_release (mpi); + } + else + hash = NULL; + + } + + *r_hash = hash; + return rc; +} + + +/* Return the number of bits of the Q parameter from the DSA key + KEY. */ +static unsigned int +get_dsa_qbits (gcry_sexp_t key) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t q; + unsigned int nbits; + + l1 = gcry_sexp_find_token (key, "private-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (key, "protected-private-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (key, "shadowed-private-key", 0); + if (!l1) + l1 = gcry_sexp_find_token (key, "public-key", 0); + if (!l1) + return 0; /* Does not contain a key object. */ + l2 = gcry_sexp_cadr (l1); + gcry_sexp_release (l1); + l1 = gcry_sexp_find_token (l2, "q", 1); + gcry_sexp_release (l2); + if (!l1) + return 0; /* Invalid object. */ + q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l1); + if (!q) + return 0; /* Missing value. */ + nbits = gcry_mpi_get_nbits (q); + gcry_mpi_release (q); + + return nbits; +} + + +/* Return an appropriate hash algorithm to be used with RFC-6979 for a + message digest of length MDLEN. Although a fallback of SHA-256 is + used the current implementation in Libgcrypt will reject a hash + algorithm which does not match the length of the message. */ +static const char * +rfc6979_hash_algo_string (size_t mdlen) +{ + switch (mdlen) + { + case 20: return "sha1"; + case 28: return "sha224"; + case 32: return "sha256"; + case 48: return "sha384"; + case 64: return "sha512"; + default: return "sha256"; + } +} + + +/* Encode a message digest for use with the EdDSA algorithm + (i.e. curve Ed25519). */ +static gpg_error_t +do_encode_eddsa (const byte *md, size_t mdlen, gcry_sexp_t *r_hash) +{ + gpg_error_t err; + gcry_sexp_t hash; + + *r_hash = NULL; + err = gcry_sexp_build (&hash, NULL, + "(data(flags eddsa)(hash-algo sha512)(value %b))", + (int)mdlen, md); + if (!err) + *r_hash = hash; + return err; +} + + +/* Encode a message digest for use with an DSA algorithm. */ +static gpg_error_t +do_encode_dsa (const byte *md, size_t mdlen, int pkalgo, gcry_sexp_t pkey, + gcry_sexp_t *r_hash) +{ + gpg_error_t err; + gcry_sexp_t hash; + unsigned int qbits; + + *r_hash = NULL; + + if (pkalgo == GCRY_PK_ECDSA) + qbits = gcry_pk_get_nbits (pkey); + else if (pkalgo == GCRY_PK_DSA) + qbits = get_dsa_qbits (pkey); + else + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + + if (pkalgo == GCRY_PK_DSA && (qbits%8)) + { + /* FIXME: We check the QBITS but print a message about the hash + length. */ + log_error (_("DSA requires the hash length to be a" + " multiple of 8 bits\n")); + return gpg_error (GPG_ERR_INV_LENGTH); + } + + /* Don't allow any Q smaller than 160 bits. We don't want someone + to issue signatures from a key with a 16-bit Q or something like + that, which would look correct but allow trivial forgeries. Yes, + I know this rules out using MD5 with DSA. ;) */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + gcry_pk_algo_name (pkalgo), qbits); + return gpg_error (GPG_ERR_INV_LENGTH); + } + + /* ECDSA 521 is special has it is larger than the largest hash + we have (SHA-512). Thus we chnage the size for further + processing to 512. */ + if (pkalgo == GCRY_PK_ECDSA && qbits > 512) + qbits = 512; + + /* Check if we're too short. Too long is safe as we'll + automatically left-truncate. */ + if (mdlen < qbits/8) + { + log_error (_("a %zu bit hash is not valid for a %u bit %s key\n"), + mdlen*8, + gcry_pk_get_nbits (pkey), + gcry_pk_algo_name (pkalgo)); + return gpg_error (GPG_ERR_INV_LENGTH); + } + + /* Truncate. */ + if (mdlen > qbits/8) + mdlen = qbits/8; + + /* Create the S-expression. */ + err = gcry_sexp_build (&hash, NULL, + "(data (flags rfc6979) (hash %s %b))", + rfc6979_hash_algo_string (mdlen), + (int)mdlen, md); + if (!err) + *r_hash = hash; + return err; +} + + +/* Special version of do_encode_md to take care of pkcs#1 padding. + For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does + not know about this special scheme. Fixme: We should have a + pkcs1-only-padding flag for Libgcrypt. */ +static int +do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits, + gcry_sexp_t *r_hash) +{ + int rc; + gcry_sexp_t hash; + unsigned char *frame; + size_t i, n, nframe; + + nframe = (nbits+7) / 8; + if ( !mdlen || mdlen + 8 + 4 > nframe ) + { + /* Can't encode this hash into a frame of size NFRAME. */ + return gpg_error (GPG_ERR_TOO_SHORT); + } + + frame = xtrymalloc (nframe); + if (!frame) + return gpg_error_from_syserror (); + + /* Assemble the pkcs#1 block type 1. */ + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* Block type. */ + i = nframe - mdlen - 3 ; + assert (i >= 8); /* At least 8 bytes of padding. */ + memset (frame+n, 0xff, i ); + n += i; + frame[n++] = 0; + memcpy (frame+n, md, mdlen ); + n += mdlen; + assert (n == nframe); + + /* Create the S-expression. */ + rc = gcry_sexp_build (&hash, NULL, + "(data (flags raw) (value %b))", + (int)nframe, frame); + xfree (frame); + + *r_hash = hash; + return rc; +} + + + +/* SIGN whatever information we have accumulated in CTRL and return + the signature S-expression. LOOKUP is an optional function to + provide a way for lower layers to ask for the caching TTL. If a + CACHE_NONCE is given that cache item is first tried to get a + passphrase. If OVERRIDEDATA is not NULL, OVERRIDEDATALEN bytes + from this buffer are used instead of the data in CTRL. The + override feature is required to allow the use of Ed25519 with ssh + because Ed25519 does the hashing itself. */ +int +agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, + const char *desc_text, + gcry_sexp_t *signature_sexp, + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + const void *overridedata, size_t overridedatalen) +{ + gcry_sexp_t s_skey = NULL, s_sig = NULL; + gcry_sexp_t s_hash = NULL; + gcry_sexp_t s_pkey = NULL; + unsigned char *shadow_info = NULL; + unsigned int rc = 0; /* FIXME: gpg-error? */ + const unsigned char *data; + int datalen; + int check_signature = 0; + + if (overridedata) + { + data = overridedata; + datalen = overridedatalen; + } + else + { + data = ctrl->digest.value; + datalen = ctrl->digest.valuelen; + } + + if (!ctrl->have_keygrip) + return gpg_error (GPG_ERR_NO_SECKEY); + + rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip, + &shadow_info, cache_mode, lookup_ttl, + &s_skey, NULL); + if (rc) + { + if (gpg_err_code (rc) != GPG_ERR_NO_SECKEY) + log_error ("failed to read the secret key\n"); + goto leave; + } + + if (shadow_info) + { + /* Divert operation to the smartcard */ + size_t len; + unsigned char *buf = NULL; + int key_type; + int is_RSA = 0; + int is_ECDSA = 0; + int is_EdDSA = 0; + + rc = agent_public_key_from_file (ctrl, ctrl->keygrip, &s_pkey); + if (rc) + { + log_error ("failed to read the public key\n"); + goto leave; + } + + if (agent_is_eddsa_key (s_skey)) + is_EdDSA = 1; + else + { + key_type = agent_is_dsa_key (s_skey); + if (key_type == 0) + is_RSA = 1; + else if (key_type == GCRY_PK_ECDSA) + is_ECDSA = 1; + } + + rc = divert_pksign (ctrl, + data, datalen, + ctrl->digest.algo, + shadow_info, &buf, &len); + if (rc) + { + log_error ("smartcard signing failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (is_RSA) + { + check_signature = 1; + if (*buf & 0x80) + { + len++; + buf = xtryrealloc (buf, len); + if (!buf) + goto leave; + + memmove (buf + 1, buf, len - 1); + *buf = 0; + } + + rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%b)))", + (int)len, buf); + } + else if (is_EdDSA) + { + rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(eddsa(r%b)(s%b)))", + (int)len/2, buf, (int)len/2, buf + len/2); + } + else if (is_ECDSA) + { + unsigned char *r_buf_allocated = NULL; + unsigned char *s_buf_allocated = NULL; + unsigned char *r_buf, *s_buf; + int r_buflen, s_buflen; + + r_buflen = s_buflen = len/2; + + if (*buf & 0x80) + { + r_buflen++; + r_buf_allocated = xtrymalloc (r_buflen); + if (!r_buf_allocated) + goto leave; + + r_buf = r_buf_allocated; + memcpy (r_buf + 1, buf, len/2); + *r_buf = 0; + } + else + r_buf = buf; + + if (*(buf + len/2) & 0x80) + { + s_buflen++; + s_buf_allocated = xtrymalloc (s_buflen); + if (!s_buf_allocated) + { + xfree (r_buf_allocated); + goto leave; + } + + s_buf = s_buf_allocated; + memcpy (s_buf + 1, buf + len/2, len/2); + *s_buf = 0; + } + else + s_buf = buf + len/2; + + rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))", + r_buflen, r_buf, + s_buflen, s_buf); + xfree (r_buf_allocated); + xfree (s_buf_allocated); + } + else + rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + + xfree (buf); + if (rc) + { + log_error ("failed to convert sigbuf returned by divert_pksign " + "into S-Exp: %s", gpg_strerror (rc)); + goto leave; + } + } + else + { + /* No smartcard, but a private key */ + int dsaalgo = 0; + + /* Put the hash into a sexp */ + if (agent_is_eddsa_key (s_skey)) + rc = do_encode_eddsa (data, datalen, + &s_hash); + else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) + rc = do_encode_raw_pkcs1 (data, datalen, + gcry_pk_get_nbits (s_skey), + &s_hash); + else if ( (dsaalgo = agent_is_dsa_key (s_skey)) ) + rc = do_encode_dsa (data, datalen, + dsaalgo, s_skey, + &s_hash); + else + rc = do_encode_md (data, datalen, + ctrl->digest.algo, + &s_hash, + ctrl->digest.raw_value); + if (rc) + goto leave; + + if (dsaalgo == 0 && GCRYPT_VERSION_NUMBER < 0x010700) + /* It's RSA and Libgcrypt < 1.7 */ + check_signature = 1; + + if (DBG_CRYPTO) + { + gcry_log_debugsxp ("skey", s_skey); + gcry_log_debugsxp ("hash", s_hash); + } + + /* sign */ + rc = gcry_pk_sign (&s_sig, s_hash, s_skey); + if (rc) + { + log_error ("signing failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (DBG_CRYPTO) + gcry_log_debugsxp ("rslt", s_sig); + } + + /* Check that the signature verification worked and nothing is + * fooling us e.g. by a bug in the signature create code or by + * deliberately introduced faults. Because Libgcrypt 1.7 does this + * for RSA internally there is no need to do it here again. */ + if (check_signature) + { + gcry_sexp_t sexp_key = s_pkey? s_pkey: s_skey; + + if (s_hash == NULL) + { + if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) + rc = do_encode_raw_pkcs1 (data, datalen, + gcry_pk_get_nbits (sexp_key), &s_hash); + else + rc = do_encode_md (data, datalen, ctrl->digest.algo, &s_hash, + ctrl->digest.raw_value); + } + + if (! rc) + rc = gcry_pk_verify (s_sig, s_hash, sexp_key); + + if (rc) + { + log_error (_("checking created signature failed: %s\n"), + gpg_strerror (rc)); + gcry_sexp_release (s_sig); + s_sig = NULL; + } + } + + leave: + + *signature_sexp = s_sig; + + gcry_sexp_release (s_pkey); + gcry_sexp_release (s_skey); + gcry_sexp_release (s_hash); + xfree (shadow_info); + + return rc; +} + +/* SIGN whatever information we have accumulated in CTRL and write it + back to OUTFP. If a CACHE_NONCE is given that cache item is first + tried to get a passphrase. */ +int +agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, + membuf_t *outbuf, cache_mode_t cache_mode) +{ + gcry_sexp_t s_sig = NULL; + char *buf = NULL; + size_t len = 0; + int rc = 0; + + rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL, + NULL, 0); + if (rc) + goto leave; + + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + + put_membuf (outbuf, buf, len); + + leave: + gcry_sexp_release (s_sig); + xfree (buf); + + return rc; +} diff --git a/agent/preset-passphrase.c b/agent/preset-passphrase.c new file mode 100644 index 0000000..ae6f0ce --- /dev/null +++ b/agent/preset-passphrase.c @@ -0,0 +1,266 @@ +/* preset-passphrase.c - A tool to preset a passphrase. + * 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif +#ifdef HAVE_LANGINFO_CODESET +#include +#endif +#ifdef HAVE_DOSISH_SYSTEM +#include /* for setmode() */ +#endif +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include /* To initialize the sockets. fixme */ +#endif + +#include "agent.h" +#include "simple-pwquery.h" +#include "i18n.h" +#include "sysutils.h" +#include "../common/init.h" + + +enum cmd_and_opt_values +{ aNull = 0, + oVerbose = 'v', + oPassphrase = 'P', + + oPreset = 'c', + oForget = 'f', + + oNoVerbose = 500, + + oHomedir, + +aTest }; + + +static const char *opt_passphrase; + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oVerbose, "verbose", 0, "verbose" }, + { oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" }, + { oPreset, "preset", 256, "preset passphrase"}, + { oForget, "forget", 256, "forget passphrase"}, + + { oHomedir, "homedir", 2, "@" }, + {0} +}; + + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "gpg-preset-passphrase (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + + case 1: + case 40: + p = _("Usage: gpg-preset-passphrase [options] KEYGRIP (-h for help)\n"); + break; + case 41: + p = _("Syntax: gpg-preset-passphrase [options] KEYGRIP\n" + "Password cache maintenance\n"); + break; + + default: p = NULL; + } + return p; +} + + + + +static void +preset_passphrase (const char *keygrip) +{ + int rc; + char *line; + /* FIXME: Use secure memory. */ + char passphrase[500]; + char *passphrase_esc; + + if (!opt_passphrase) + { + rc = read (0, passphrase, sizeof (passphrase) - 1); + if (rc < 0) + { + log_error ("reading passphrase failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + return; + } + passphrase[rc] = '\0'; + line = strchr (passphrase, '\n'); + if (line) + { + if (line > passphrase && line[-1] == '\r') + line--; + *line = '\0'; + } + + /* FIXME: How to handle empty passwords? */ + } + + { + const char *s = opt_passphrase ? opt_passphrase : passphrase; + passphrase_esc = bin2hex (s, strlen (s), NULL); + } + if (!passphrase_esc) + { + log_error ("can not escape string: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + return; + } + + rc = asprintf (&line, "PRESET_PASSPHRASE %s -1 %s\n", keygrip, + passphrase_esc); + wipememory (passphrase_esc, strlen (passphrase_esc)); + xfree (passphrase_esc); + + if (rc < 0) + { + log_error ("caching passphrase failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + return; + } + if (!opt_passphrase) + wipememory (passphrase, sizeof (passphrase)); + + rc = simple_query (line); + if (rc) + { + log_error ("caching passphrase failed: %s\n", gpg_strerror (rc)); + return; + } + + wipememory (line, strlen (line)); + xfree (line); +} + + +static void +forget_passphrase (const char *keygrip) +{ + int rc; + char *line; + + rc = asprintf (&line, "CLEAR_PASSPHRASE %s\n", keygrip); + if (rc < 0) + rc = gpg_error_from_syserror (); + else + rc = simple_query (line); + if (rc) + { + log_error ("clearing passphrase failed: %s\n", gpg_strerror (rc)); + return; + } + + xfree (line); +} + + +int +main (int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + int cmd = 0; + const char *keygrip = NULL; + + early_system_init (); + set_strusage (my_strusage); + log_set_prefix ("gpg-preset-passphrase", GPGRT_LOG_WITH_PREFIX); + + /* Make sure that our subsystems are ready. */ + i18n_init (); + init_common_subsystems (&argc, &argv); + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* (do not remove the args) */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; + + case oPreset: cmd = oPreset; break; + case oForget: cmd = oForget; break; + case oPassphrase: opt_passphrase = pargs.r.ret_str; break; + + default : pargs.err = 2; break; + } + } + if (log_get_errorcount(0)) + exit(2); + + if (argc == 1) + keygrip = *argv; + else + usage (1); + + /* Tell simple-pwquery about the the standard socket name. */ + { + char *tmp = make_filename (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + simple_pw_set_socket (tmp); + xfree (tmp); + } + + if (cmd == oPreset) + preset_passphrase (keygrip); + else if (cmd == oForget) + forget_passphrase (keygrip); + else + log_error ("one of the options --preset or --forget must be given\n"); + + agent_exit (0); + return 8; /*NOTREACHED*/ +} + + +void +agent_exit (int rc) +{ + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} diff --git a/agent/protect-tool.c b/agent/protect-tool.c new file mode 100644 index 0000000..2312744 --- /dev/null +++ b/agent/protect-tool.c @@ -0,0 +1,819 @@ +/* protect-tool.c - A tool to test the secret key protection + * Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif +#ifdef HAVE_LANGINFO_CODESET +#include +#endif +#ifdef HAVE_DOSISH_SYSTEM +#include /* for setmode() */ +#endif + +#include "agent.h" +#include "i18n.h" +#include "get-passphrase.h" +#include "sysutils.h" +#include "../common/init.h" + + +enum cmd_and_opt_values +{ + aNull = 0, + oVerbose = 'v', + oArmor = 'a', + oPassphrase = 'P', + + oProtect = 'p', + oUnprotect = 'u', + + oNoVerbose = 500, + oShadow, + oShowShadowInfo, + oShowKeygrip, + oS2Kcalibration, + oCanonical, + + oStore, + oForce, + oHaveCert, + oNoFailOnExist, + oHomedir, + oPrompt, + oStatusMsg, + oDebugUseOCB, + + oAgentProgram +}; + + +struct rsa_secret_key_s +{ + gcry_mpi_t n; /* public modulus */ + gcry_mpi_t e; /* public exponent */ + gcry_mpi_t d; /* exponent */ + gcry_mpi_t p; /* prime p. */ + gcry_mpi_t q; /* prime q. */ + gcry_mpi_t u; /* inverse of p mod q. */ +}; + + +static int opt_armor; +static int opt_canonical; +static int opt_store; +static int opt_force; +static int opt_no_fail_on_exist; +static int opt_have_cert; +static const char *opt_passphrase; +static char *opt_prompt; +static int opt_status_msg; +static const char *opt_agent_program; +static int opt_debug_use_ocb; + +static char *get_passphrase (int promptno); +static void release_passphrase (char *pw); + + +static ARGPARSE_OPTS opts[] = { + ARGPARSE_group (300, N_("@Commands:\n ")), + + ARGPARSE_c (oProtect, "protect", "protect a private key"), + ARGPARSE_c (oUnprotect, "unprotect", "unprotect a private key"), + ARGPARSE_c (oShadow, "shadow", "create a shadow entry for a public key"), + ARGPARSE_c (oShowShadowInfo, "show-shadow-info", "return the shadow info"), + ARGPARSE_c (oShowKeygrip, "show-keygrip", "show the \"keygrip\""), + ARGPARSE_c (oS2Kcalibration, "s2k-calibration", "@"), + + ARGPARSE_group (301, N_("@\nOptions:\n ")), + + ARGPARSE_s_n (oVerbose, "verbose", "verbose"), + ARGPARSE_s_n (oArmor, "armor", "write output in advanced format"), + ARGPARSE_s_n (oCanonical, "canonical", "write output in canonical format"), + + ARGPARSE_s_s (oPassphrase, "passphrase", "|STRING|use passphrase STRING"), + ARGPARSE_s_n (oHaveCert, "have-cert", + "certificate to export provided on STDIN"), + ARGPARSE_s_n (oStore, "store", + "store the created key in the appropriate place"), + ARGPARSE_s_n (oForce, "force", + "force overwriting"), + ARGPARSE_s_n (oNoFailOnExist, "no-fail-on-exist", "@"), + ARGPARSE_s_s (oHomedir, "homedir", "@"), + ARGPARSE_s_s (oPrompt, "prompt", + "|ESCSTRING|use ESCSTRING as prompt in pinentry"), + ARGPARSE_s_n (oStatusMsg, "enable-status-msg", "@"), + + ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), + + ARGPARSE_s_n (oDebugUseOCB, "debug-use-ocb", "@"), /* For hacking only. */ + + ARGPARSE_end () +}; + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "gpg-protect-tool (" GNUPG_NAME ")"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + + case 1: + case 40: p = _("Usage: gpg-protect-tool [options] (-h for help)\n"); + break; + case 41: p = _("Syntax: gpg-protect-tool [options] [args]\n" + "Secret key maintenance tool\n"); + break; + + default: p = NULL; + } + return p; +} + + +/* static void */ +/* print_mpi (const char *text, gcry_mpi_t a) */ +/* { */ +/* char *buf; */ +/* void *bufaddr = &buf; */ +/* int rc; */ + +/* rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); */ +/* if (rc) */ +/* log_info ("%s: [error printing number: %s]\n", text, gpg_strerror (rc)); */ +/* else */ +/* { */ +/* log_info ("%s: %s\n", text, buf); */ +/* gcry_free (buf); */ +/* } */ +/* } */ + + + +static unsigned char * +make_canonical (const char *fname, const char *buf, size_t buflen) +{ + int rc; + size_t erroff, len; + gcry_sexp_t sexp; + unsigned char *result; + + rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen); + if (rc) + { + log_error ("invalid S-Expression in '%s' (off=%u): %s\n", + fname, (unsigned int)erroff, gpg_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + result = xmalloc (len); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, result, len); + assert (len); + gcry_sexp_release (sexp); + return result; +} + +static char * +make_advanced (const unsigned char *buf, size_t buflen) +{ + int rc; + size_t erroff, len; + gcry_sexp_t sexp; + char *result; + + rc = gcry_sexp_sscan (&sexp, &erroff, (const char*)buf, buflen); + if (rc) + { + log_error ("invalid canonical S-Expression (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + assert (len); + result = xmalloc (len); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len); + assert (len); + gcry_sexp_release (sexp); + return result; +} + + +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + char *buf; + size_t buflen; + + if (!strcmp (fname, "-")) + { + size_t nread, bufsize = 0; + + fp = stdin; +#ifdef HAVE_DOSISH_SYSTEM + setmode ( fileno(fp) , O_BINARY ); +#endif + buf = NULL; + buflen = 0; +#define NCHUNK 8192 + do + { + bufsize += NCHUNK; + if (!buf) + buf = xmalloc (bufsize); + else + buf = xrealloc (buf, bufsize); + + nread = fread (buf+buflen, 1, NCHUNK, fp); + if (nread < NCHUNK && ferror (fp)) + { + log_error ("error reading '[stdin]': %s\n", strerror (errno)); + xfree (buf); + return NULL; + } + buflen += nread; + } + while (nread == NCHUNK); +#undef NCHUNK + + } + else + { + struct stat st; + + fp = fopen (fname, "rb"); + if (!fp) + { + log_error ("can't open '%s': %s\n", fname, strerror (errno)); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + log_error ("can't stat '%s': %s\n", fname, strerror (errno)); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading '%s': %s\n", fname, strerror (errno)); + fclose (fp); + xfree (buf); + return NULL; + } + fclose (fp); + } + + *r_length = buflen; + return buf; +} + + +static unsigned char * +read_key (const char *fname) +{ + char *buf; + size_t buflen; + unsigned char *key; + + buf = read_file (fname, &buflen); + if (!buf) + return NULL; + key = make_canonical (fname, buf, buflen); + xfree (buf); + return key; +} + + + +static void +read_and_protect (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + char *pw; + + key = read_key (fname); + if (!key) + return; + + pw = get_passphrase (1); + rc = agent_protect (key, pw, &result, &resultlen, 0, + opt_debug_use_ocb? 1 : -1); + release_passphrase (pw); + xfree (key); + if (rc) + { + log_error ("protecting the key failed: %s\n", gpg_strerror (rc)); + return; + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = (unsigned char*)p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + + +static void +read_and_unprotect (ctrl_t ctrl, const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + char *pw; + gnupg_isotime_t protected_at; + + key = read_key (fname); + if (!key) + return; + + rc = agent_unprotect (ctrl, key, (pw=get_passphrase (1)), + protected_at, &result, &resultlen); + release_passphrase (pw); + xfree (key); + if (rc) + { + if (opt_status_msg) + log_info ("[PROTECT-TOOL:] bad-passphrase\n"); + log_error ("unprotecting the key failed: %s\n", gpg_strerror (rc)); + return; + } + if (opt.verbose) + { + if (*protected_at) + log_info ("key protection done at %.4s-%.2s-%.2s %.2s:%.2s:%s\n", + protected_at, protected_at+4, protected_at+6, + protected_at+9, protected_at+11, protected_at+13); + else + log_info ("key protection done at [unknown]\n"); + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = (unsigned char*)p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + + + +static void +read_and_shadow (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + unsigned char dummy_info[] = "(8:313233342:43)"; + + key = read_key (fname); + if (!key) + return; + + rc = agent_shadow_key (key, dummy_info, &result); + xfree (key); + if (rc) + { + log_error ("shadowing the key failed: %s\n", gpg_strerror (rc)); + return; + } + resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL); + assert (resultlen); + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = (unsigned char*)p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + +static void +show_shadow_info (const char *fname) +{ + int rc; + unsigned char *key; + const unsigned char *info; + size_t infolen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_get_shadow_info (key, &info); + xfree (key); + if (rc) + { + log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); + return; + } + infolen = gcry_sexp_canon_len (info, 0, NULL,NULL); + assert (infolen); + + if (opt_armor) + { + char *p = make_advanced (info, infolen); + if (!p) + return; + fwrite (p, strlen (p), 1, stdout); + xfree (p); + } + else + fwrite (info, infolen, 1, stdout); +} + + +static void +show_file (const char *fname) +{ + unsigned char *key; + size_t keylen; + char *p; + + key = read_key (fname); + if (!key) + return; + + keylen = gcry_sexp_canon_len (key, 0, NULL,NULL); + assert (keylen); + + if (opt_canonical) + { + fwrite (key, keylen, 1, stdout); + } + else + { + p = make_advanced (key, keylen); + if (p) + { + fwrite (p, strlen (p), 1, stdout); + xfree (p); + } + } + xfree (key); +} + +static void +show_keygrip (const char *fname) +{ + unsigned char *key; + gcry_sexp_t private; + unsigned char grip[20]; + int i; + + key = read_key (fname); + if (!key) + return; + + if (gcry_sexp_new (&private, key, 0, 0)) + { + log_error ("gcry_sexp_new failed\n"); + return; + } + xfree (key); + + if (!gcry_pk_get_keygrip (private, grip)) + { + log_error ("can't calculate keygrip\n"); + return; + } + gcry_sexp_release (private); + + for (i=0; i < 20; i++) + printf ("%02X", grip[i]); + putchar ('\n'); +} + + + + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int cmd = 0; + const char *fname; + ctrl_t ctrl; + + early_system_init (); + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + log_set_prefix ("gpg-protect-tool", GPGRT_LOG_WITH_PREFIX); + + /* Make sure that our subsystems are ready. */ + i18n_init (); + init_common_subsystems (&argc, &argv); + + setup_libgcrypt_logging (); + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* (do not remove the args) */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oArmor: opt_armor=1; break; + case oCanonical: opt_canonical=1; break; + case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; + + case oAgentProgram: opt_agent_program = pargs.r.ret_str; break; + + case oProtect: cmd = oProtect; break; + case oUnprotect: cmd = oUnprotect; break; + case oShadow: cmd = oShadow; break; + case oShowShadowInfo: cmd = oShowShadowInfo; break; + case oShowKeygrip: cmd = oShowKeygrip; break; + case oS2Kcalibration: cmd = oS2Kcalibration; break; + + case oPassphrase: opt_passphrase = pargs.r.ret_str; break; + case oStore: opt_store = 1; break; + case oForce: opt_force = 1; break; + case oNoFailOnExist: opt_no_fail_on_exist = 1; break; + case oHaveCert: opt_have_cert = 1; break; + case oPrompt: opt_prompt = pargs.r.ret_str; break; + case oStatusMsg: opt_status_msg = 1; break; + case oDebugUseOCB: opt_debug_use_ocb = 1; break; + + default: pargs.err = ARGPARSE_PRINT_ERROR; break; + } + } + if (log_get_errorcount (0)) + exit (2); + + fname = "-"; + if (argc == 1) + fname = *argv; + else if (argc > 1) + usage (1); + + /* Allocate an CTRL object. An empty object should be sufficient. */ + ctrl = xtrycalloc (1, sizeof *ctrl); + if (!ctrl) + { + log_error ("error allocating connection control data: %s\n", + strerror (errno)); + agent_exit (1); + } + + /* Set the information which can't be taken from envvars. */ + gnupg_prepare_get_passphrase (GPG_ERR_SOURCE_DEFAULT, + opt.verbose, + opt_agent_program, + NULL, NULL, NULL); + + if (opt_prompt) + opt_prompt = percent_plus_unescape (opt_prompt, 0); + + if (cmd == oProtect) + read_and_protect (fname); + else if (cmd == oUnprotect) + read_and_unprotect (ctrl, fname); + else if (cmd == oShadow) + read_and_shadow (fname); + else if (cmd == oShowShadowInfo) + show_shadow_info (fname); + else if (cmd == oShowKeygrip) + show_keygrip (fname); + else if (cmd == oS2Kcalibration) + { + if (!opt.verbose) + opt.verbose++; /* We need to see something. */ + get_standard_s2k_count (); + } + else + show_file (fname); + + xfree (ctrl); + + agent_exit (0); + return 8; /*NOTREACHED*/ +} + +void +agent_exit (int rc) +{ + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} + + +/* Return the passphrase string and ask the agent if it has not been + set from the command line PROMPTNO select the prompt to display: + 0 = default + 1 = taken from the option --prompt + 2 = for unprotecting a pkcs#12 object + 3 = for protecting a new pkcs#12 object + 4 = for protecting an imported pkcs#12 in our system +*/ +static char * +get_passphrase (int promptno) +{ + char *pw; + int err; + const char *desc; + char *orig_codeset; + int repeat = 0; + + if (opt_passphrase) + return xstrdup (opt_passphrase); + + orig_codeset = i18n_switchto_utf8 (); + + if (promptno == 1 && opt_prompt) + { + desc = opt_prompt; + } + else if (promptno == 2) + { + desc = _("Please enter the passphrase to unprotect the " + "PKCS#12 object."); + } + else if (promptno == 3) + { + desc = _("Please enter the passphrase to protect the " + "new PKCS#12 object."); + repeat = 1; + } + else if (promptno == 4) + { + desc = _("Please enter the passphrase to protect the " + "imported object within the GnuPG system."); + repeat = 1; + } + else + desc = _("Please enter the passphrase or the PIN\n" + "needed to complete this operation."); + + i18n_switchback (orig_codeset); + + err = gnupg_get_passphrase (NULL, NULL, _("Passphrase:"), desc, + repeat, repeat, 1, &pw); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_CANCELED + || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) + log_info (_("cancelled\n")); + else + log_error (_("error while asking for the passphrase: %s\n"), + gpg_strerror (err)); + agent_exit (0); + } + assert (pw); + + return pw; +} + + +static void +release_passphrase (char *pw) +{ + if (pw) + { + wipememory (pw, strlen (pw)); + xfree (pw); + } +} + + +/* Stub function. */ +int +agent_key_available (const unsigned char *grip) +{ + (void)grip; + return -1; /* Not available. */ +} + +char * +agent_get_cache (const char *key, cache_mode_t cache_mode) +{ + (void)key; + (void)cache_mode; + return NULL; +} + +gpg_error_t +agent_askpin (ctrl_t ctrl, + const char *desc_text, const char *prompt_text, + const char *initial_errtext, + struct pin_entry_info_s *pininfo, + const char *keyinfo, cache_mode_t cache_mode) +{ + gpg_error_t err; + unsigned char *passphrase; + size_t size; + + (void)ctrl; + (void)desc_text; + (void)prompt_text; + (void)initial_errtext; + (void)keyinfo; + (void)cache_mode; + + *pininfo->pin = 0; /* Reset the PIN. */ + passphrase = get_passphrase (0); + size = strlen (passphrase); + if (size >= pininfo->max_length) + return gpg_error (GPG_ERR_TOO_LARGE); + + memcpy (&pininfo->pin, passphrase, size); + xfree (passphrase); + pininfo->pin[size] = 0; + if (pininfo->check_cb) + { + /* More checks by utilizing the optional callback. */ + pininfo->cb_errtext = NULL; + err = pininfo->check_cb (pininfo); + } + else + err = 0; + return err; +} + +/* Replacement for the function in findkey.c. Here we write the key + * to stdout. */ +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force) +{ + char hexgrip[40+4+1]; + char *p; + + (void)force; + + bin2hex (grip, 20, hexgrip); + strcpy (hexgrip+40, ".key"); + p = make_advanced (buffer, length); + if (p) + { + printf ("# Begin dump of %s\n%s%s# End dump of %s\n", + hexgrip, p, (*p && p[strlen(p)-1] == '\n')? "":"\n", hexgrip); + xfree (p); + } + + return 0; +} diff --git a/agent/protect.c b/agent/protect.c new file mode 100644 index 0000000..e205869 --- /dev/null +++ b/agent/protect.c @@ -0,0 +1,1709 @@ +/* protect.c - Un/Protect a secret key + * Copyright (C) 1998-2003, 2007, 2009, 2011 Free Software Foundation, Inc. + * Copyright (C) 1998-2003, 2007, 2009, 2011, 2013-2015 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else +# include +#endif + +#include "agent.h" + +#include "cvt-openpgp.h" +#include "sexp-parse.h" + + +/* To use the openpgp-s2k3-ocb-aes scheme by default set the value of + * this macro to 1. Note that the caller of agent_protect may + * override this default. */ +#define PROT_DEFAULT_TO_OCB 0 + +/* The protection mode for encryption. The supported modes for + decryption are listed in agent_unprotect(). */ +#define PROT_CIPHER GCRY_CIPHER_AES128 +#define PROT_CIPHER_STRING "aes" +#define PROT_CIPHER_KEYLEN (128/8) + +/* Decode an rfc4880 encoded S2K count. */ +#define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) + + +/* A table containing the information needed to create a protected + private key. */ +static struct { + const char *algo; + const char *parmlist; + int prot_from, prot_to; + int ecc_hack; +} protect_info[] = { + { "rsa", "nedpqu", 2, 5 }, + { "dsa", "pqgyx", 4, 4 }, + { "elg", "pgyx", 3, 3 }, + { "ecdsa","pabgnqd", 6, 6, 1 }, + { "ecdh", "pabgnqd", 6, 6, 1 }, + { "ecc", "pabgnqd", 6, 6, 1 }, + { NULL } +}; + + +/* A helper object for time measurement. */ +struct calibrate_time_s +{ +#ifdef HAVE_W32_SYSTEM + FILETIME creation_time, exit_time, kernel_time, user_time; +#else + clock_t ticks; +#endif +}; + + +static int +hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, unsigned long s2kcount, + unsigned char *key, size_t keylen); + + + + +/* Get the process time and store it in DATA. */ +static void +calibrate_get_time (struct calibrate_time_s *data) +{ +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_W32CE_SYSTEM + GetThreadTimes (GetCurrentThread (), + &data->creation_time, &data->exit_time, + &data->kernel_time, &data->user_time); +# else + GetProcessTimes (GetCurrentProcess (), + &data->creation_time, &data->exit_time, + &data->kernel_time, &data->user_time); +# endif +#else + struct tms tmp; + + times (&tmp); + data->ticks = tmp.tms_utime; +#endif +} + + +static unsigned long +calibrate_elapsed_time (struct calibrate_time_s *starttime) +{ + struct calibrate_time_s stoptime; + + calibrate_get_time (&stoptime); +#ifdef HAVE_W32_SYSTEM + { + unsigned long long t1, t2; + + t1 = (((unsigned long long)starttime->kernel_time.dwHighDateTime << 32) + + starttime->kernel_time.dwLowDateTime); + t1 += (((unsigned long long)starttime->user_time.dwHighDateTime << 32) + + starttime->user_time.dwLowDateTime); + t2 = (((unsigned long long)stoptime.kernel_time.dwHighDateTime << 32) + + stoptime.kernel_time.dwLowDateTime); + t2 += (((unsigned long long)stoptime.user_time.dwHighDateTime << 32) + + stoptime.user_time.dwLowDateTime); + return (unsigned long)((t2 - t1)/10000); + } +#else + return (unsigned long)((((double) (stoptime.ticks - starttime->ticks)) + /CLOCKS_PER_SEC)*10000000); +#endif +} + + +/* Run a test hashing for COUNT and return the time required in + milliseconds. */ +static unsigned long +calibrate_s2k_count_one (unsigned long count) +{ + int rc; + char keybuf[PROT_CIPHER_KEYLEN]; + struct calibrate_time_s starttime; + + calibrate_get_time (&starttime); + rc = hash_passphrase ("123456789abcdef0", GCRY_MD_SHA1, + 3, "saltsalt", count, keybuf, sizeof keybuf); + if (rc) + BUG (); + return calibrate_elapsed_time (&starttime); +} + + +/* Measure the time we need to do the hash operations and deduce an + S2K count which requires about 100ms of time. */ +static unsigned long +calibrate_s2k_count (void) +{ + unsigned long count; + unsigned long ms; + + for (count = 65536; count; count *= 2) + { + ms = calibrate_s2k_count_one (count); + if (opt.verbose > 1) + log_info ("S2K calibration: %lu -> %lums\n", count, ms); + if (ms > 100) + break; + } + + count = (unsigned long)(((double)count / ms) * 100); + count /= 1024; + count *= 1024; + if (count < 65536) + count = 65536; + + if (opt.verbose) + { + ms = calibrate_s2k_count_one (count); + log_info ("S2K calibration: %lu -> %lums\n", count, ms); + } + + return count; +} + + + +/* Return the standard S2K count. */ +unsigned long +get_standard_s2k_count (void) +{ + static unsigned long count; + + if (!count) + count = calibrate_s2k_count (); + + /* Enforce a lower limit. */ + return count < 65536 ? 65536 : count; +} + + +/* Same as get_standard_s2k_count but return the count in the encoding + as described by rfc4880. */ +unsigned char +get_standard_s2k_count_rfc4880 (void) +{ + unsigned long iterations; + unsigned int count; + unsigned char result; + unsigned char c=0; + + iterations = get_standard_s2k_count (); + if (iterations >= 65011712) + return 255; + + /* Need count to be in the range 16-31 */ + for (count=iterations>>6; count>=32; count>>=1) + c++; + + result = (c<<4)|(count-16); + + if (S2K_DECODE_COUNT(result) < iterations) + result++; + + return result; + +} + + + +/* Calculate the MIC for a private key or shared secret S-expression. + SHA1HASH should point to a 20 byte buffer. This function is + suitable for all algorithms. */ +static int +calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash) +{ + const unsigned char *hash_begin, *hash_end; + const unsigned char *s; + size_t n; + int is_shared_secret; + + s = plainkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "private-key")) + is_shared_secret = 0; + else if (smatch (&s, n, "shared-secret")) + is_shared_secret = 1; + else + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + hash_begin = s; + if (!is_shared_secret) + { + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; /* Skip the algorithm name. */ + } + + while (*s == '(') + { + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + if ( *s != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + s++; + } + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + hash_end = s; + + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, + hash_begin, hash_end - hash_begin); + + return 0; +} + + + +/* Encrypt the parameter block starting at PROTBEGIN with length + PROTLEN using the utf8 encoded key PASSPHRASE and return the entire + encrypted block in RESULT or return with an error code. SHA1HASH + is the 20 byte SHA-1 hash required for the integrity code. + + The parameter block is expected to be an incomplete canonical + encoded S-Expression of the form (example in advanced format): + + (d #046129F..[some bytes not shown]..81#) + (p #00e861b..[some bytes not shown]..f1#) + (q #00f7a7c..[some bytes not shown]..61#) + (u #304559a..[some bytes not shown]..9b#) + + the returned block is the S-Expression: + + (protected mode (parms) encrypted_octet_string) + +*/ +static int +do_encryption (const unsigned char *hashbegin, size_t hashlen, + const unsigned char *protbegin, size_t protlen, + const char *passphrase, + const char *timestamp_exp, size_t timestamp_exp_len, + unsigned char **result, size_t *resultlen, + unsigned long s2k_count, int use_ocb) +{ + gcry_cipher_hd_t hd; + const char *modestr; + unsigned char hashvalue[20]; + int blklen, enclen, outlen; + unsigned char *iv = NULL; + unsigned int ivsize; /* Size of the buffer allocated for IV. */ + const unsigned char *s2ksalt; /* Points into IV. */ + int rc; + char *outbuf = NULL; + char *p; + int saltpos, ivpos, encpos; + + s2ksalt = iv; /* Silence compiler warning. */ + + *resultlen = 0; + *result = NULL; + + modestr = (use_ocb? "openpgp-s2k3-ocb-aes" + /* */: "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"); + + rc = gcry_cipher_open (&hd, PROT_CIPHER, + use_ocb? GCRY_CIPHER_MODE_OCB : + GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_SECURE); + if (rc) + return rc; + + /* We need to work on a copy of the data because this makes it + * easier to add the trailer and the padding and more important we + * have to prefix the text with 2 parenthesis. In CBC mode we + * have to allocate enough space for: + * + * (()(4:hash4:sha120:)) + padding + * + * we always append a full block of random bytes as padding but + * encrypt only what is needed for a full blocksize. In OCB mode we + * have to allocate enough space for just: + * + * (()) + */ + blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); + if (use_ocb) + { + /* (( )) */ + outlen = 2 + protlen + 2 ; + enclen = outlen + 16 /* taglen */; + outbuf = gcry_malloc_secure (enclen); + } + else + { + /* (( )( 4:hash 4:sha1 20: )) */ + outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen; + enclen = outlen/blklen * blklen; + outbuf = gcry_malloc_secure (outlen); + } + if (!outbuf) + rc = out_of_core (); + + /* Allocate a buffer for the nonce and the salt. */ + if (!rc) + { + /* Allocate random bytes to be used as IV, padding and s2k salt + * or in OCB mode for a nonce and the s2k salt. The IV/nonce is + * set later because for OCB we need to set the key first. */ + ivsize = (use_ocb? 12 : (blklen*2)) + 8; + iv = xtrymalloc (ivsize); + if (!iv) + rc = gpg_error_from_syserror (); + else + { + gcry_create_nonce (iv, ivsize); + s2ksalt = iv + ivsize - 8; + } + } + + /* Hash the passphrase and set the key. */ + if (!rc) + { + unsigned char *key; + size_t keylen = PROT_CIPHER_KEYLEN; + + key = gcry_malloc_secure (keylen); + if (!key) + rc = out_of_core (); + else + { + rc = hash_passphrase (passphrase, GCRY_MD_SHA1, + 3, s2ksalt, + s2k_count? s2k_count:get_standard_s2k_count(), + key, keylen); + if (!rc) + rc = gcry_cipher_setkey (hd, key, keylen); + xfree (key); + } + } + + /* Set the IV/nonce. */ + if (!rc) + { + rc = gcry_cipher_setiv (hd, iv, use_ocb? 12 : blklen); + } + + if (use_ocb) + { + /* In OCB Mode we use only the public key parameters as AAD. */ + rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin); + if (!rc) + rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len); + if (!rc) + rc = gcry_cipher_authenticate + (hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin)); + + } + else + { + /* Hash the entire expression for CBC mode. Because + * TIMESTAMP_EXP won't get protected, we can't simply hash a + * continuous buffer but need to call md_write several times. */ + gcry_md_hd_t md; + + rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 ); + if (!rc) + { + gcry_md_write (md, hashbegin, protbegin - hashbegin); + gcry_md_write (md, protbegin, protlen); + gcry_md_write (md, timestamp_exp, timestamp_exp_len); + gcry_md_write (md, protbegin+protlen, + hashlen - (protbegin+protlen - hashbegin)); + memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20); + gcry_md_close (md); + } + } + + + /* Encrypt. */ + if (!rc) + { + p = outbuf; + *p++ = '('; + *p++ = '('; + memcpy (p, protbegin, protlen); + p += protlen; + if (use_ocb) + { + *p++ = ')'; + *p++ = ')'; + } + else + { + memcpy (p, ")(4:hash4:sha120:", 17); + p += 17; + memcpy (p, hashvalue, 20); + p += 20; + *p++ = ')'; + *p++ = ')'; + memcpy (p, iv+blklen, blklen); /* Add padding. */ + p += blklen; + } + assert ( p - outbuf == outlen); + if (use_ocb) + { + gcry_cipher_final (hd); + rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0); + if (!rc) + { + log_assert (outlen + 16 == enclen); + rc = gcry_cipher_gettag (hd, outbuf + outlen, 16); + } + } + else + { + rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0); + } + } + + /* Release cipher handle and check for errors. */ + gcry_cipher_close (hd); + if (rc) + { + xfree (iv); + xfree (outbuf); + return rc; + } + + /* Now allocate the buffer we want to return. This is + + (protected openpgp-s2k3-sha1-aes-cbc + ((sha1 salt no_of_iterations) 16byte_iv) + encrypted_octet_string) + + in canoncical format of course. We use asprintf and %n modifier + and dummy values as placeholders. */ + { + char countbuf[35]; + + snprintf (countbuf, sizeof countbuf, "%lu", + s2k_count ? s2k_count : get_standard_s2k_count ()); + p = xtryasprintf + ("(9:protected%d:%s((4:sha18:%n_8bytes_%u:%s)%d:%n%*s)%d:%n%*s)", + (int)strlen (modestr), modestr, + &saltpos, + (unsigned int)strlen (countbuf), countbuf, + use_ocb? 12 : blklen, &ivpos, use_ocb? 12 : blklen, "", + enclen, &encpos, enclen, ""); + if (!p) + { + gpg_error_t tmperr = out_of_core (); + xfree (iv); + xfree (outbuf); + return tmperr; + } + + } + *resultlen = strlen (p); + *result = (unsigned char*)p; + memcpy (p+saltpos, s2ksalt, 8); + memcpy (p+ivpos, iv, use_ocb? 12 : blklen); + memcpy (p+encpos, outbuf, enclen); + xfree (iv); + xfree (outbuf); + return 0; +} + + + +/* Protect the key encoded in canonical format in PLAINKEY. We assume + a valid S-Exp here. With USE_UCB set to -1 the default scheme is + used (ie. either CBC or OCB), set to 0 the old CBC mode is used, + and set to 1 OCB is used. */ +int +agent_protect (const unsigned char *plainkey, const char *passphrase, + unsigned char **result, size_t *resultlen, + unsigned long s2k_count, int use_ocb) +{ + int rc; + const char *parmlist; + int prot_from_idx, prot_to_idx; + const unsigned char *s; + const unsigned char *hash_begin, *hash_end; + const unsigned char *prot_begin, *prot_end, *real_end; + size_t n; + int c, infidx, i; + char timestamp_exp[35]; + unsigned char *protected; + size_t protectedlen; + int depth = 0; + unsigned char *p; + int have_curve = 0; + + if (use_ocb == -1) + use_ocb = PROT_DEFAULT_TO_OCB; + + /* Create an S-expression with the protected-at timestamp. */ + memcpy (timestamp_exp, "(12:protected-at15:", 19); + gnupg_get_isotime (timestamp_exp+19); + timestamp_exp[19+15] = ')'; + + /* Parse original key. */ + s = plainkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + depth++; + hash_begin = s; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + for (infidx=0; protect_info[infidx].algo + && !smatch (&s, n, protect_info[infidx].algo); infidx++) + ; + if (!protect_info[infidx].algo) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + /* The parser below is a complete mess: To make it robust for ECC + use we should reorder the s-expression to include only what we + really need and thus guarantee the right order for saving stuff. + This should be done before calling this function and maybe with + the help of the new gcry_sexp_extract_param. */ + parmlist = protect_info[infidx].parmlist; + prot_from_idx = protect_info[infidx].prot_from; + prot_to_idx = protect_info[infidx].prot_to; + prot_begin = prot_end = NULL; + for (i=0; (c=parmlist[i]); i++) + { + if (i == prot_from_idx) + prot_begin = s; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (n != 1 || c != *s) + { + if (n == 5 && !memcmp (s, "curve", 5) + && !i && protect_info[infidx].ecc_hack) + { + /* This is a private ECC key but the first parameter is + the name of the curve. We change the parameter list + here to the one we expect in this case. */ + have_curve = 1; + parmlist = "?qd"; + prot_from_idx = 2; + prot_to_idx = 2; + } + else if (n == 5 && !memcmp (s, "flags", 5) + && i == 1 && have_curve) + { + /* "curve" followed by "flags": Change again. */ + parmlist = "??qd"; + prot_from_idx = 3; + prot_to_idx = 3; + } + else + return gpg_error (GPG_ERR_INV_SEXP); + } + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s +=n; /* skip value */ + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + if (i == prot_to_idx) + prot_end = s; + s++; + } + if (*s != ')' || !prot_begin || !prot_end ) + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + hash_end = s; + s++; + /* Skip to the end of the S-expression. */ + assert (depth == 1); + rc = sskip (&s, &depth); + if (rc) + return rc; + assert (!depth); + real_end = s-1; + + rc = do_encryption (hash_begin, hash_end - hash_begin + 1, + prot_begin, prot_end - prot_begin + 1, + passphrase, timestamp_exp, sizeof (timestamp_exp), + &protected, &protectedlen, s2k_count, use_ocb); + if (rc) + return rc; + + /* Now create the protected version of the key. Note that the 10 + extra bytes are for for the inserted "protected-" string (the + beginning of the plaintext reads: "((11:private-key(" ). The 35 + term is the space for (12:protected-at15:). */ + *resultlen = (10 + + (prot_begin-plainkey) + + protectedlen + + 35 + + (real_end-prot_end)); + *result = p = xtrymalloc (*resultlen); + if (!p) + { + gpg_error_t tmperr = out_of_core (); + xfree (protected); + return tmperr; + } + memcpy (p, "(21:protected-", 14); + p += 14; + memcpy (p, plainkey+4, prot_begin - plainkey - 4); + p += prot_begin - plainkey - 4; + memcpy (p, protected, protectedlen); + p += protectedlen; + + memcpy (p, timestamp_exp, 35); + p += 35; + + memcpy (p, prot_end+1, real_end - prot_end); + p += real_end - prot_end; + assert ( p - *result == *resultlen); + xfree (protected); + + return 0; +} + + + +/* Do the actual decryption and check the return list for consistency. */ +static int +do_decryption (const unsigned char *aad_begin, size_t aad_len, + const unsigned char *aadhole_begin, size_t aadhole_len, + const unsigned char *protected, size_t protectedlen, + const char *passphrase, + const unsigned char *s2ksalt, unsigned long s2kcount, + const unsigned char *iv, size_t ivlen, + int prot_cipher, int prot_cipher_keylen, int is_ocb, + unsigned char **result) +{ + int rc = 0; + int blklen; + gcry_cipher_hd_t hd; + unsigned char *outbuf; + size_t reallen; + + blklen = gcry_cipher_get_algo_blklen (prot_cipher); + if (is_ocb) + { + /* OCB does not require a multiple of the block length but we + * check that it is long enough for the 128 bit tag and that we + * have the 96 bit nonce. */ + if (protectedlen < (4 + 16) || ivlen != 12) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + } + else + { + if (protectedlen < 4 || (protectedlen%blklen)) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + } + + rc = gcry_cipher_open (&hd, prot_cipher, + is_ocb? GCRY_CIPHER_MODE_OCB : + GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_SECURE); + if (rc) + return rc; + + outbuf = gcry_malloc_secure (protectedlen); + if (!outbuf) + rc = out_of_core (); + + /* Hash the passphrase and set the key. */ + if (!rc) + { + unsigned char *key; + + key = gcry_malloc_secure (prot_cipher_keylen); + if (!key) + rc = out_of_core (); + else + { + rc = hash_passphrase (passphrase, GCRY_MD_SHA1, + 3, s2ksalt, s2kcount, key, prot_cipher_keylen); + if (!rc) + rc = gcry_cipher_setkey (hd, key, prot_cipher_keylen); + xfree (key); + } + } + + /* Set the IV/nonce. */ + if (!rc) + { + rc = gcry_cipher_setiv (hd, iv, ivlen); + } + + /* Decrypt. */ + if (!rc) + { + if (is_ocb) + { + rc = gcry_cipher_authenticate (hd, aad_begin, + aadhole_begin - aad_begin); + if (!rc) + rc = gcry_cipher_authenticate + (hd, aadhole_begin + aadhole_len, + aad_len - (aadhole_begin+aadhole_len - aad_begin)); + + if (!rc) + { + gcry_cipher_final (hd); + rc = gcry_cipher_decrypt (hd, outbuf, protectedlen - 16, + protected, protectedlen - 16); + } + if (!rc) + rc = gcry_cipher_checktag (hd, protected + protectedlen - 16, 16); + } + else + { + rc = gcry_cipher_decrypt (hd, outbuf, protectedlen, + protected, protectedlen); + } + } + + /* Release cipher handle and check for errors. */ + gcry_cipher_close (hd); + if (rc) + { + xfree (outbuf); + return rc; + } + + /* Do a quick check on the data structure. */ + if (*outbuf != '(' && outbuf[1] != '(') + { + /* Note that in OCB mode this is actually invalid _encrypted_ + * data and not a bad passphrase. */ + xfree (outbuf); + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + } + + /* Check that we have a consistent S-Exp. */ + reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL); + if (!reallen || (reallen + blklen < protectedlen) ) + { + xfree (outbuf); + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + } + *result = outbuf; + return 0; +} + + +/* Merge the parameter list contained in CLEARTEXT with the original + * protect lists PROTECTEDKEY by replacing the list at REPLACEPOS. + * Return the new list in RESULT and the MIC value in the 20 byte + * buffer SHA1HASH; if SHA1HASH is NULL no MIC will be computed. + * CUTOFF and CUTLEN will receive the offset and the length of the + * resulting list which should go into the MIC calculation but then be + * removed. */ +static int +merge_lists (const unsigned char *protectedkey, + size_t replacepos, + const unsigned char *cleartext, + unsigned char *sha1hash, + unsigned char **result, size_t *resultlen, + size_t *cutoff, size_t *cutlen) +{ + size_t n, newlistlen; + unsigned char *newlist, *p; + const unsigned char *s; + const unsigned char *startpos, *endpos; + int i, rc; + + *result = NULL; + *resultlen = 0; + *cutoff = 0; + *cutlen = 0; + + if (replacepos < 26) + return gpg_error (GPG_ERR_BUG); + + /* Estimate the required size of the resulting list. We have a large + safety margin of >20 bytes (FIXME: MIC hash from CLEARTEXT and the + removed "protected-" */ + newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL); + if (!newlistlen) + return gpg_error (GPG_ERR_BUG); + n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL); + if (!n) + return gpg_error (GPG_ERR_BUG); + newlistlen += n; + newlist = gcry_malloc_secure (newlistlen); + if (!newlist) + return out_of_core (); + + /* Copy the initial segment */ + strcpy ((char*)newlist, "(11:private-key"); + p = newlist + 15; + memcpy (p, protectedkey+15+10, replacepos-15-10); + p += replacepos-15-10; + + /* Copy the cleartext. */ + s = cleartext; + if (*s != '(' && s[1] != '(') + return gpg_error (GPG_ERR_BUG); /*we already checked this */ + s += 2; + startpos = s; + while ( *s == '(' ) + { + s++; + n = snext (&s); + if (!n) + goto invalid_sexp; + s += n; + n = snext (&s); + if (!n) + goto invalid_sexp; + s += n; + if ( *s != ')' ) + goto invalid_sexp; + s++; + } + if ( *s != ')' ) + goto invalid_sexp; + endpos = s; + s++; + + /* Intermezzo: Get the MIC if requested. */ + if (sha1hash) + { + if (*s != '(') + goto invalid_sexp; + s++; + n = snext (&s); + if (!smatch (&s, n, "hash")) + goto invalid_sexp; + n = snext (&s); + if (!smatch (&s, n, "sha1")) + goto invalid_sexp; + n = snext (&s); + if (n != 20) + goto invalid_sexp; + memcpy (sha1hash, s, 20); + s += n; + if (*s != ')') + goto invalid_sexp; + } + + /* Append the parameter list. */ + memcpy (p, startpos, endpos - startpos); + p += endpos - startpos; + + /* Skip over the protected list element in the original list. */ + s = protectedkey + replacepos; + assert (*s == '('); + s++; + i = 1; + rc = sskip (&s, &i); + if (rc) + goto failure; + /* Record the position of the optional protected-at expression. */ + if (*s == '(') + { + const unsigned char *save_s = s; + s++; + n = snext (&s); + if (smatch (&s, n, "protected-at")) + { + i = 1; + rc = sskip (&s, &i); + if (rc) + goto failure; + *cutlen = s - save_s; + } + s = save_s; + } + startpos = s; + i = 2; /* we are inside this level */ + rc = sskip (&s, &i); + if (rc) + goto failure; + assert (s[-1] == ')'); + endpos = s; /* one behind the end of the list */ + + /* Append the rest. */ + if (*cutlen) + *cutoff = p - newlist; + memcpy (p, startpos, endpos - startpos); + p += endpos - startpos; + + + /* ready */ + *result = newlist; + *resultlen = newlistlen; + return 0; + + failure: + wipememory (newlist, newlistlen); + xfree (newlist); + return rc; + + invalid_sexp: + wipememory (newlist, newlistlen); + xfree (newlist); + return gpg_error (GPG_ERR_INV_SEXP); +} + + + +/* Unprotect the key encoded in canonical format. We assume a valid + S-Exp here. If a protected-at item is available, its value will + be stored at protected_at unless this is NULL. */ +int +agent_unprotect (ctrl_t ctrl, + const unsigned char *protectedkey, const char *passphrase, + gnupg_isotime_t protected_at, + unsigned char **result, size_t *resultlen) +{ + static struct { + const char *name; /* Name of the protection method. */ + int algo; /* (A zero indicates the "openpgp-native" hack.) */ + int keylen; /* Used key length in bytes. */ + unsigned int is_ocb:1; + } algotable[] = { + { "openpgp-s2k3-sha1-aes-cbc", GCRY_CIPHER_AES128, (128/8)}, + { "openpgp-s2k3-sha1-aes256-cbc", GCRY_CIPHER_AES256, (256/8)}, + { "openpgp-s2k3-ocb-aes", GCRY_CIPHER_AES128, (128/8), 1}, + { "openpgp-native", 0, 0 } + }; + int rc; + const unsigned char *s; + const unsigned char *protect_list; + size_t n; + int infidx, i; + unsigned char sha1hash[20], sha1hash2[20]; + const unsigned char *s2ksalt; + unsigned long s2kcount; + const unsigned char *iv; + int prot_cipher, prot_cipher_keylen; + int is_ocb; + const unsigned char *aad_begin, *aad_end, *aadhole_begin, *aadhole_end; + const unsigned char *prot_begin; + unsigned char *cleartext; + unsigned char *final; + size_t finallen; + size_t cutoff, cutlen; + + if (protected_at) + *protected_at = 0; + + s = protectedkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "protected-private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + { + aad_begin = aad_end = s; + aad_end++; + i = 1; + rc = sskip (&aad_end, &i); + if (rc) + return rc; + } + + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + for (infidx=0; protect_info[infidx].algo + && !smatch (&s, n, protect_info[infidx].algo); infidx++) + ; + if (!protect_info[infidx].algo) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + /* See wether we have a protected-at timestamp. */ + protect_list = s; /* Save for later. */ + if (protected_at) + { + while (*s == '(') + { + prot_begin = s; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "protected-at")) + { + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (n != 15) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + memcpy (protected_at, s, 15); + protected_at[15] = 0; + break; + } + s += n; + i = 1; + rc = sskip (&s, &i); + if (rc) + return rc; + } + } + + /* Now find the list with the protected information. Here is an + example for such a list: + (protected openpgp-s2k3-sha1-aes-cbc + ((sha1 ) ) + ) + */ + s = protect_list; + for (;;) + { + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + prot_begin = s; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "protected")) + break; + s += n; + i = 1; + rc = sskip (&s, &i); + if (rc) + return rc; + } + /* found */ + { + aadhole_begin = aadhole_end = prot_begin; + aadhole_end++; + i = 1; + rc = sskip (&aadhole_end, &i); + if (rc) + return rc; + } + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + /* Lookup the protection algo. */ + prot_cipher = 0; /* (avoid gcc warning) */ + prot_cipher_keylen = 0; /* (avoid gcc warning) */ + is_ocb = 0; + for (i=0; i < DIM (algotable); i++) + if (smatch (&s, n, algotable[i].name)) + { + prot_cipher = algotable[i].algo; + prot_cipher_keylen = algotable[i].keylen; + is_ocb = algotable[i].is_ocb; + break; + } + if (i == DIM (algotable)) + return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION); + + if (!prot_cipher) /* This is "openpgp-native". */ + { + gcry_sexp_t s_prot_begin; + + rc = gcry_sexp_sscan (&s_prot_begin, NULL, + prot_begin, + gcry_sexp_canon_len (prot_begin, 0,NULL,NULL)); + if (rc) + return rc; + + rc = convert_from_openpgp_native (ctrl, s_prot_begin, passphrase, &final); + gcry_sexp_release (s_prot_begin); + if (!rc) + { + *result = final; + *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL); + } + return rc; + } + + if (*s != '(' || s[1] != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s += 2; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "sha1")) + return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION); + n = snext (&s); + if (n != 8) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + s2ksalt = s; + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + /* We expect a list close as next, so we can simply use strtoul() + here. We might want to check that we only have digits - but this + is nothing we should worry about */ + if (s[n] != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + + /* Old versions of gpg-agent used the funny floating point number in + a byte encoding as specified by OpenPGP. However this is not + needed and thus we now store it as a plain unsigned integer. We + can easily distinguish the old format by looking at its value: + Less than 256 is an old-style encoded number; other values are + plain integers. In any case we check that they are at least + 65536 because we never used a lower value in the past and we + should have a lower limit. */ + s2kcount = strtoul ((const char*)s, NULL, 10); + if (!s2kcount) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + if (s2kcount < 256) + s2kcount = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6); + if (s2kcount < 65536) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + + s += n; + s++; /* skip list end */ + + n = snext (&s); + if (is_ocb) + { + if (n != 12) /* Wrong size of the nonce. */ + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + } + else + { + if (n != 16) /* Wrong blocksize for IV (we support only 128 bit). */ + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + } + iv = s; + s += n; + if (*s != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + cleartext = NULL; /* Avoid cc warning. */ + rc = do_decryption (aad_begin, aad_end - aad_begin, + aadhole_begin, aadhole_end - aadhole_begin, + s, n, + passphrase, s2ksalt, s2kcount, + iv, is_ocb? 12:16, + prot_cipher, prot_cipher_keylen, is_ocb, + &cleartext); + if (rc) + return rc; + + rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext, + is_ocb? NULL : sha1hash, + &final, &finallen, &cutoff, &cutlen); + /* Albeit cleartext has been allocated in secure memory and thus + xfree will wipe it out, we do an extra wipe just in case + somethings goes badly wrong. */ + wipememory (cleartext, n); + xfree (cleartext); + if (rc) + return rc; + + if (!is_ocb) + { + rc = calculate_mic (final, sha1hash2); + if (!rc && memcmp (sha1hash, sha1hash2, 20)) + rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + if (rc) + { + wipememory (final, finallen); + xfree (final); + return rc; + } + } + + /* Now remove the part which is included in the MIC but should not + go into the final thing. */ + if (cutlen) + { + memmove (final+cutoff, final+cutoff+cutlen, finallen-cutoff-cutlen); + finallen -= cutlen; + } + + *result = final; + *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL); + return 0; +} + +/* Check the type of the private key, this is one of the constants: + PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the + value 0), PRIVATE_KEY_CLEAR for an unprotected private key. + PRIVATE_KEY_PROTECTED for an protected private key or + PRIVATE_KEY_SHADOWED for a sub key where the secret parts are + stored elsewhere. Finally PRIVATE_KEY_OPENPGP_NONE may be returned + is the key is still in the openpgp-native format but without + protection. */ +int +agent_private_key_type (const unsigned char *privatekey) +{ + const unsigned char *s; + size_t n; + int i; + + s = privatekey; + if (*s != '(') + return PRIVATE_KEY_UNKNOWN; + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; + if (smatch (&s, n, "protected-private-key")) + { + /* We need to check whether this is openpgp-native protected + with the protection method "none". In that case we return a + different key type so that the caller knows that there is no + need to ask for a passphrase. */ + if (*s != '(') + return PRIVATE_KEY_PROTECTED; /* Unknown sexp - assume protected. */ + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + s += n; /* Skip over the algo */ + + /* Find the (protected ...) list. */ + for (;;) + { + if (*s != '(') + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + if (smatch (&s, n, "protected")) + break; + s += n; + i = 1; + if (sskip (&s, &i)) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + } + /* Found - Is this openpgp-native? */ + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + if (smatch (&s, n, "openpgp-native")) /* Yes. */ + { + if (*s != '(') + return PRIVATE_KEY_UNKNOWN; /* Unknown sexp. */ + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + s += n; /* Skip over "openpgp-private-key". */ + /* Find the (protection ...) list. */ + for (;;) + { + if (*s != '(') + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + if (smatch (&s, n, "protection")) + break; + s += n; + i = 1; + if (sskip (&s, &i)) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + } + /* Found - Is the mode "none"? */ + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */ + if (smatch (&s, n, "none")) + return PRIVATE_KEY_OPENPGP_NONE; /* Yes. */ + } + + return PRIVATE_KEY_PROTECTED; + } + if (smatch (&s, n, "shadowed-private-key")) + return PRIVATE_KEY_SHADOWED; + if (smatch (&s, n, "private-key")) + return PRIVATE_KEY_CLEAR; + return PRIVATE_KEY_UNKNOWN; +} + + + +/* Transform a passphrase into a suitable key of length KEYLEN and + store this key in the caller provided buffer KEY. The caller must + provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on + that mode an S2KSALT of 8 random bytes and an S2KCOUNT. + + Returns an error code on failure. */ +static int +hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, + unsigned long s2kcount, + unsigned char *key, size_t keylen) +{ + /* The key derive function does not support a zero length string for + the passphrase in the S2K modes. Return a better suited error + code than GPG_ERR_INV_DATA. */ + if (!passphrase || !*passphrase) + return gpg_error (GPG_ERR_NO_PASSPHRASE); + return gcry_kdf_derive (passphrase, strlen (passphrase), + s2kmode == 3? GCRY_KDF_ITERSALTED_S2K : + s2kmode == 1? GCRY_KDF_SALTED_S2K : + s2kmode == 0? GCRY_KDF_SIMPLE_S2K : GCRY_KDF_NONE, + hashalgo, s2ksalt, 8, s2kcount, + keylen, key); +} + + +gpg_error_t +s2k_hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, + unsigned int s2kcount, + unsigned char *key, size_t keylen) +{ + return hash_passphrase (passphrase, hashalgo, s2kmode, s2ksalt, + S2K_DECODE_COUNT (s2kcount), + key, keylen); +} + + + + +/* Create an canonical encoded S-expression with the shadow info from + a card's SERIALNO and the IDSTRING. */ +unsigned char * +make_shadow_info (const char *serialno, const char *idstring) +{ + const char *s; + char *info, *p; + char numbuf[20]; + size_t n; + + for (s=serialno, n=0; *s && s[1]; s += 2) + n++; + + info = p = xtrymalloc (1 + sizeof numbuf + n + + sizeof numbuf + strlen (idstring) + 1 + 1); + if (!info) + return NULL; + *p++ = '('; + p = stpcpy (p, smklen (numbuf, sizeof numbuf, n, NULL)); + for (s=serialno; *s && s[1]; s += 2) + *(unsigned char *)p++ = xtoi_2 (s); + p = stpcpy (p, smklen (numbuf, sizeof numbuf, strlen (idstring), NULL)); + p = stpcpy (p, idstring); + *p++ = ')'; + *p = 0; + return (unsigned char *)info; +} + + + +/* Create a shadow key from a public key. We use the shadow protocol + "ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting + S-expression is returned in an allocated buffer RESULT will point + to. The input parameters are expected to be valid canonicalized + S-expressions */ +int +agent_shadow_key (const unsigned char *pubkey, + const unsigned char *shadow_info, + unsigned char **result) +{ + const unsigned char *s; + const unsigned char *point; + size_t n; + int depth = 0; + char *p; + size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL); + size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL); + + if (!pubkey_len || !shadow_info_len) + return gpg_error (GPG_ERR_INV_VALUE); + s = pubkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "public-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; /* skip over the algorithm name */ + + while (*s != ')') + { + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s +=n; /* skip value */ + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + s++; + } + point = s; /* insert right before the point */ + depth--; + s++; + assert (depth == 1); + + /* Calculate required length by taking in account: the "shadowed-" + prefix, the "shadowed", "t1-v1" as well as some parenthesis */ + n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1; + *result = xtrymalloc (n); + p = (char*)*result; + if (!p) + return out_of_core (); + p = stpcpy (p, "(20:shadowed-private-key"); + /* (10:public-key ...)*/ + memcpy (p, pubkey+14, point - (pubkey+14)); + p += point - (pubkey+14); + p = stpcpy (p, "(8:shadowed5:t1-v1"); + memcpy (p, shadow_info, shadow_info_len); + p += shadow_info_len; + *p++ = ')'; + memcpy (p, point, pubkey_len - (point - pubkey)); + p += pubkey_len - (point - pubkey); + + return 0; +} + +/* Parse a canonical encoded shadowed key and return a pointer to the + inner list with the shadow_info */ +int +agent_get_shadow_info (const unsigned char *shadowkey, + unsigned char const **shadow_info) +{ + const unsigned char *s; + size_t n; + int depth = 0; + + s = shadowkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "shadowed-private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; /* skip over the algorithm name */ + + for (;;) + { + if (*s == ')') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "shadowed")) + break; + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s +=n; /* skip value */ + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + s++; + } + /* Found the shadowed list, S points to the protocol */ + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "t1-v1")) + { + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + *shadow_info = s; + } + else + return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + return 0; +} + + +/* Parse the canonical encoded SHADOW_INFO S-expression. On success + the hex encoded serial number is returned as a malloced strings at + R_HEXSN and the Id string as a malloced string at R_IDSTR. On + error an error code is returned and NULL is stored at the result + parameters addresses. If the serial number or the ID string is not + required, NULL may be passed for them. */ +gpg_error_t +parse_shadow_info (const unsigned char *shadow_info, + char **r_hexsn, char **r_idstr, int *r_pinlen) +{ + const unsigned char *s; + size_t n; + + if (r_hexsn) + *r_hexsn = NULL; + if (r_idstr) + *r_idstr = NULL; + if (r_pinlen) + *r_pinlen = 0; + + s = shadow_info; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + if (r_hexsn) + { + *r_hexsn = bin2hex (s, n, NULL); + if (!*r_hexsn) + return gpg_error_from_syserror (); + } + s += n; + + n = snext (&s); + if (!n) + { + if (r_hexsn) + { + xfree (*r_hexsn); + *r_hexsn = NULL; + } + return gpg_error (GPG_ERR_INV_SEXP); + } + + if (r_idstr) + { + *r_idstr = xtrymalloc (n+1); + if (!*r_idstr) + { + if (r_hexsn) + { + xfree (*r_hexsn); + *r_hexsn = NULL; + } + return gpg_error_from_syserror (); + } + memcpy (*r_idstr, s, n); + (*r_idstr)[n] = 0; + } + + /* Parse the optional PINLEN. */ + n = snext (&s); + if (!n) + return 0; + + if (r_pinlen) + { + char *tmpstr = xtrymalloc (n+1); + if (!tmpstr) + { + if (r_hexsn) + { + xfree (*r_hexsn); + *r_hexsn = NULL; + } + if (r_idstr) + { + xfree (*r_idstr); + *r_idstr = NULL; + } + return gpg_error_from_syserror (); + } + memcpy (tmpstr, s, n); + tmpstr[n] = 0; + + *r_pinlen = (int)strtol (tmpstr, NULL, 10); + xfree (tmpstr); + } + + return 0; +} diff --git a/agent/t-protect.c b/agent/t-protect.c new file mode 100644 index 0000000..1d3c8ec --- /dev/null +++ b/agent/t-protect.c @@ -0,0 +1,350 @@ +/* t-protect.c - Module tests for protect.c + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include + +#include "agent.h" + + +#define pass() do { ; } while(0) +#define fail() do { fprintf (stderr, "%s:%d: test failed\n",\ + __FILE__,__LINE__); \ + exit (1); \ + } while(0) + + +static void +test_agent_protect (void) +{ + /* Protect the key encoded in canonical format in PLAINKEY. We assume + a valid S-Exp here. */ + + unsigned int i; + int ret; + struct key_spec + { + const char *string; + }; + /* Valid RSA key. */ + struct key_spec key_rsa_valid = + { + "\x28\x31\x31\x3A\x70\x72\x69\x76\x61\x74\x65\x2D\x6B\x65\x79\x28\x33\x3A\x72\x73" + "\x61\x28\x31\x3A\x6E\x31\x32\x39\x3A\x00\xB6\xB5\x09\x59\x6A\x9E\xCA\xBC\x93\x92" + "\x12\xF8\x91\xE6\x56\xA6\x26\xBA\x07\xDA\x85\x21\xA9\xCA\xD4\xC0\x8E\x64\x0C\x04" + "\x05\x2F\xBB\x87\xF4\x24\xEF\x1A\x02\x75\xA4\x8A\x92\x99\xAC\x9D\xB6\x9A\xBE\x3D" + "\x01\x24\xE6\xC7\x56\xB1\xF7\xDF\xB9\xB8\x42\xD6\x25\x1A\xEA\x6E\xE8\x53\x90\x49" + "\x5C\xAD\xA7\x3D\x67\x15\x37\xFC\xE5\x85\x0A\x93\x2F\x32\xBA\xB6\x0A\xB1\xAC\x1F" + "\x85\x2C\x1F\x83\xC6\x25\xE7\xA7\xD7\x0C\xDA\x9E\xF1\x6D\x5C\x8E\x47\x73\x9D\x77" + "\xDF\x59\x26\x1A\xBE\x84\x54\x80\x7F\xF4\x41\xE1\x43\xFB\xD3\x7F\x85\x45\x29\x28" + "\x31\x3A\x65\x33\x3A\x01\x00\x01\x29\x28\x31\x3A\x64\x31\x32\x38\x3A\x07\x7A\xD3" + "\xDE\x28\x42\x45\xF4\x80\x6A\x1B\x82\xB7\x9E\x61\x6F\xBD\xE8\x21\xC8\x2D\x69\x1A" + "\x65\x66\x5E\x57\xB5\xFA\xD3\xF3\x4E\x67\xF4\x01\xE7\xBD\x2E\x28\x69\x9E\x89\xD9" + "\xC4\x96\xCF\x82\x19\x45\xAE\x83\xAC\x7A\x12\x31\x17\x6A\x19\x6B\xA6\x02\x7E\x77" + "\xD8\x57\x89\x05\x5D\x50\x40\x4A\x7A\x2A\x95\xB1\x51\x2F\x91\xF1\x90\xBB\xAE\xF7" + "\x30\xED\x55\x0D\x22\x7D\x51\x2F\x89\xC0\xCD\xB3\x1A\xC0\x6F\xA9\xA1\x95\x03\xDD" + "\xF6\xB6\x6D\x0B\x42\xB9\x69\x1B\xFD\x61\x40\xEC\x17\x20\xFF\xC4\x8A\xE0\x0C\x34" + "\x79\x6D\xC8\x99\xE5\x29\x28\x31\x3A\x70\x36\x35\x3A\x00\xD5\x86\xC7\x8E\x5F\x1B" + "\x4B\xF2\xE7\xCD\x7A\x04\xCA\x09\x19\x11\x70\x6F\x19\x78\x8B\x93\xE4\x4E\xE2\x0A" + "\xAF\x46\x2E\x83\x63\xE9\x8A\x72\x25\x3E\xD8\x45\xCC\xBF\x24\x81\xBB\x35\x1E\x85" + "\x57\xC8\x5B\xCF\xFF\x0D\xAB\xDB\xFF\x8E\x26\xA7\x9A\x09\x38\x09\x6F\x27\x29\x28" + "\x31\x3A\x71\x36\x35\x3A\x00\xDB\x0C\xDF\x60\xF2\x6F\x2A\x29\x6C\x88\xD6\xBF\x9F" + "\x8E\x5B\xE4\x5C\x0D\xDD\x71\x3C\x96\xCC\x73\xEB\xCB\x48\xB0\x61\x74\x09\x43\xF2" + "\x1D\x2A\x93\xD6\xE4\x2A\x72\x11\xE7\xF0\x2A\x95\xDC\xED\x6C\x39\x0A\x67\xAD\x21" + "\xEC\xF7\x39\xAE\x8A\x0C\xA4\x6F\xF2\xEB\xB3\x29\x28\x31\x3A\x75\x36\x34\x3A\x33" + "\x14\x91\x95\xF1\x69\x12\xDB\x20\xA4\x8D\x02\x0D\xBC\x3B\x9E\x38\x81\xB3\x9D\x72" + "\x2B\xF7\x93\x78\xF6\x34\x0F\x43\x14\x8A\x6E\x9F\xC5\xF5\x3E\x28\x53\xB7\x38\x7B" + "\xA4\x44\x3B\xA5\x3A\x52\xFC\xA8\x17\x3D\xE6\xE8\x5B\x42\xF9\x78\x3D\x4A\x78\x17" + "\xD0\x68\x0B\x29\x29\x29\x00" + }; + /* This RSA key is missing the last closing brace. */ + struct key_spec key_rsa_bogus_0 = + { + "\x28\x31\x31\x3A\x70\x72\x69\x76\x61\x74\x65\x2D\x6B\x65\x79\x28\x33\x3A\x72\x73" + "\x61\x28\x31\x3A\x6E\x31\x32\x39\x3A\x00\xB6\xB5\x09\x59\x6A\x9E\xCA\xBC\x93\x92" + "\x12\xF8\x91\xE6\x56\xA6\x26\xBA\x07\xDA\x85\x21\xA9\xCA\xD4\xC0\x8E\x64\x0C\x04" + "\x05\x2F\xBB\x87\xF4\x24\xEF\x1A\x02\x75\xA4\x8A\x92\x99\xAC\x9D\xB6\x9A\xBE\x3D" + "\x01\x24\xE6\xC7\x56\xB1\xF7\xDF\xB9\xB8\x42\xD6\x25\x1A\xEA\x6E\xE8\x53\x90\x49" + "\x5C\xAD\xA7\x3D\x67\x15\x37\xFC\xE5\x85\x0A\x93\x2F\x32\xBA\xB6\x0A\xB1\xAC\x1F" + "\x85\x2C\x1F\x83\xC6\x25\xE7\xA7\xD7\x0C\xDA\x9E\xF1\x6D\x5C\x8E\x47\x73\x9D\x77" + "\xDF\x59\x26\x1A\xBE\x84\x54\x80\x7F\xF4\x41\xE1\x43\xFB\xD3\x7F\x85\x45\x29\x28" + "\x31\x3A\x65\x33\x3A\x01\x00\x01\x29\x28\x31\x3A\x64\x31\x32\x38\x3A\x07\x7A\xD3" + "\xDE\x28\x42\x45\xF4\x80\x6A\x1B\x82\xB7\x9E\x61\x6F\xBD\xE8\x21\xC8\x2D\x69\x1A" + "\x65\x66\x5E\x57\xB5\xFA\xD3\xF3\x4E\x67\xF4\x01\xE7\xBD\x2E\x28\x69\x9E\x89\xD9" + "\xC4\x96\xCF\x82\x19\x45\xAE\x83\xAC\x7A\x12\x31\x17\x6A\x19\x6B\xA6\x02\x7E\x77" + "\xD8\x57\x89\x05\x5D\x50\x40\x4A\x7A\x2A\x95\xB1\x51\x2F\x91\xF1\x90\xBB\xAE\xF7" + "\x30\xED\x55\x0D\x22\x7D\x51\x2F\x89\xC0\xCD\xB3\x1A\xC0\x6F\xA9\xA1\x95\x03\xDD" + "\xF6\xB6\x6D\x0B\x42\xB9\x69\x1B\xFD\x61\x40\xEC\x17\x20\xFF\xC4\x8A\xE0\x0C\x34" + "\x79\x6D\xC8\x99\xE5\x29\x28\x31\x3A\x70\x36\x35\x3A\x00\xD5\x86\xC7\x8E\x5F\x1B" + "\x4B\xF2\xE7\xCD\x7A\x04\xCA\x09\x19\x11\x70\x6F\x19\x78\x8B\x93\xE4\x4E\xE2\x0A" + "\xAF\x46\x2E\x83\x63\xE9\x8A\x72\x25\x3E\xD8\x45\xCC\xBF\x24\x81\xBB\x35\x1E\x85" + "\x57\xC8\x5B\xCF\xFF\x0D\xAB\xDB\xFF\x8E\x26\xA7\x9A\x09\x38\x09\x6F\x27\x29\x28" + "\x31\x3A\x71\x36\x35\x3A\x00\xDB\x0C\xDF\x60\xF2\x6F\x2A\x29\x6C\x88\xD6\xBF\x9F" + "\x8E\x5B\xE4\x5C\x0D\xDD\x71\x3C\x96\xCC\x73\xEB\xCB\x48\xB0\x61\x74\x09\x43\xF2" + "\x1D\x2A\x93\xD6\xE4\x2A\x72\x11\xE7\xF0\x2A\x95\xDC\xED\x6C\x39\x0A\x67\xAD\x21" + "\xEC\xF7\x39\xAE\x8A\x0C\xA4\x6F\xF2\xEB\xB3\x29\x28\x31\x3A\x75\x36\x34\x3A\x33" + "\x14\x91\x95\xF1\x69\x12\xDB\x20\xA4\x8D\x02\x0D\xBC\x3B\x9E\x38\x81\xB3\x9D\x72" + "\x2B\xF7\x93\x78\xF6\x34\x0F\x43\x14\x8A\x6E\x9F\xC5\xF5\x3E\x28\x53\xB7\x38\x7B" + "\xA4\x44\x3B\xA5\x3A\x52\xFC\xA8\x17\x3D\xE6\xE8\x5B\x42\xF9\x78\x3D\x4A\x78\x17" + "\xD0\x68\x0B\x29\x29\x00" + }; + /* This RSA key is the 'e' value. */ + struct key_spec key_rsa_bogus_1 = + { + "\x28\x31\x31\x3A\x70\x72\x69\x76\x61\x74\x65\x2D\x6B\x65\x79\x28\x33\x3A\x72\x73" + "\x61\x28\x31\x3A\x6E\x31\x32\x39\x3A\x00\xA8\x80\xB6\x71\xF4\x95\x9F\x49\x84\xED" + "\xC1\x1D\x5F\xFF\xED\x14\x7B\x9C\x6A\x62\x0B\x7B\xE2\x3E\x41\x48\x49\x85\xF5\x64" + "\x50\x04\x9D\x30\xFC\x84\x1F\x01\xC3\xC3\x15\x03\x48\x6D\xFE\x59\x0B\xB0\xD0\x3E" + "\x68\x8A\x05\x7A\x62\xB0\xB9\x6E\xC5\xD2\xA8\xEE\x0C\x6B\xDE\x5E\x3D\x8E\xE8\x8F" + "\xB3\xAE\x86\x99\x7E\xDE\x2B\xC2\x4D\x60\x51\xDB\xB1\x2C\xD0\x38\xEC\x88\x62\x3E" + "\xA9\xDD\x11\x53\x04\x17\xE4\xF2\x07\x50\xDC\x44\xED\x14\xF5\x0B\xAB\x9C\xBC\x24" + "\xC6\xCB\xAD\x0F\x05\x25\x94\xE2\x73\xEB\x14\xD5\xEE\x5E\x18\xF0\x40\x31\x29\x28" + "\x31\x3A\x64\x31\x32\x38\x3A\x40\xD0\x55\x9D\x2A\xA7\xBC\xBF\xE2\x3E\x33\x98\x71" + "\x7B\x37\x3D\xB8\x38\x57\xA1\x43\xEA\x90\x81\x42\xCA\x23\xE1\xBF\x9C\xA8\xBC\xC5" + "\x9B\xF8\x9D\x77\x71\xCD\xD3\x85\x8B\x20\x3A\x92\xE9\xBC\x79\xF3\xF7\xF5\x6D\x15" + "\xA3\x58\x3F\xC2\xEB\xED\x72\xD4\xE0\xCF\xEC\xB3\xEC\xEB\x09\xEA\x1E\x72\x6A\xBA" + "\x95\x82\x2C\x7E\x30\x95\x66\x3F\xA8\x2D\x40\x0F\x7A\x12\x4E\xF0\x71\x0F\x97\xDB" + "\x81\xE4\x39\x6D\x24\x58\xFA\xAB\x3A\x36\x73\x63\x01\x77\x42\xC7\x9A\xEA\x87\xDA" + "\x93\x8F\x6C\x64\xAD\x9E\xF0\xCA\xA2\x89\xA4\x0E\xB3\x25\x73\x29\x28\x31\x3A\x70" + "\x36\x35\x3A\x00\xC3\xF7\x37\x3F\x9D\x93\xEC\xC7\x5E\x4C\xB5\x73\x29\x62\x35\x80" + "\xC6\x7C\x1B\x1E\x68\x5F\x92\x56\x77\x0A\xE2\x8E\x95\x74\x87\xA5\x2F\x83\x2D\xF7" + "\xA1\xC2\x78\x54\x18\x6E\xDE\x35\xF0\x9F\x7A\xCA\x80\x5C\x83\x5C\x44\xAD\x8B\xE7" + "\x5B\xE2\x63\x7D\x6A\xC7\x98\x97\x29\x28\x31\x3A\x71\x36\x35\x3A\x00\xDC\x1F\xB1" + "\xB3\xD8\x13\xE0\x09\x19\xFD\x1C\x58\xA1\x2B\x02\xB4\xC8\xF2\x1C\xE7\xF9\xC6\x3B" + "\x68\xB9\x72\x43\x86\xEF\xA9\x94\x68\x02\xEF\x7D\x77\xE0\x0A\xD1\xD7\x48\xFD\xCD" + "\x98\xDA\x13\x8A\x76\x48\xD4\x0F\x63\x28\xFA\x01\x1B\xF3\xC7\x15\xB8\x53\x22\x7E" + "\x77\x29\x28\x31\x3A\x75\x36\x35\x3A\x00\xB3\xBB\x4D\xEE\x5A\xAF\xD0\xF2\x56\x8A" + "\x10\x2D\x6F\x4B\x2D\x76\x49\x9B\xE9\xA8\x60\x5D\x9E\x7E\x50\x86\xF1\xA1\x0F\x28" + "\x9B\x7B\xE8\xDD\x1F\x87\x4E\x79\x7B\x50\x12\xA7\xB4\x8B\x52\x38\xEC\x7C\xBB\xB9" + "\x55\x87\x11\x1C\x74\xE7\x7F\xA0\xBA\xE3\x34\x5D\x61\xBF\x29\x29\x29\x00" + }; + + struct key_spec key_ecdsa_valid = + { + "\x28\x31\x31\x3A\x70\x72\x69\x76\x61\x74\x65\x2D\x6B\x65\x79\x28" + "\x35\x3A\x65\x63\x64\x73\x61\x28\x35\x3A\x63\x75\x72\x76\x65\x31" + "\x30\x3A\x4E\x49\x53\x54\x20\x50\x2D\x32\x35\x36\x29\x28\x31\x3A" + "\x71\x36\x35\x3A\x04\x64\x5A\x12\x6F\x86\x7C\x43\x87\x2B\x7C\xAF" + "\x77\xFE\xD8\x22\x31\xEA\xE6\x89\x9F\xAA\xEA\x63\x26\xBC\x49\xED" + "\x85\xC6\xD2\xC9\x8B\x38\xD2\x78\x75\xE6\x1C\x27\x57\x01\xC5\xA1" + "\xE3\xF9\x1F\xBE\xCF\xC1\x72\x73\xFE\xA4\x58\xB6\x6A\x92\x7D\x33" + "\x1D\x02\xC9\xCB\x12\x29\x28\x31\x3A\x64\x33\x33\x3A\x00\x81\x2D" + "\x69\x9A\x5F\x5B\x6F\x2C\x99\x61\x36\x15\x6B\x44\xD8\x06\xC1\x54" + "\xC1\x4C\xFB\x70\x6A\xB6\x64\x81\x78\xF3\x94\x2F\x30\x5D\x29\x29" + "\x28\x37\x3A\x63\x6F\x6D\x6D\x65\x6E\x74\x32\x32\x3A\x2F\x68\x6F" + "\x6D\x65\x2F\x77\x6B\x2F\x2E\x73\x73\x68\x2F\x69\x64\x5F\x65\x63" + "\x64\x73\x61\x29\x29" + }; + + + struct + { + const char *key; + const char *passphrase; + int no_result_expected; + int compare_results; + unsigned char *result_expected; + size_t resultlen_expected; + int ret_expected; + unsigned char *result; + size_t resultlen; + } specs[] = + { + /* Invalid S-Expressions */ + /* - non-NULL */ + { "", + "passphrase", 1, 0, NULL, 0, GPG_ERR_INV_SEXP, NULL, 0 }, + /* - NULL; disabled, this segfaults */ + //{ NULL, + // "passphrase", 1, NULL, 0, GPG_ERR_INV_SEXP, NULL, 0 }, + + /* Valid and invalid keys. */ + { key_rsa_valid.string, + "passphrase", 0, 0, NULL, 0, 0, NULL, 0 }, + { key_rsa_bogus_0.string, + "passphrase", 0, 0, NULL, 0, GPG_ERR_INV_SEXP, NULL, 0 }, + { key_rsa_bogus_1.string, + "passphrase", 0, 0, NULL, 0, GPG_ERR_INV_SEXP, NULL, 0 }, + + { key_ecdsa_valid.string, + "passphrase", 0, 0, NULL, 0, 0, NULL, 0 }, + + /* FIXME: add more test data. */ + }; + + for (i = 0; i < DIM (specs); i++) + { + ret = agent_protect ((const unsigned char*)specs[i].key, + specs[i].passphrase, + &specs[i].result, &specs[i].resultlen, 0, -1); + if (gpg_err_code (ret) != specs[i].ret_expected) + { + printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n", + i, ret, gpg_strerror (ret), + specs[i].ret_expected, gpg_strerror (specs[i].ret_expected)); + abort (); + } + + if (specs[i].no_result_expected) + { + assert (! specs[i].result); + assert (! specs[i].resultlen); + } + else + { + if (specs[i].compare_results) + { + assert (specs[i].resultlen == specs[i].resultlen_expected); + if (specs[i].result_expected) + assert (! memcmp (specs[i].result, specs[i].result_expected, + specs[i].resultlen)); + else + assert (! specs[i].result); + } + xfree (specs[i].result); + } + } +} + + +static void +test_agent_unprotect (void) +{ + /* Unprotect the key encoded in canonical format. We assume a valid + S-Exp here. */ +/* int */ +/* agent_unprotect (const unsigned char *protectedkey, const char *passphrase, */ +/* unsigned char **result, size_t *resultlen) */ +} + + +static void +test_agent_private_key_type (void) +{ +/* Check the type of the private key, this is one of the constants: + PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the + value 0), PRIVATE_KEY_CLEAR for an unprotected private key. + PRIVATE_KEY_PROTECTED for an protected private key or + PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored + elsewhere. */ +/* int */ +/* agent_private_key_type (const unsigned char *privatekey) */ +} + + +static void +test_make_shadow_info (void) +{ +#if 0 + static struct + { + const char *snstr; + const char *idstr; + const char *expected; + } data[] = { + { "", "", NULL }, + + }; + int i; + unsigned char *result; + + for (i=0; i < DIM(data); i++) + { + result = make_shadow_info (data[i].snstr, data[i].idstr); + if (!result && !data[i].expected) + pass (); + else if (!result && data[i].expected) + fail (); + else if (!data[i].expected) + fail (); + /* fixme: Need to compare the result but also need to check + proper S-expression syntax. */ + } +#endif +} + + + +static void +test_agent_shadow_key (void) +{ +/* Create a shadow key from a public key. We use the shadow protocol + "ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting + S-expression is returned in an allocated buffer RESULT will point + to. The input parameters are expected to be valid canonicalized + S-expressions */ +/* int */ +/* agent_shadow_key (const unsigned char *pubkey, */ +/* const unsigned char *shadow_info, */ +/* unsigned char **result) */ +} + + +static void +test_agent_get_shadow_info (void) +{ +/* Parse a canonical encoded shadowed key and return a pointer to the + inner list with the shadow_info */ +/* int */ +/* agent_get_shadow_info (const unsigned char *shadowkey, */ +/* unsigned char const **shadow_info) */ +} + + +static void +test_agent_protect_shared_secret (void) +{ + +} + + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + gcry_control (GCRYCTL_DISABLE_SECMEM); + + test_agent_protect (); + test_agent_unprotect (); + test_agent_private_key_type (); + test_make_shadow_info (); + test_agent_shadow_key (); + test_agent_get_shadow_info (); + test_agent_protect_shared_secret (); + + return 0; +} + +/* Stub function. */ +gpg_error_t +convert_from_openpgp_native (gcry_sexp_t s_pgp, const char *passphrase, + unsigned char **r_key) +{ + (void)s_pgp; + (void)passphrase; + (void)r_key; + return gpg_error (GPG_ERR_BUG); +} diff --git a/agent/trans.c b/agent/trans.c new file mode 100644 index 0000000..ff1a34e --- /dev/null +++ b/agent/trans.c @@ -0,0 +1,41 @@ +/* trans.c - translatable strings + * Copyright (C) 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 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, see . + */ + +/* To avoid any problems with the gettext implementation (there used + to be some vulnerabilities in the last years and the use of + external files is a minor security problem in itself), we use our + own simple translation stuff */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" + +const char * +trans (const char *text) +{ + return text; +} diff --git a/agent/trustlist.c b/agent/trustlist.c new file mode 100644 index 0000000..9d33259 --- /dev/null +++ b/agent/trustlist.c @@ -0,0 +1,825 @@ +/* trustlist.c - Maintain the list of trusted keys + * Copyright (C) 2002, 2004, 2006, 2007, 2009, + * 2012 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" +#include /* fixme: need a way to avoid assuan calls here */ +#include "i18n.h" + + +/* A structure to store the information from the trust file. */ +struct trustitem_s +{ + struct + { + int disabled:1; /* This entry is disabled. */ + int for_pgp:1; /* Set by '*' or 'P' as first flag. */ + int for_smime:1; /* Set by '*' or 'S' as first flag. */ + int relax:1; /* Relax checking of root certificate + constraints. */ + int cm:1; /* Use chain model for validation. */ + } flags; + unsigned char fpr[20]; /* The binary fingerprint. */ +}; +typedef struct trustitem_s trustitem_t; + +/* Malloced table and its allocated size with all trust items. */ +static trustitem_t *trusttable; +static size_t trusttablesize; +/* A mutex used to protect the table. */ +static npth_mutex_t trusttable_lock; + + +static const char headerblurb[] = +"# This is the list of trusted keys. Comment lines, like this one, as\n" +"# well as empty lines are ignored. Lines have a length limit but this\n" +"# is not a serious limitation as the format of the entries is fixed and\n" +"# checked by gpg-agent. A non-comment line starts with optional white\n" +"# space, followed by the SHA-1 fingerpint in hex, followed by a flag\n" +"# which may be one of 'P', 'S' or '*' and optionally followed by a list of\n" +"# other flags. The fingerprint may be prefixed with a '!' to mark the\n" +"# key as not trusted. You should give the gpg-agent a HUP or run the\n" +"# command \"gpgconf --reload gpg-agent\" after changing this file.\n" +"\n\n" +"# Include the default trust list\n" +"include-default\n" +"\n"; + + +/* This function must be called once to initialize this module. This + has to be done before a second thread is spawned. We can't do the + static initialization because Pth emulation code might not be able + to do a static init; in particular, it is not possible for W32. */ +void +initialize_module_trustlist (void) +{ + static int initialized; + int err; + + if (!initialized) + { + err = npth_mutex_init (&trusttable_lock, NULL); + if (err) + log_fatal ("failed to init mutex in %s: %s\n", __FILE__,strerror (err)); + initialized = 1; + } +} + + + + +static void +lock_trusttable (void) +{ + int err; + + err = npth_mutex_lock (&trusttable_lock); + if (err) + log_fatal ("failed to acquire mutex in %s: %s\n", __FILE__, strerror (err)); +} + + +static void +unlock_trusttable (void) +{ + int err; + + err = npth_mutex_unlock (&trusttable_lock); + if (err) + log_fatal ("failed to release mutex in %s: %s\n", __FILE__, strerror (err)); +} + + +/* Clear the trusttable. The caller needs to make sure that the + trusttable is locked. */ +static inline void +clear_trusttable (void) +{ + xfree (trusttable); + trusttable = NULL; + trusttablesize = 0; +} + + +static gpg_error_t +read_one_trustfile (const char *fname, int allow_include, + trustitem_t **addr_of_table, + size_t *addr_of_tablesize, + int *addr_of_tableidx) +{ + gpg_error_t err = 0; + estream_t fp; + int n, c; + char *p, line[256]; + trustitem_t *table, *ti; + int tableidx; + size_t tablesize; + int lnr = 0; + + table = *addr_of_table; + tablesize = *addr_of_tablesize; + tableidx = *addr_of_tableidx; + + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err)); + goto leave; + } + + while (es_fgets (line, DIM(line)-1, fp)) + { + lnr++; + + n = strlen (line); + if (!n || line[n-1] != '\n') + { + /* Eat until end of line. */ + while ( (c=es_getc (fp)) != EOF && c != '\n') + ; + err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + log_error (_("file '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + continue; + } + line[--n] = 0; /* Chop the LF. */ + if (n && line[n-1] == '\r') + line[--n] = 0; /* Chop an optional CR. */ + + /* Allow for empty lines and spaces */ + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '#') + continue; + + if (!strncmp (p, "include-default", 15) + && (!p[15] || spacep (p+15))) + { + char *etcname; + gpg_error_t err2; + + if (!allow_include) + { + log_error (_("statement \"%s\" ignored in '%s', line %d\n"), + "include-default", fname, lnr); + continue; + } + /* fixme: Should check for trailing garbage. */ + + etcname = make_filename (gnupg_sysconfdir (), "trustlist.txt", NULL); + if ( !strcmp (etcname, fname) ) /* Same file. */ + log_info (_("statement \"%s\" ignored in '%s', line %d\n"), + "include-default", fname, lnr); + else if ( access (etcname, F_OK) && errno == ENOENT ) + { + /* A non existent system trustlist is not an error. + Just print a note. */ + log_info (_("system trustlist '%s' not available\n"), etcname); + } + else + { + err2 = read_one_trustfile (etcname, 0, + &table, &tablesize, &tableidx); + if (err2) + err = err2; + } + xfree (etcname); + + continue; + } + + if (tableidx == tablesize) /* Need more space. */ + { + trustitem_t *tmp; + size_t tmplen; + + tmplen = tablesize + 20; + tmp = xtryrealloc (table, tmplen * sizeof *table); + if (!tmp) + { + err = gpg_error_from_syserror (); + goto leave; + } + table = tmp; + tablesize = tmplen; + } + + ti = table + tableidx; + + memset (&ti->flags, 0, sizeof ti->flags); + if (*p == '!') + { + ti->flags.disabled = 1; + p++; + while (spacep (p)) + p++; + } + + n = hexcolon2bin (p, ti->fpr, 20); + if (n < 0) + { + log_error (_("bad fingerprint in '%s', line %d\n"), fname, lnr); + err = gpg_error (GPG_ERR_BAD_DATA); + continue; + } + p += n; + for (; spacep (p); p++) + ; + + /* Process the first flag which needs to be the first for + backward compatibility. */ + if (!*p || *p == '*' ) + { + ti->flags.for_smime = 1; + ti->flags.for_pgp = 1; + } + else if ( *p == 'P' || *p == 'p') + { + ti->flags.for_pgp = 1; + } + else if ( *p == 'S' || *p == 's') + { + ti->flags.for_smime = 1; + } + else + { + log_error (_("invalid keyflag in '%s', line %d\n"), fname, lnr); + err = gpg_error (GPG_ERR_BAD_DATA); + continue; + } + p++; + if ( *p && !spacep (p) ) + { + log_error (_("invalid keyflag in '%s', line %d\n"), fname, lnr); + err = gpg_error (GPG_ERR_BAD_DATA); + continue; + } + + /* Now check for more key-value pairs of the form NAME[=VALUE]. */ + while (*p) + { + for (; spacep (p); p++) + ; + if (!*p) + break; + n = strcspn (p, "= \t"); + if (p[n] == '=') + { + log_error ("assigning a value to a flag is not yet supported; " + "in '%s', line %d\n", fname, lnr); + err = gpg_error (GPG_ERR_BAD_DATA); + p++; + } + else if (n == 5 && !memcmp (p, "relax", 5)) + ti->flags.relax = 1; + else if (n == 2 && !memcmp (p, "cm", 2)) + ti->flags.cm = 1; + else + log_error ("flag '%.*s' in '%s', line %d ignored\n", + n, p, fname, lnr); + p += n; + } + tableidx++; + } + if ( !err && !es_feof (fp) ) + { + err = gpg_error_from_syserror (); + log_error (_("error reading '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + } + + leave: + es_fclose (fp); + *addr_of_table = table; + *addr_of_tablesize = tablesize; + *addr_of_tableidx = tableidx; + return err; +} + + +/* Read the trust files and update the global table on success. The + trusttable is assumed to be locked. */ +static gpg_error_t +read_trustfiles (void) +{ + gpg_error_t err; + trustitem_t *table, *ti; + int tableidx; + size_t tablesize; + char *fname; + int allow_include = 1; + + tablesize = 20; + table = xtrycalloc (tablesize, sizeof *table); + if (!table) + return gpg_error_from_syserror (); + tableidx = 0; + + fname = make_filename_try (gnupg_homedir (), "trustlist.txt", NULL); + if (!fname) + { + err = gpg_error_from_syserror (); + xfree (table); + return err; + } + + if ( access (fname, F_OK) ) + { + if ( errno == ENOENT ) + ; /* Silently ignore a non-existing trustfile. */ + else + { + err = gpg_error_from_syserror (); + log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err)); + } + xfree (fname); + fname = make_filename (gnupg_sysconfdir (), "trustlist.txt", NULL); + allow_include = 0; + } + err = read_one_trustfile (fname, allow_include, + &table, &tablesize, &tableidx); + xfree (fname); + + if (err) + { + xfree (table); + if (gpg_err_code (err) == GPG_ERR_ENOENT) + { + /* Take a missing trustlist as an empty one. */ + clear_trusttable (); + err = 0; + } + return err; + } + + /* Fixme: we should drop duplicates and sort the table. */ + ti = xtryrealloc (table, (tableidx?tableidx:1) * sizeof *table); + if (!ti) + { + err = gpg_error_from_syserror (); + xfree (table); + return err; + } + + /* Replace the trusttable. */ + xfree (trusttable); + trusttable = ti; + trusttablesize = tableidx; + return 0; +} + + +/* Check whether the given fpr is in our trustdb. We expect FPR to be + an all uppercase hexstring of 40 characters. If ALREADY_LOCKED is + true the function assumes that the trusttable is already locked. */ +static gpg_error_t +istrusted_internal (ctrl_t ctrl, const char *fpr, int *r_disabled, + int already_locked) +{ + gpg_error_t err = 0; + int locked = already_locked; + trustitem_t *ti; + size_t len; + unsigned char fprbin[20]; + + if (r_disabled) + *r_disabled = 0; + + if ( hexcolon2bin (fpr, fprbin, 20) < 0 ) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + + if (!already_locked) + { + lock_trusttable (); + locked = 1; + } + + if (!trusttable) + { + err = read_trustfiles (); + if (err) + { + log_error (_("error reading list of trusted root certificates\n")); + goto leave; + } + } + + if (trusttable) + { + for (ti=trusttable, len = trusttablesize; len; ti++, len--) + if (!memcmp (ti->fpr, fprbin, 20)) + { + if (ti->flags.disabled && r_disabled) + *r_disabled = 1; + + /* Print status messages only if we have not been called + in a locked state. */ + if (already_locked) + ; + else if (ti->flags.relax) + { + unlock_trusttable (); + locked = 0; + err = agent_write_status (ctrl, "TRUSTLISTFLAG", "relax", NULL); + } + else if (ti->flags.cm) + { + unlock_trusttable (); + locked = 0; + err = agent_write_status (ctrl, "TRUSTLISTFLAG", "cm", NULL); + } + + if (!err) + err = ti->flags.disabled? gpg_error (GPG_ERR_NOT_TRUSTED) : 0; + goto leave; + } + } + err = gpg_error (GPG_ERR_NOT_TRUSTED); + + leave: + if (locked && !already_locked) + unlock_trusttable (); + return err; +} + + +/* Check whether the given fpr is in our trustdb. We expect FPR to be + an all uppercase hexstring of 40 characters. */ +gpg_error_t +agent_istrusted (ctrl_t ctrl, const char *fpr, int *r_disabled) +{ + return istrusted_internal (ctrl, fpr, r_disabled, 0); +} + + +/* Write all trust entries to FP. */ +gpg_error_t +agent_listtrusted (void *assuan_context) +{ + trustitem_t *ti; + char key[51]; + gpg_error_t err; + size_t len; + + lock_trusttable (); + if (!trusttable) + { + err = read_trustfiles (); + if (err) + { + unlock_trusttable (); + log_error (_("error reading list of trusted root certificates\n")); + return err; + } + } + + if (trusttable) + { + for (ti=trusttable, len = trusttablesize; len; ti++, len--) + { + if (ti->flags.disabled) + continue; + bin2hex (ti->fpr, 20, key); + key[40] = ' '; + key[41] = ((ti->flags.for_smime && ti->flags.for_pgp)? '*' + : ti->flags.for_smime? 'S': ti->flags.for_pgp? 'P':' '); + key[42] = '\n'; + assuan_send_data (assuan_context, key, 43); + assuan_send_data (assuan_context, NULL, 0); /* flush */ + } + } + + unlock_trusttable (); + return 0; +} + + +/* Create a copy of string with colons inserted after each two bytes. + Caller needs to release the string. In case of a memory failure, + NULL is returned. */ +static char * +insert_colons (const char *string) +{ + char *buffer, *p; + size_t n = strlen (string); + size_t nnew = n + (n+1)/2; + + p = buffer = xtrymalloc ( nnew + 1 ); + if (!buffer) + return NULL; + while (*string) + { + *p++ = *string++; + if (*string) + { + *p++ = *string++; + if (*string) + *p++ = ':'; + } + } + *p = 0; + assert (strlen (buffer) <= nnew); + + return buffer; +} + + +/* To pretty print DNs in the Pinentry, we replace slashes by + REPLSTRING. The caller needs to free the returned string. NULL is + returned on error with ERRNO set. */ +static char * +reformat_name (const char *name, const char *replstring) +{ + const char *s; + char *newname; + char *d; + size_t count; + size_t replstringlen = strlen (replstring); + + /* If the name does not start with a slash it is not a preformatted + DN and thus we don't bother to reformat it. */ + if (*name != '/') + return xtrystrdup (name); + + /* Count the names. Note that a slash contained in a DN part is + expected to be C style escaped and thus the slashes we see here + are the actual part delimiters. */ + for (s=name+1, count=0; *s; s++) + if (*s == '/') + count++; + newname = xtrymalloc (strlen (name) + count*replstringlen + 1); + if (!newname) + return NULL; + for (s=name+1, d=newname; *s; s++) + if (*s == '/') + d = stpcpy (d, replstring); + else + *d++ = *s; + *d = 0; + return newname; +} + + +/* Insert the given fpr into our trustdb. We expect FPR to be an all + uppercase hexstring of 40 characters. FLAG is either 'P' or 'C'. + This function does first check whether that key has already been + put into the trustdb and returns success in this case. Before a + FPR actually gets inserted, the user is asked by means of the + Pinentry whether this is actual what he wants to do. */ +gpg_error_t +agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag) +{ + gpg_error_t err = 0; + char *desc; + char *fname; + estream_t fp; + char *fprformatted; + char *nameformatted; + int is_disabled; + int yes_i_trust; + + /* Check whether we are at all allowed to modify the trustlist. + This is useful so that the trustlist may be a symlink to a global + trustlist with only admin priviliges to modify it. Of course + this is not a secure way of denying access, but it avoids the + usual clicking on an Okay button most users are used to. */ + fname = make_filename_try (gnupg_homedir (), "trustlist.txt", NULL); + if (!fname) + return gpg_error_from_syserror (); + + if ( access (fname, W_OK) && errno != ENOENT) + { + xfree (fname); + return gpg_error (GPG_ERR_EPERM); + } + xfree (fname); + + if (!agent_istrusted (ctrl, fpr, &is_disabled)) + { + return 0; /* We already got this fingerprint. Silently return + success. */ + } + + /* This feature must explicitly been enabled. */ + if (!opt.allow_mark_trusted) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (is_disabled) + { + /* There is an disabled entry in the trustlist. Return an error + so that the user won't be asked again for that one. Changing + this flag with the integrated marktrusted feature is and will + not be made possible. */ + return gpg_error (GPG_ERR_NOT_TRUSTED); + } + + + /* Insert a new one. */ + nameformatted = reformat_name (name, "%0A "); + if (!nameformatted) + return gpg_error_from_syserror (); + + /* First a general question whether this is trusted. */ + desc = xtryasprintf ( + /* TRANSLATORS: This prompt is shown by the Pinentry + and has one special property: A "%%0A" is used by + Pinentry to insert a line break. The double + percent sign is actually needed because it is also + a printf format string. If you need to insert a + plain % sign, you need to encode it as "%%25". The + "%s" gets replaced by the name as stored in the + certificate. */ + L_("Do you ultimately trust%%0A" + " \"%s\"%%0A" + "to correctly certify user certificates?"), + nameformatted); + if (!desc) + { + xfree (nameformatted); + return out_of_core (); + } + err = agent_get_confirmation (ctrl, desc, L_("Yes"), L_("No"), 1); + xfree (desc); + if (!err) + yes_i_trust = 1; + else if (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED) + yes_i_trust = 0; + else + { + xfree (nameformatted); + return err; + } + + + fprformatted = insert_colons (fpr); + if (!fprformatted) + { + xfree (nameformatted); + return out_of_core (); + } + + /* If the user trusts this certificate he has to verify the + fingerprint of course. */ + if (yes_i_trust) + { + desc = xtryasprintf + ( + /* TRANSLATORS: This prompt is shown by the Pinentry and has + one special property: A "%%0A" is used by Pinentry to + insert a line break. The double percent sign is actually + needed because it is also a printf format string. If you + need to insert a plain % sign, you need to encode it as + "%%25". The second "%s" gets replaced by a hexdecimal + fingerprint string whereas the first one receives the name + as stored in the certificate. */ + L_("Please verify that the certificate identified as:%%0A" + " \"%s\"%%0A" + "has the fingerprint:%%0A" + " %s"), nameformatted, fprformatted); + if (!desc) + { + xfree (fprformatted); + xfree (nameformatted); + return out_of_core (); + } + + /* TRANSLATORS: "Correct" is the label of a button and intended + to be hit if the fingerprint matches the one of the CA. The + other button is "the default "Cancel" of the Pinentry. */ + err = agent_get_confirmation (ctrl, desc, L_("Correct"), L_("Wrong"), 1); + xfree (desc); + if (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED) + yes_i_trust = 0; + else if (err) + { + xfree (fprformatted); + xfree (nameformatted); + return err; + } + } + + + /* Now check again to avoid duplicates. We take the lock to make + sure that nobody else plays with our file and force a reread. */ + lock_trusttable (); + clear_trusttable (); + if (!istrusted_internal (ctrl, fpr, &is_disabled, 1) || is_disabled) + { + unlock_trusttable (); + xfree (fprformatted); + xfree (nameformatted); + return is_disabled? gpg_error (GPG_ERR_NOT_TRUSTED) : 0; + } + + fname = make_filename_try (gnupg_homedir (), "trustlist.txt", NULL); + if (!fname) + { + err = gpg_error_from_syserror (); + unlock_trusttable (); + xfree (fprformatted); + xfree (nameformatted); + return err; + } + if ( access (fname, F_OK) && errno == ENOENT) + { + fp = es_fopen (fname, "wx,mode=-rw-r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("can't create '%s': %s\n", fname, gpg_strerror (err)); + xfree (fname); + unlock_trusttable (); + xfree (fprformatted); + xfree (nameformatted); + return err; + } + es_fputs (headerblurb, fp); + es_fclose (fp); + } + fp = es_fopen (fname, "a+,mode=-rw-r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("can't open '%s': %s\n", fname, gpg_strerror (err)); + xfree (fname); + unlock_trusttable (); + xfree (fprformatted); + xfree (nameformatted); + return err; + } + + /* Append the key. */ + es_fputs ("\n# ", fp); + xfree (nameformatted); + nameformatted = reformat_name (name, "\n# "); + if (!nameformatted || strchr (name, '\n')) + { + /* Note that there should never be a LF in NAME but we better + play safe and print a sanitized version in this case. */ + es_write_sanitized (fp, name, strlen (name), NULL, NULL); + } + else + es_fputs (nameformatted, fp); + es_fprintf (fp, "\n%s%s %c%s\n", yes_i_trust?"":"!", fprformatted, flag, + flag == 'S'? " relax":""); + if (es_ferror (fp)) + err = gpg_error_from_syserror (); + + if (es_fclose (fp)) + err = gpg_error_from_syserror (); + + clear_trusttable (); + xfree (fname); + unlock_trusttable (); + xfree (fprformatted); + xfree (nameformatted); + if (!err) + bump_key_eventcounter (); + return err; +} + + +/* This function may be called to force reloading of the + trustlist. */ +void +agent_reload_trustlist (void) +{ + /* All we need to do is to delete the trusttable. At the next + access it will get re-read. */ + lock_trusttable (); + clear_trusttable (); + unlock_trusttable (); + bump_key_eventcounter (); +} diff --git a/am/cmacros.am b/am/cmacros.am new file mode 100644 index 0000000..9610e4e --- /dev/null +++ b/am/cmacros.am @@ -0,0 +1,81 @@ +# cmacros.am - C macro definitions +# 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 3 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, see . + +localedir = $(datadir)/locale + +# NB: AM_CFLAGS may also be used by tools running on the build +# platform to create source files. +AM_CPPFLAGS += -DLOCALEDIR=\"$(localedir)\" + +if ! HAVE_DOSISH_SYSTEM +AM_CPPFLAGS += -DGNUPG_BINDIR="\"$(bindir)\"" \ + -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ + -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ + -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \ + -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \ + -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\"" +endif + + +# If a specific protect tool program has been defined, pass its name +# to cc. Note that these macros should not be used directly but via +# the gnupg_module_name function. +if GNUPG_AGENT_PGM +AM_CPPFLAGS += -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\"" +endif +if GNUPG_PINENTRY_PGM +AM_CPPFLAGS += -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\"" +endif +if GNUPG_SCDAEMON_PGM +AM_CPPFLAGS += -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\"" +endif +if GNUPG_DIRMNGR_PGM +AM_CPPFLAGS += -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\"" +endif +if GNUPG_PROTECT_TOOL_PGM +AM_CPPFLAGS += -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\"" +endif +if GNUPG_DIRMNGR_LDAP_PGM +AM_CPPFLAGS += -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\"" +endif + +# Under Windows we use LockFileEx. WindowsCE provides this only on +# the WindowsMobile 6 platform and thus we need to use the coredll6 +# import library. We also want to use a stacksize of 256k instead of +# the 2MB which is the default with cegcc. 256k is the largest stack +# we use with pth. +if HAVE_W32CE_SYSTEM +extra_sys_libs = -lcoredll6 +extra_bin_ldflags = -Wl,--stack=0x40000 +else +extra_sys_libs = +extra_bin_ldflags = +endif + +if HAVE_W32_SYSTEM +.rc.o: + $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" + +endif +resource_objs = + +# Convenience macros +libcommon = ../common/libcommon.a +libcommonpth = ../common/libcommonpth.a +libcommontls = ../common/libcommontls.a +libcommontlsnpth = ../common/libcommontlsnpth.a diff --git a/autogen.rc b/autogen.rc new file mode 100644 index 0000000..3694817 --- /dev/null +++ b/autogen.rc @@ -0,0 +1,44 @@ +# autogen.sh configuration for GnuPG -*- sh -*- + +#version_parts=3 + +case "$myhost:$myhostsub" in + w32:ce) + extraoptions="--enable-dirmngr-auto-start --disable-scdaemon " + extraoptions="$extraoptions --disable-zip --enable-gpg2-is-gpg" + ;; + w32:) + extraoptions="--enable-gpgtar --enable-gpg2-is-gpg" + ;; +esac + +case "$myhost" in + w32) + configure_opts=" + --with-gpg-error-prefix=@SYSROOT@ + --with-ksba-prefix=@SYSROOT@ + --with-libgcrypt-prefix=@SYSROOT@ + --with-libassuan-prefix=@SYSROOT@ + --with-zlib=@SYSROOT@ + --with-regex=@SYSROOT@ + --with-npth-prefix=@SYSROOT@ + --disable-g13 + " + ;; + + amd64) + configure_opts=" + --with-gpg-error-prefix=@SYSROOT@ + --with-ksba-prefix=@SYSROOT@ + --with-libgcrypt-prefix=@SYSROOT@ + --with-libassuan-prefix=@SYSROOT@ + --with-zlib=/usr/x86_64-linux-gnu/usr + --with-pth-prefix=/usr/x86_64-linux-gnu/usr + " + ;; +esac + + +extra_aclocal_flags="" + +final_info="./configure --sysconfdir=/etc --enable-maintainer-mode && make" diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..92c6df8 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,469 @@ +#! /bin/sh +# autogen.sh +# Copyright (C) 2003, 2014 g10 Code GmbH +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This 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. +# +# This is a generic script to create the configure script and handle cross +# build environments. It requires the presence of a autogen.rc file to +# configure it for the respective package. It is maintained as part of +# GnuPG and source copied by other packages. +# +# Version: 2014-06-06 + +configure_ac="configure.ac" + +cvtver () { + awk 'NR==1 {split($NF,A,".");X=1000000*A[1]+1000*A[2]+A[3];print X;exit 0}' +} + +check_version () { + if [ $(( `("$1" --version || echo "0") | cvtver` >= $2 )) = 1 ]; then + return 0 + fi + echo "**Error**: "\`$1\'" not installed or too old." >&2 + echo ' Version '$3' or newer is required.' >&2 + [ -n "$4" ] && echo ' Note that this is part of '\`$4\''.' >&2 + DIE="yes" + return 1 +} + +fatal () { + echo "autogen.sh:" "$*" >&2 + DIE=yes +} + +info () { + if [ -z "${SILENT}" ]; then + echo "autogen.sh:" "$*" >&2 + fi +} + +die_p () { + if [ "$DIE" = "yes" ]; then + echo "autogen.sh: Stop." >&2 + exit 1 + fi +} + +replace_sysroot () { + configure_opts=$(echo $configure_opts | sed "s#@SYSROOT@#${w32root}#g") + extraoptions=$(echo $extraoptions | sed "s#@SYSROOT@#${w32root}#g") +} + +# Allow to override the default tool names +AUTOCONF=${AUTOCONF_PREFIX}${AUTOCONF:-autoconf}${AUTOCONF_SUFFIX} +AUTOHEADER=${AUTOCONF_PREFIX}${AUTOHEADER:-autoheader}${AUTOCONF_SUFFIX} + +AUTOMAKE=${AUTOMAKE_PREFIX}${AUTOMAKE:-automake}${AUTOMAKE_SUFFIX} +ACLOCAL=${AUTOMAKE_PREFIX}${ACLOCAL:-aclocal}${AUTOMAKE_SUFFIX} + +GETTEXT=${GETTEXT_PREFIX}${GETTEXT:-gettext}${GETTEXT_SUFFIX} +MSGMERGE=${GETTEXT_PREFIX}${MSGMERGE:-msgmerge}${GETTEXT_SUFFIX} + +DIE=no +FORCE= +SILENT= +PRINT_HOST=no +PRINT_BUILD=no +tmp=$(dirname "$0") +tsdir=$(cd "${tmp}"; pwd) +version_parts=3 + +if [ -n "${AUTOGEN_SH_SILENT}" ]; then + SILENT=" --silent" +fi +if test x"$1" = x"--help"; then + echo "usage: ./autogen.sh [--silent] [--force] [--build-TYPE] [ARGS]" + exit 0 +fi +if test x"$1" = x"--silent"; then + SILENT=" --silent" + shift +fi +if test x"$1" = x"--force"; then + FORCE=" --force" + shift +fi +if test x"$1" = x"--print-host"; then + PRINT_HOST=yes + shift +fi +if test x"$1" = x"--print-build"; then + PRINT_BUILD=yes + shift +fi + + +# Reject unsafe characters in $HOME, $tsdir and cwd. We consider spaces +# as unsafe because it is too easy to get scripts wrong in this regard. +am_lf=' +' +case `pwd` in + *[\;\\\"\#\$\&\'\`$am_lf\ \ ]*) + fatal "unsafe working directory name" ;; +esac +case $tsdir in + *[\;\\\"\#\$\&\'\`$am_lf\ \ ]*) + fatal "unsafe source directory: \`$tsdir'" ;; +esac +case $HOME in + *[\;\\\"\#\$\&\'\`$am_lf\ \ ]*) + fatal "unsafe home directory: \`$HOME'" ;; +esac +die_p + + +# List of variables sourced from autogen.rc. The strings '@SYSROOT@' in +# these variables are replaced by the actual system root. +configure_opts= +extraoptions= +# List of optional variables sourced from autogen.rc and ~/.gnupg-autogen.rc +w32_toolprefixes= +w32_extraoptions= +w32ce_toolprefixes= +w32ce_extraoptions= +w64_toolprefixes= +w64_extraoptions= +amd64_toolprefixes= +# End list of optional variables sourced from ~/.gnupg-autogen.rc +# What follows are variables which are sourced but default to +# environment variables or lacking them hardcoded values. +#w32root= +#w32ce_root= +#w64root= +#amd64root= + +# Convenience option to use certain configure options for some hosts. +myhost="" +myhostsub="" +case "$1" in + --find-version) + myhost="find-version" + SILENT=" --silent" + shift + ;; + --build-w32) + myhost="w32" + shift + ;; + --build-w32ce) + myhost="w32" + myhostsub="ce" + shift + ;; + --build-w64) + myhost="w32" + myhostsub="64" + shift + ;; + --build-amd64) + myhost="amd64" + shift + ;; + --build*) + fatal "**Error**: invalid build option $1" + shift + ;; + *) + ;; +esac +die_p + + +# Source our configuration +if [ -f "${tsdir}/autogen.rc" ]; then + . "${tsdir}/autogen.rc" +fi + +# Source optional site specific configuration +if [ -f "$HOME/.gnupg-autogen.rc" ]; then + info "sourcing extra definitions from $HOME/.gnupg-autogen.rc" + . "$HOME/.gnupg-autogen.rc" +fi + + +# **** FIND VERSION **** +# This is a helper for the configure.ac M4 magic +# Called +# ./autogen.sh --find-version PACKAGE MAJOR MINOR [MICRO] +# returns a complete version string with automatic beta numbering. +if [ "$myhost" = "find-version" ]; then + package="$1" + major="$2" + minor="$3" + micro="$4" + + case "$version_parts" in + 2) + matchstr1="$package-$major.[0-9]*" + matchstr2="$package-$major-base" + vers="$major.$minor" + ;; + *) + matchstr1="$package-$major.$minor.[0-9]*" + matchstr2="$package-$major.$minor-base" + vers="$major.$minor.$micro" + ;; + esac + + beta=no + if [ -e .git ]; then + ingit=yes + tmp=$(git describe --match "${matchstr1}" --long 2>/dev/null) + if [ -n "$tmp" ]; then + tmp=$(echo "$tmp"|awk -F- '$3!=0 && $3 !~ /^beta/ {print"-beta"$3}') + else + tmp=$(git describe --match "${matchstr2}" --long 2>/dev/null \ + | awk -F- '$4!=0{print"-beta"$4}') + fi + [ -n "$tmp" ] && beta=yes + rev=$(git rev-parse --short HEAD | tr -d '\n\r') + rvd=$((0x$(echo ${rev} | head -c 4))) + else + ingit=no + beta=yes + tmp="-unknown" + rev="0000000" + rvd="0" + fi + + echo "$package-$vers$tmp:$beta:$ingit:$vers$tmp:$vers:$tmp:$rev:$rvd:" + exit 0 +fi +# **** end FIND VERSION **** + + +if [ ! -f "$tsdir/build-aux/config.guess" ]; then + fatal "$tsdir/build-aux/config.guess not found" + exit 1 +fi +build=`$tsdir/build-aux/config.guess` +if [ $PRINT_BUILD = yes ]; then + echo "$build" + exit 0 +fi + + + +# ****************** +# W32 build script +# ****************** +if [ "$myhost" = "w32" ]; then + case $myhostsub in + ce) + w32root="$w32ce_root" + [ -z "$w32root" ] && w32root="$HOME/w32ce_root" + toolprefixes="$w32ce_toolprefixes arm-mingw32ce" + extraoptions="$extraoptions $w32ce_extraoptions" + ;; + 64) + w32root="$w64root" + [ -z "$w32root" ] && w32root="$HOME/w64root" + toolprefixes="$w64_toolprefixes x86_64-w64-mingw32" + extraoptions="$extraoptions $w64_extraoptions" + ;; + *) + [ -z "$w32root" ] && w32root="$HOME/w32root" + toolprefixes="$w32_toolprefixes i686-w64-mingw32 i586-mingw32msvc" + toolprefixes="$toolprefixes i386-mingw32msvc mingw32" + extraoptions="$extraoptions $w32_extraoptions" + ;; + esac + info "Using $w32root as standard install directory" + replace_sysroot + + # Locate the cross compiler + crossbindir= + for host in $toolprefixes; do + if ${host}-gcc --version >/dev/null 2>&1 ; then + crossbindir=/usr/${host}/bin + conf_CC="CC=${host}-gcc" + break; + fi + done + if [ -z "$crossbindir" ]; then + fatal "cross compiler kit not installed" + if [ -z "$myhostsub" ]; then + info "Under Debian GNU/Linux, you may install it using" + info " apt-get install mingw32 mingw32-runtime mingw32-binutils" + fi + die_p + fi + if [ $PRINT_HOST = yes ]; then + echo "$host" + exit 0 + fi + + if [ -f "$tsdir/config.log" ]; then + if ! head $tsdir/config.log | grep "$host" >/dev/null; then + fatal "Please run a 'make distclean' first" + die_p + fi + fi + + $tsdir/configure --enable-maintainer-mode ${SILENT} \ + --prefix=${w32root} \ + --host=${host} --build=${build} SYSROOT=${w32root} \ + PKG_CONFIG_LIBDIR=${w32root}/lib/pkgconfig \ + ${configure_opts} ${extraoptions} "$@" + rc=$? + exit $rc +fi +# ***** end W32 build script ******* + +# ***** AMD64 cross build script ******* +# Used to cross-compile for AMD64 (for testing) +if [ "$myhost" = "amd64" ]; then + [ -z "$amd64root" ] && amd64root="$HOME/amd64root" + info "Using $amd64root as standard install directory" + replace_sysroot + + toolprefixes="$amd64_toolprefixes x86_64-linux-gnu amd64-linux-gnu" + + # Locate the cross compiler + crossbindir= + for host in $toolprefixes ; do + if ${host}-gcc --version >/dev/null 2>&1 ; then + crossbindir=/usr/${host}/bin + conf_CC="CC=${host}-gcc" + break; + fi + done + if [ -z "$crossbindir" ]; then + echo "Cross compiler kit not installed" >&2 + echo "Stop." >&2 + exit 1 + fi + if [ $PRINT_HOST = yes ]; then + echo "$host" + exit 0 + fi + + if [ -f "$tsdir/config.log" ]; then + if ! head $tsdir/config.log | grep "$host" >/dev/null; then + echo "Please run a 'make distclean' first" >&2 + exit 1 + fi + fi + + $tsdir/configure --enable-maintainer-mode ${SILENT} \ + --prefix=${amd64root} \ + --host=${host} --build=${build} \ + ${configure_opts} ${extraoptions} "$@" + rc=$? + exit $rc +fi +# ***** end AMD64 cross build script ******* + + +# Grep the required versions from configure.ac +autoconf_vers=`sed -n '/^AC_PREREQ(/ { +s/^.*(\(.*\))/\1/p +q +}' ${configure_ac}` +autoconf_vers_num=`echo "$autoconf_vers" | cvtver` + +automake_vers=`sed -n '/^min_automake_version=/ { +s/^.*="\(.*\)"/\1/p +q +}' ${configure_ac}` +automake_vers_num=`echo "$automake_vers" | cvtver` + +if [ -d "${tsdir}/po" ]; then + gettext_vers=`sed -n '/^AM_GNU_GETTEXT_VERSION(/ { +s/^.*\[\(.*\)])/\1/p +q +}' ${configure_ac}` + gettext_vers_num=`echo "$gettext_vers" | cvtver` +else + gettext_vers="n/a" +fi + +if [ -z "$autoconf_vers" -o -z "$automake_vers" -o -z "$gettext_vers" ] +then + echo "**Error**: version information not found in "\`${configure_ac}\'"." >&2 + exit 1 +fi + + +if check_version $AUTOCONF $autoconf_vers_num $autoconf_vers ; then + check_version $AUTOHEADER $autoconf_vers_num $autoconf_vers autoconf +fi +if check_version $AUTOMAKE $automake_vers_num $automake_vers; then + check_version $ACLOCAL $automake_vers_num $autoconf_vers automake +fi +if [ "$gettext_vers" != "n/a" ]; then + if check_version $GETTEXT $gettext_vers_num $gettext_vers; then + check_version $MSGMERGE $gettext_vers_num $gettext_vers gettext + fi +fi + +if [ "$DIE" = "yes" ]; then + cat < + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-11-29 Werner Koch + + * build-all.sh: Make sure HOME has no unsafe characters. + +2011-11-28 Werner Koch , + Jim Meyering + + * build-all.sh: New. + +2011-08-10 Werner Koch + + * config.guess, config.sub: Update to version 2011-06-03. + +2010-10-26 Werner Koch + + * config.guess: Update to version 2010-09-24. + * config.sub: Update to version 2010-09-11. + +2007-12-14 Werner Koch + + * config.guess, config.sub: Update to version 2007-11-19. + +2007-07-04 Werner Koch + + Switched to GPLv3+. + + * config.sub, config.guess: Updated from current Savannah + upstream. Changed to GPLv3+. + +2007-05-04 Werner Koch + + * texinfo.tex: Updated from gnulib. + +2007-04-04 Werner Koch + + * mail-to-translators: Copied from 1.4 and adjusted. + +2004-09-30 Werner Koch + + * config.guess, config.sub: Updated. + + + Copyright 2004, 2007, 2010 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/build-aux/compile b/build-aux/compile new file mode 100755 index 0000000..531136b --- /dev/null +++ b/build-aux/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# 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, 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, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/config.guess b/build-aux/config.guess new file mode 100755 index 0000000..c4bd827 --- /dev/null +++ b/build-aux/config.guess @@ -0,0 +1,1456 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2016 Free Software Foundation, Inc. + +timestamp='2016-05-15' + +# This file 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 3 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, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2016 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "${UNAME_MACHINE_ARCH}" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}${abi}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = hppa2.0w ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; +esac + +cat >&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build-aux/config.rpath b/build-aux/config.rpath new file mode 100755 index 0000000..c38b914 --- /dev/null +++ b/build-aux/config.rpath @@ -0,0 +1,690 @@ +#! /bin/sh +# Output a system dependent set of variables, describing how to set the +# run time search path of shared libraries in an executable. +# +# Copyright 1996-2013 Free Software Foundation, Inc. +# Taken from GNU libtool, 2001 +# Originally by Gordon Matzigkeit , 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld +# should be set by the caller. +# +# The set of defined variables is at the end of this script. + +# Known limitations: +# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer +# than 256 bytes, otherwise the compiler driver will dump core. The only +# known workaround is to choose shorter directory names for the build +# directory and/or the installation directory. + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a +shrext=.so + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +# Code taken from libtool.m4's _LT_CC_BASENAME. + +for cc_temp in $CC""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` + +# Code taken from libtool.m4's _LT_COMPILER_PIC. + +wl= +if test "$GCC" = yes; then + wl='-Wl,' +else + case "$host_os" in + aix*) + wl='-Wl,' + ;; + mingw* | cygwin* | pw32* | os2* | cegcc*) + ;; + hpux9* | hpux10* | hpux11*) + wl='-Wl,' + ;; + irix5* | irix6* | nonstopux*) + wl='-Wl,' + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + ecc*) + wl='-Wl,' + ;; + icc* | ifort*) + wl='-Wl,' + ;; + lf95*) + wl='-Wl,' + ;; + nagfor*) + wl='-Wl,-Wl,,' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + wl='-Wl,' + ;; + ccc*) + wl='-Wl,' + ;; + xl* | bgxl* | bgf* | mpixl*) + wl='-Wl,' + ;; + como) + wl='-lopt=' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ F* | *Sun*Fortran*) + wl= + ;; + *Sun\ C*) + wl='-Wl,' + ;; + esac + ;; + esac + ;; + newsos6) + ;; + *nto* | *qnx*) + ;; + osf3* | osf4* | osf5*) + wl='-Wl,' + ;; + rdos*) + ;; + solaris*) + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + wl='-Qoption ld ' + ;; + *) + wl='-Wl,' + ;; + esac + ;; + sunos4*) + wl='-Qoption ld ' + ;; + sysv4 | sysv4.2uw2* | sysv4.3*) + wl='-Wl,' + ;; + sysv4*MP*) + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + wl='-Wl,' + ;; + unicos*) + wl='-Wl,' + ;; + uts4*) + ;; + esac +fi + +# Code taken from libtool.m4's _LT_LINKER_SHLIBS. + +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no + +case "$host_os" in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes; then + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + # Unlike libtool, we use -rpath here, not --rpath, since the documented + # option of GNU ld is called -rpath, not --rpath. + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + case "$host_os" in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + fi + ;; + amigaos*) + case "$host_cpu" in + powerpc) + ;; + m68k) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + cygwin* | mingw* | pw32* | cegcc*) + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + haiku*) + ;; + interix[3-9]*) + hardcode_direct=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + netbsd*) + ;; + solaris*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' + else + ld_shlibs=no + fi + ;; + esac + ;; + sunos4*) + hardcode_direct=yes + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + esac + if test "$ld_shlibs" = no; then + hardcode_libdir_flag_spec= + fi +else + case "$host_os" in + aix3*) + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + else + aix_use_runtimelinking=no + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + fi + hardcode_direct=yes + hardcode_libdir_separator=':' + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + fi + # Begin _LT_AC_SYS_LIBPATH_AIX. + echo 'int main () { return 0; }' > conftest.c + ${CC} ${LDFLAGS} conftest.c -o conftest + aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + fi + if test -z "$aix_libpath"; then + aix_libpath="/usr/lib:/lib" + fi + rm -f conftest.c conftest + # End _LT_AC_SYS_LIBPATH_AIX. + if test "$aix_use_runtimelinking" = yes; then + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + else + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + fi + fi + ;; + amigaos*) + case "$host_cpu" in + powerpc) + ;; + m68k) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + libext=lib + ;; + darwin* | rhapsody*) + hardcode_direct=no + if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then + : + else + ld_shlibs=no + fi + ;; + dgux*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + freebsd2.2*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + freebsd2*) + hardcode_direct=yes + hardcode_minus_L=yes + ;; + freebsd* | dragonfly*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + hpux9*) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + hpux10*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + hpux11*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + ;; + *) + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + irix5* | irix6* | nonstopux*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + netbsd*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + newsos6) + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + *nto* | *qnx*) + ;; + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + osf3*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + osf4* | osf5*) + if test "$GCC" = yes; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + # Both cc and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + hardcode_libdir_separator=: + ;; + solaris*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + sunos4*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + ;; + sysv4) + case $host_vendor in + sni) + hardcode_direct=yes # is this really true??? + ;; + siemens) + hardcode_direct=no + ;; + motorola) + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + ;; + sysv4.3*) + ;; + sysv4*MP*) + if test -d /usr/nec; then + ld_shlibs=yes + fi + ;; + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + ;; + sysv5* | sco3.2v5* | sco5v6*) + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' + hardcode_libdir_separator=':' + ;; + uts4*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + *) + ld_shlibs=no + ;; + esac +fi + +# Check dynamic linker characteristics +# Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. +# Unlike libtool.m4, here we don't care about _all_ names of the library, but +# only about the one the linker finds when passed -lNAME. This is the last +# element of library_names_spec in libtool.m4, or possibly two of them if the +# linker has special search rules. +library_names_spec= # the last element of library_names_spec in libtool.m4 +libname_spec='lib$name' +case "$host_os" in + aix3*) + library_names_spec='$libname.a' + ;; + aix[4-9]*) + library_names_spec='$libname$shrext' + ;; + amigaos*) + case "$host_cpu" in + powerpc*) + library_names_spec='$libname$shrext' ;; + m68k) + library_names_spec='$libname.a' ;; + esac + ;; + beos*) + library_names_spec='$libname$shrext' + ;; + bsdi[45]*) + library_names_spec='$libname$shrext' + ;; + cygwin* | mingw* | pw32* | cegcc*) + shrext=.dll + library_names_spec='$libname.dll.a $libname.lib' + ;; + darwin* | rhapsody*) + shrext=.dylib + library_names_spec='$libname$shrext' + ;; + dgux*) + library_names_spec='$libname$shrext' + ;; + freebsd* | dragonfly*) + case "$host_os" in + freebsd[123]*) + library_names_spec='$libname$shrext$versuffix' ;; + *) + library_names_spec='$libname$shrext' ;; + esac + ;; + gnu*) + library_names_spec='$libname$shrext' + ;; + haiku*) + library_names_spec='$libname$shrext' + ;; + hpux9* | hpux10* | hpux11*) + case $host_cpu in + ia64*) + shrext=.so + ;; + hppa*64*) + shrext=.sl + ;; + *) + shrext=.sl + ;; + esac + library_names_spec='$libname$shrext' + ;; + interix[3-9]*) + library_names_spec='$libname$shrext' + ;; + irix5* | irix6* | nonstopux*) + library_names_spec='$libname$shrext' + case "$host_os" in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; + *) libsuff= shlibsuff= ;; + esac + ;; + esac + ;; + linux*oldld* | linux*aout* | linux*coff*) + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + library_names_spec='$libname$shrext' + ;; + knetbsd*-gnu) + library_names_spec='$libname$shrext' + ;; + netbsd*) + library_names_spec='$libname$shrext' + ;; + newsos6) + library_names_spec='$libname$shrext' + ;; + *nto* | *qnx*) + library_names_spec='$libname$shrext' + ;; + openbsd*) + library_names_spec='$libname$shrext$versuffix' + ;; + os2*) + libname_spec='$name' + shrext=.dll + library_names_spec='$libname.a' + ;; + osf3* | osf4* | osf5*) + library_names_spec='$libname$shrext' + ;; + rdos*) + ;; + solaris*) + library_names_spec='$libname$shrext' + ;; + sunos4*) + library_names_spec='$libname$shrext$versuffix' + ;; + sysv4 | sysv4.3*) + library_names_spec='$libname$shrext' + ;; + sysv4*MP*) + library_names_spec='$libname$shrext' + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + library_names_spec='$libname$shrext' + ;; + tpf*) + library_names_spec='$libname$shrext' + ;; + uts4*) + library_names_spec='$libname$shrext' + ;; +esac + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` +shlibext=`echo "$shrext" | sed -e 's,^\.,,'` +escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` +escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` +escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` + +LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2016 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos* | -phoenix*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -ios) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build-aux/depcomp b/build-aux/depcomp new file mode 100755 index 0000000..4ebd5b3 --- /dev/null +++ b/build-aux/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2013 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, 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, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/getswdb.sh b/build-aux/getswdb.sh new file mode 100755 index 0000000..83ecdb2 --- /dev/null +++ b/build-aux/getswdb.sh @@ -0,0 +1,190 @@ +#!/bin/sh +# Get the online version of the GnuPG software version database +# Copyright (C) 2014 Werner Koch +# +# 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. + +# The URL of the file to retrieve. +urlbase="https://versions.gnupg.org/" + +WGET=wget +GPGV=gpgv + +srcdir=$(dirname "$0") +distsigkey="$srcdir/../g10/distsigkey.gpg" + +# Convert a 3 part version number it a numeric value. +cvtver () { + awk 'NR==1 {split($NF,A,".");X=1000000*A[1]+1000*A[2]+A[3];print X;exit 0}' +} + +# Prints usage information. +usage() +{ + cat <&2 + ;; + esac + shift +done + +# Mac OSX has only a shasum and not sha1sum +if [ ${find_sha1sum} = yes ]; then + for i in sha1sum shasum ; do + tmp=$($i /dev/null | cut -d ' ' -f1) + if [ x"$tmp" = x"da39a3ee5e6b4b0d3255bfef95601890afd80709" ]; then + echo "$i" + exit 0 + fi + done + echo "false" + exit 1 +fi + +# Mac OSX has only a shasum and not sha256sum +if [ ${find_sha256sum} = yes ]; then + for i in 'shasum -a 256' sha256sum ; do + tmp=$($i /dev/null | cut -d ' ' -f1) + tmp2="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + if [ x"$tmp" = x"$tmp2" ]; then + echo "$i" + exit 0 + fi + done + echo "false" + exit 1 +fi + + +# Get GnuPG version from VERSION file. For a GIT checkout this means +# that ./autogen.sh must have been run first. For a regular tarball +# VERSION is always available. +if [ ! -f "$srcdir/../VERSION" ]; then + echo "VERSION file missing - run autogen.sh first." >&2 + exit 1 +fi +version=$(cat "$srcdir/../VERSION") +version_num=$(echo "$version" | cvtver) + +if [ $skip_verify = no ]; then + if ! $GPGV --version >/dev/null 2>/dev/null ; then + echo "command \"gpgv\" is not installed" >&2 + echo "(please install an older version of GnuPG)" >&2 + exit 1 + fi +fi + +# +# Download the list and verify. +# +if [ $skip_download = yes ]; then + if [ ! -f swdb.lst ]; then + echo "swdb.lst is missing." >&2 + exit 1 + fi + if [ $skip_verify = no ]; then + if [ ! -f swdb.lst.sig ]; then + echo "swdb.lst.sig is missing." >&2 + exit 1 + fi + fi +else + if ! $WGET --version >/dev/null 2>/dev/null ; then + echo "command \"wget\" is not installed" >&2 + exit 1 + fi + + if ! $WGET -q -O swdb.lst "$urlbase/swdb.lst" ; then + echo "download of swdb.lst failed." >&2 + exit 1 + fi + if [ $skip_verify = no ]; then + if ! $WGET -q -O swdb.lst.sig "$urlbase/swdb.lst.sig" ; then + echo "download of swdb.lst.sig failed." >&2 + exit 1 + fi + fi +fi +if [ $skip_verify = no ]; then + if ! $GPGV --keyring "$distsigkey" swdb.lst.sig swdb.lst; then + echo "list of software versions is not valid!" >&2 + exit 1 + fi +fi + +# +# Check that the online version of GnuPG is not less than this version +# to help detect rollback attacks. +# +if [ $skip_selfcheck = no ]; then + gnupg_ver=$(awk '$1=="gnupg21_ver" {print $2;exit}' swdb.lst) + if [ -z "$gnupg_ver" ]; then + echo "GnuPG 2.1 version missing in swdb.lst!" >&2 + exit 1 + fi + gnupg_ver_num=$(echo "$gnupg_ver" | cvtver) + if [ $(( $gnupg_ver_num >= $version_num )) = 0 ]; then + echo "GnuPG version in swdb.lst is less than this version!" >&2 + echo " This version: $version" >&2 + echo " SWDB version: $gnupg_ver" >&2 + exit 1 + fi +fi diff --git a/build-aux/git-log-fix b/build-aux/git-log-fix new file mode 100644 index 0000000..af702fe --- /dev/null +++ b/build-aux/git-log-fix @@ -0,0 +1,3 @@ +# This file is expected to be used via gitlog-to-changelog's --amend=FILE +# option. It specifies what changes to make to each given SHA1's commit +# log and metadata, using Perl-eval'able expressions. diff --git a/build-aux/git-log-footer b/build-aux/git-log-footer new file mode 100644 index 0000000..c31fe93 --- /dev/null +++ b/build-aux/git-log-footer @@ -0,0 +1,14 @@ + +2011-12-01 Werner Koch + + NB: Changes done before December 1st, 2011 are described in + per directory files named ChangeLog-2011. See doc/HACKING for + details. + + ----- + Copyright (C) 2011 Free Software Foundation, Inc. + + Copying and distribution of this file and/or the original GIT + commit log messages, with or without modification, are + permitted provided the copyright notice and this notice are + preserved. diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog new file mode 100755 index 0000000..24a3d72 --- /dev/null +++ b/build-aux/gitlog-to-changelog @@ -0,0 +1,374 @@ +eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}' + & eval 'exec perl -wS "$0" $argv:q' + if 0; +# Convert git log output to ChangeLog format. + +my $VERSION = '2012-01-24 15:58 (wk)'; # UTC +# The definition above must lie within the first 8 lines in order +# for the Emacs time-stamp write hook (at end) to update it. +# If you change this file with Emacs, please let the write hook +# do its job. Otherwise, update this string manually. + +# Copyright (C) 2008-2012 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 3 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, see . + +# Written by Jim Meyering +# Custom bugs bred by Werner Koch + +use strict; +use warnings; +use Getopt::Long; +use POSIX qw(strftime); + +(my $ME = $0) =~ s|.*/||; + +# use File::Coda; # http://meyering.net/code/Coda/ +END { + defined fileno STDOUT or return; + close STDOUT and return; + warn "$ME: failed to close standard output: $!\n"; + $? ||= 1; +} + +sub usage ($) +{ + my ($exit_code) = @_; + my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); + if ($exit_code != 0) + { + print $STREAM "Try `$ME --help' for more information.\n"; + } + else + { + print $STREAM < ChangeLog + $ME -- -n 5 foo > last-5-commits-to-branch-foo + +In a FILE specified via --amend, comment lines (starting with "#") are ignored. +FILE must consist of pairs where SHA is a 40-byte SHA1 (alone on +a line) referring to a commit in the current project, and CODE refers to one +or more consecutive lines of Perl code. Pairs must be separated by one or +more blank line. + +Here is sample input for use with --amend=FILE, from coreutils: + +3a169f4c5d9159283548178668d2fae6fced3030 +# fix typo in title: +s/all tile types/all file types/ + +1379ed974f1fa39b12e2ffab18b3f7a607082202 +# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself. +# Change the author to be Paul. Note the escaped "@": +s,Jim .*>,Paul Eggert , + +EOF + } + exit $exit_code; +} + +# If the string $S is a well-behaved file name, simply return it. +# If it contains white space, quotes, etc., quote it, and return the new string. +sub shell_quote($) +{ + my ($s) = @_; + if ($s =~ m![^\w+/.,-]!) + { + # Convert each single quote to '\'' + $s =~ s/\'/\'\\\'\'/g; + # Then single quote the string. + $s = "'$s'"; + } + return $s; +} + +sub quoted_cmd(@) +{ + return join (' ', map {shell_quote $_} @_); +} + +# Parse file F. +# Comment lines (starting with "#") are ignored. +# F must consist of pairs where SHA is a 40-byte SHA1 +# (alone on a line) referring to a commit in the current project, and +# CODE refers to one or more consecutive lines of Perl code. +# Pairs must be separated by one or more blank line. +sub parse_amend_file($) +{ + my ($f) = @_; + + open F, '<', $f + or die "$ME: $f: failed to open for reading: $!\n"; + + my $fail; + my $h = {}; + my $in_code = 0; + my $sha; + while (defined (my $line = )) + { + $line =~ /^\#/ + and next; + chomp $line; + $line eq '' + and $in_code = 0, next; + + if (!$in_code) + { + $line =~ /^([0-9a-fA-F]{40})$/ + or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"), + $fail = 1, next; + $sha = lc $1; + $in_code = 1; + exists $h->{$sha} + and (warn "$ME: $f:$.: duplicate SHA1\n"), + $fail = 1, next; + } + else + { + $h->{$sha} ||= ''; + $h->{$sha} .= "$line\n"; + } + } + close F; + + $fail + and exit 1; + + return $h; +} + +{ + my $since_date; + my $format_string = '%s%n%b%n'; + my $amend_file; + my $append_dot = 0; + my $tear_off = 0; + GetOptions + ( + help => sub { usage 0 }, + version => sub { print "$ME version $VERSION\n"; exit }, + 'since=s' => \$since_date, + 'format=s' => \$format_string, + 'amend=s' => \$amend_file, + 'append-dot' => \$append_dot, + 'tear-off' => \$tear_off, + ) or usage 1; + + + defined $since_date + and unshift @ARGV, "--since=$since_date"; + + # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/) + # that makes a correction in the log or attribution of that commit. + my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {}; + + my @cmd = (qw (git log --log-size), + '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV); + open PIPE, '-|', @cmd + or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n" + . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); + + my $prev_date_line = ''; + my @prev_coauthors = (); + + while (1) + { + defined (my $in = ) + or last; + $in =~ /^log size (\d+)$/ + or die "$ME:$.: Invalid line (expected log size):\n$in"; + my $log_nbytes = $1; + + my $log; + my $n_read = read PIPE, $log, $log_nbytes; + $n_read == $log_nbytes + or die "$ME:$.: unexpected EOF\n"; + + # Skip log entries with the default merge commit message. + $log =~ /^.*\n\nMerge branch '.*\n\s*/ + and goto SKIPCOMMIT; + + # Skip log entries if the body starts with a tear off marker. + if ($tear_off) + { + $log =~ /^.*\n\n.*\n--\s*/ + and goto SKIPCOMMIT; + } + + # Extract leading hash. + my ($sha, $rest) = split ':', $log, 2; + defined $sha + or die "$ME:$.: malformed log entry\n"; + $sha =~ /^[0-9a-fA-F]{40}$/ + or die "$ME:$.: invalid SHA1: $sha\n"; + + # If this commit's log requires any transformation, do it now. + my $code = $amend_code->{$sha}; + if (defined $code) + { + eval 'use Safe'; + my $s = new Safe; + # Put the unpreprocessed entry into "$_". + $_ = $rest; + + # Let $code operate on it, safely. + my $r = $s->reval("$code") + or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n"; + + # Note that we've used this entry. + delete $amend_code->{$sha}; + + # Update $rest upon success. + $rest = $_; + } + + my @line = split "\n", $rest; + my $author_line = shift @line; + defined $author_line + or die "$ME:$.: unexpected EOF\n"; + $author_line =~ /^(\d+) (.*>)$/ + or die "$ME:$.: Invalid line " + . "(expected date/author/email):\n$author_line\n"; + + my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1)); + + # Format 'Co-authored-by: A U Thor ' lines in + # standard multi-author ChangeLog format. + my @coauthors = grep /^Co-authored-by:.*$/, @line; + for (@coauthors) + { + s/^Co-authored-by:\s*/\t /; + s/\s*/ + or warn "$ME: warning: missing email address for " + . substr ($_, 5) . "\n"; + } + + # If this header would be the same as the previous date/name/email/ + # coauthors header, then arrange not to print it. + if ($date_line ne $prev_date_line or "@coauthors" ne "@prev_coauthors") + { + $prev_date_line eq '' + or print "\n"; + print $date_line; + @coauthors + and print join ("\n", @coauthors), "\n"; + } + $prev_date_line = $date_line; + @prev_coauthors = @coauthors; + + # Omit keyword lines like "Signed-off-by:" or "GnuPG-bug-id:" + @line = grep !/^[A-Z][A-Za-z]+-[a-z-]+: /, @line; + + # Remove everything after a line with 2 dashes at the beginning. + if ($tear_off) + { + my @tmpline; + foreach (@line) + { + last if /^--\s*$/; + push @tmpline,$_; + } + @line = @tmpline; + } + + # Remove leading and trailing blank lines. + if (@line) + { + while ($line[0] =~ /^\s*$/) { shift @line; } + while ($line[$#line] =~ /^\s*$/) { pop @line; } + } + + # If there were any lines + if (@line == 0) + { + warn "$ME: warning: empty commit message:\n $date_line\n"; + } + else + { + if ($append_dot) + { + # If the first line of the message has enough room, then + if (length $line[0] < 72) + { + # append a dot if there is no other punctuation or blank + # at the end. + $line[0] =~ /[[:punct:]\s]$/ + or $line[0] .= '.'; + } + } + + # Prefix each non-empty line with a TAB. + @line = map { length $_ ? "\t$_" : '' } @line; + + print "\n", join ("\n", @line), "\n"; + } + + SKIPCOMMIT: + defined ($in = ) + or last; + $in ne "\n" + and die "$ME:$.: unexpected line:\n$in"; + } + + close PIPE + or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; + # FIXME-someday: include $PROCESS_STATUS in the diagnostic + + # Complain about any unused entry in the --amend=F specified file. + my $fail = 0; + foreach my $sha (keys %$amend_code) + { + warn "$ME:$amend_file: unused entry: $sha\n"; + $fail = 1; + } + + exit $fail; +} + +# Local Variables: +# mode: perl +# indent-tabs-mode: nil +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "my $VERSION = '" +# time-stamp-format: "%:y-%02m-%02d %02H:%02M (wk)" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "'; # UTC" +# End: diff --git a/build-aux/install-sh b/build-aux/install-sh new file mode 100755 index 0000000..377bb86 --- /dev/null +++ b/build-aux/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/mdate-sh b/build-aux/mdate-sh new file mode 100755 index 0000000..b3719cf --- /dev/null +++ b/build-aux/mdate-sh @@ -0,0 +1,224 @@ +#!/bin/sh +# Get modification time of a file or directory and pretty-print it. + +scriptversion=2010-08-21.06; # UTC + +# Copyright (C) 1995-2013 Free Software Foundation, Inc. +# written by Ulrich Drepper , June 1995 +# +# 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, 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, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +fi + +case $1 in + '') + echo "$0: No file. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: mdate-sh [--help] [--version] FILE + +Pretty-print the modification day of FILE, in the format: +1 January 1970 + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "mdate-sh $scriptversion" + exit $? + ;; +esac + +error () +{ + echo "$0: $1" >&2 + exit 1 +} + + +# Prevent date giving response in another language. +LANG=C +export LANG +LC_ALL=C +export LC_ALL +LC_TIME=C +export LC_TIME + +# GNU ls changes its time format in response to the TIME_STYLE +# variable. Since we cannot assume 'unset' works, revert this +# variable to its documented default. +if test "${TIME_STYLE+set}" = set; then + TIME_STYLE=posix-long-iso + export TIME_STYLE +fi + +save_arg1=$1 + +# Find out how to get the extended ls output of a file or directory. +if ls -L /dev/null 1>/dev/null 2>&1; then + ls_command='ls -L -l -d' +else + ls_command='ls -l -d' +fi +# Avoid user/group names that might have spaces, when possible. +if ls -n /dev/null 1>/dev/null 2>&1; then + ls_command="$ls_command -n" +fi + +# A 'ls -l' line looks as follows on OS/2. +# drwxrwx--- 0 Aug 11 2001 foo +# This differs from Unix, which adds ownership information. +# drwxrwx--- 2 root root 4096 Aug 11 2001 foo +# +# To find the date, we split the line on spaces and iterate on words +# until we find a month. This cannot work with files whose owner is a +# user named "Jan", or "Feb", etc. However, it's unlikely that '/' +# will be owned by a user whose name is a month. So we first look at +# the extended ls output of the root directory to decide how many +# words should be skipped to get the date. + +# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below. +set x`$ls_command /` + +# Find which argument is the month. +month= +command= +until test $month +do + test $# -gt 0 || error "failed parsing '$ls_command /' output" + shift + # Add another shift to the command. + command="$command shift;" + case $1 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; + esac +done + +test -n "$month" || error "failed parsing '$ls_command /' output" + +# Get the extended ls output of the file or directory. +set dummy x`eval "$ls_command \"\\\$save_arg1\""` + +# Remove all preceding arguments +eval $command + +# Because of the dummy argument above, month is in $2. +# +# On a POSIX system, we should have +# +# $# = 5 +# $1 = file size +# $2 = month +# $3 = day +# $4 = year or time +# $5 = filename +# +# On Darwin 7.7.0 and 7.6.0, we have +# +# $# = 4 +# $1 = day +# $2 = month +# $3 = year or time +# $4 = filename + +# Get the month. +case $2 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; +esac + +case $3 in + ???*) day=$1;; + *) day=$3; shift;; +esac + +# Here we have to deal with the problem that the ls output gives either +# the time of day or the year. +case $3 in + *:*) set `date`; eval year=\$$# + case $2 in + Jan) nummonthtod=1;; + Feb) nummonthtod=2;; + Mar) nummonthtod=3;; + Apr) nummonthtod=4;; + May) nummonthtod=5;; + Jun) nummonthtod=6;; + Jul) nummonthtod=7;; + Aug) nummonthtod=8;; + Sep) nummonthtod=9;; + Oct) nummonthtod=10;; + Nov) nummonthtod=11;; + Dec) nummonthtod=12;; + esac + # For the first six month of the year the time notation can also + # be used for files modified in the last year. + if (expr $nummonth \> $nummonthtod) > /dev/null; + then + year=`expr $year - 1` + fi;; + *) year=$3;; +esac + +# The result. +echo $day $month $year + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/missing b/build-aux/missing new file mode 100755 index 0000000..db98974 --- /dev/null +++ b/build-aux/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# 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, 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, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/mkinstalldirs b/build-aux/mkinstalldirs new file mode 100755 index 0000000..ef7e16f --- /dev/null +++ b/build-aux/mkinstalldirs @@ -0,0 +1,161 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy + +scriptversion=2006-05-11.19 + +# Original author: Noah Friedman +# Created: 1993-05-16 +# Public domain. +# +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' +IFS=" "" $nl" +errstatus=0 +dirmode= + +usage="\ +Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... + +Create each directory DIR (with mode MODE, if specified), including all +leading file name components. + +Report bugs to ." + +# process command line arguments +while test $# -gt 0 ; do + case $1 in + -h | --help | --h*) # -h for help + echo "$usage" + exit $? + ;; + -m) # -m PERM arg + shift + test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } + dirmode=$1 + shift + ;; + --version) + echo "$0 $scriptversion" + exit $? + ;; + --) # stop option processing + shift + break + ;; + -*) # unknown option + echo "$usage" 1>&2 + exit 1 + ;; + *) # first non-opt arg + break + ;; + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in + 0) exit 0 ;; +esac + +# Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and +# mkdir -p a/c at the same time, both will detect that a is missing, +# one will create a, then the other will try to create a and die with +# a "File exists" error. This is a problem when calling mkinstalldirs +# from a parallel make. We use --version in the probe to restrict +# ourselves to GNU mkdir, which is thread-safe. +case $dirmode in + '') + if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + test -d ./-p && rmdir ./-p + test -d ./--version && rmdir ./--version + fi + ;; + *) + if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && + test ! -d ./--version; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + else + # Clean up after NextStep and OpenStep mkdir. + for d in ./-m ./-p ./--version "./$dirmode"; + do + test -d $d && rmdir $d + done + fi + ;; +esac + +for file +do + case $file in + /*) pathcomp=/ ;; + *) pathcomp= ;; + esac + oIFS=$IFS + IFS=/ + set fnord $file + shift + IFS=$oIFS + + for d + do + test "x$d" = x && continue + + pathcomp=$pathcomp$d + case $pathcomp in + -*) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + lasterr= + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp=$pathcomp/ + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/build-aux/potomo b/build-aux/potomo new file mode 100755 index 0000000..b4c0a6b --- /dev/null +++ b/build-aux/potomo @@ -0,0 +1,64 @@ +#!/bin/sh +# potomo - Convert a .po file to an utf-8 encoded .mo file. +# Copyright 2008 g10 Code GmbH +# Copyright 2010 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. + +# This script is used to create the mo files for applications using +# the simple gettext implementation provided by libgpg-error. That +# gettext can only cope with utf-8 encoded mo files; thus we make this +# sure while creating the mo. A conversion is not done if the source +# file does not exist or if it is not newer than the mo file. + +if [ "$1" = "--get-linguas" -a $# -eq 2 ]; then + if [ ! -f "$2/LINGUAS" ]; then + echo "potomo: directory '$2' has no LINGUAS file" >&2 + exit 1 + fi + echo $(sed -e "/^#/d" -e "s/#.*//" "$2"/LINGUAS) + exit 0 +fi + +if [ $# -ne 2 ]; then + echo "usage: potomo INFILE.PO OUTFILE.MO" >&2 + echo " potomo --get-linguas DIR" >&2 + exit 1 +fi +infile="$1" +outfile="$2" + +if [ ! -f "$infile" ]; then + echo "potomo: '$infile' not found - ignored" 2>&1 + exit 0 +fi + +if [ "$outfile" -nt "$infile" ]; then + echo "potomo: '$outfile' is newer than source - keeping" 2>&1 + exit 0 +fi + +# Note that we could use the newer msgconv. However this tool was not +# widely available back in 2008. + +fromset=`sed -n '/^"Content-Type:/ s/.*charset=\([a-zA-Z0-9_-]*\).*/\1/p' \ + "$infile"` + +case "$fromset" in + utf8|utf-8|UTF8|UTF-8) + echo "potomo: '$infile' keeping $fromset" >&2 + msgfmt --output-file="$outfile" "$infile" + ;; + *) + echo "potomo: '$infile' converting from $fromset to utf-8" >&2 + iconv --silent --from-code=$fromset --to-code=utf-8 < "$infile" |\ + sed "/^\"Content-Type:/ s/charset=[a-zA-Z0-9_-]*/charset=utf-8/"|\ + msgfmt --output-file="$outfile" - + ;; +esac diff --git a/build-aux/speedo.mk b/build-aux/speedo.mk new file mode 100644 index 0000000..8a366e6 --- /dev/null +++ b/build-aux/speedo.mk @@ -0,0 +1,1240 @@ +# speedo.mk - Speedo rebuilds speedily. +# Copyright (C) 2008, 2014 g10 Code GmbH +# +# speedo 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 3 of the License, or +# (at your option) any later version. +# +# speedo 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, see . + +# speedo builds gnupg-related packages from GIT and installs them in a +# user directory, thereby providing a non-obstrusive test environment. +# speedo does only work with GNU make. The build system is similar to +# that of gpg4win. The following commands are supported: +# +# make -f speedo.mk all pkg2rep=/dir/with/tarballs +# or +# make -f speedo.mk +# +# Builds all packages and installs them under PLAY/inst. At the end, +# speedo prints commands that can be executed in the local shell to +# make use of the installed packages. +# +# make -f speedo.mk clean +# or +# make -f speedo.mk clean-PACKAGE +# +# Removes all packages or the package PACKAGE from the installation +# and build tree. A subsequent make will rebuild these (and only +# these) packages. +# +# make -f speedo.mk report +# or +# make -f speedo.mk report-PACKAGE +# +# Lists packages and versions. +# + +# We need to know our own name. +SPEEDO_MK := $(realpath $(lastword $(MAKEFILE_LIST))) + +.PHONY : help native native-gui w32-installer w32-source +.PHONY : git-native git-native-gui git-w32-installer git-w32-source +.PHONY : this-native this-native-gui this-w32-installer this-w32-source + +help: + @echo 'usage: make -f speedo.mk TARGET' + @echo ' with TARGET being one of:' + @echo ' help This help' + @echo ' native Native build of the GnuPG core' + @echo ' native-gui Ditto but with pinentry and GPA' + @echo ' w32-installer Build a Windows installer' + @echo ' w32-source Pack a source archive' + @echo ' w32-release Build a Windows release' + @echo ' w32-sign-installer Sign the installer' + @echo + @echo 'You may append INSTALL_PREFIX= for native builds.' + @echo 'Prepend TARGET with "git-" to build from GIT repos.' + @echo 'Prepend TARGET with "this-" to build from the source tarball.' + @echo 'Use SELFCHECK=0 for a non-released version.' + @echo 'Use CUSTOM_SWDB=1 for an already downloaded swdb.lst.' + +SPEEDOMAKE := $(MAKE) -f $(SPEEDO_MK) UPD_SWDB=1 + +native: check-tools + $(SPEEDOMAKE) TARGETOS=native WHAT=release WITH_GUI=0 all + +git-native: check-tools + $(SPEEDOMAKE) TARGETOS=native WHAT=git WITH_GUI=0 all + +this-native: check-tools + $(SPEEDOMAKE) TARGETOS=native WHAT=this WITH_GUI=0 all + +native-gui: check-tools + $(SPEEDOMAKE) TARGETOS=native WHAT=release WITH_GUI=1 all + +git-native-gui: check-tools + $(SPEEDOMAKE) TARGETOS=native WHAT=git WITH_GUI=1 all + +this-native-gui: check-tools + $(SPEEDOMAKE) TARGETOS=native WHAT=this WITH_GUI=1 all + +w32-installer: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 installer + +git-w32-installer: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=git WITH_GUI=0 installer + +this-w32-installer: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=this WITH_GUI=0 \ + CUSTOM_SWDB=1 installer + +w32-source: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 dist-source + +git-w32-source: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=git WITH_GUI=0 dist-source + +this-w32-source: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=this WITH_GUI=0 \ + CUSTOM_SWDB=1 dist-source + +w32-release: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 SELFCHECK=0 \ + installer-from-source + +w32-sign-installer: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 SELFCHECK=0 \ + sign-installer + +w32-release-offline: check-tools + $(SPEEDOMAKE) TARGETOS=w32 WHAT=release WITH_GUI=0 SELFCHECK=0 \ + CUSTOM_SWDB=1 pkgrep=${HOME}/b pkg10rep=${HOME}/b \ + installer-from-source + + +# Set this to "git" to build from git, +# to "release" from tarballs, +# to "this" from the unpacked sources. +WHAT=git + +# Set target to "native" or "w32" +TARGETOS= + +# Set to 1 to build the GUI tools +WITH_GUI=0 + +# Set to 1 to use a pre-installed swdb.lst instead of the online version. +CUSTOM_SWDB=0 + +# Set to 1 to really download the swdb. +UPD_SWDB=0 + +# Set to 0 to skip the GnuPG version self-check +SELFCHECK=1 + +# Set to the location of the directory with tarballs of +# external packages. +TARBALLS=$(shell pwd)/../tarballs + +# Number of parallel make jobs +MAKE_J=3 + +# Name to use for the w32 installer and sources +INST_NAME=gnupg-w32 + +# Use this to override the installaion directory for native builds. +INSTALL_PREFIX=none + +# The Authenticode key used to sign the Windows installer +AUTHENTICODE_KEY=${HOME}/.gnupg/g10code-authenticode-key.p12 + + +# Directory names. +# They must be absolute, as we switch directories pretty often. +root := $(shell pwd)/PLAY +sdir := $(root)/src +bdir := $(root)/build +bdir6:= $(root)/build-w64 +ifeq ($(INSTALL_PREFIX),none) +idir := $(root)/inst +else +idir := $(abspath $(INSTALL_PREFIX)) +endif +idir6:= $(root)/inst-w64 +stampdir := $(root)/stamps +topsrc := $(shell cd $(dir $(SPEEDO_MK)).. && pwd) +auxsrc := $(topsrc)/build-aux/speedo +patdir := $(topsrc)/build-aux/speedo/patches +w32src := $(topsrc)/build-aux/speedo/w32 + +# =====BEGIN LIST OF PACKAGES===== +# The packages that should be built. The order is also the build order. +# Fixme: Do we need to build pkg-config for cross-building? + +speedo_spkgs = \ + libgpg-error npth libgcrypt + +ifeq ($(TARGETOS),w32) +speedo_spkgs += \ + zlib bzip2 adns sqlite +ifeq ($(WITH_GUI),1) +speedo_spkgs += gettext libiconv +endif +endif + +speedo_spkgs += \ + libassuan libksba gnupg + +ifeq ($(TARGETOS),w32) +ifeq ($(WITH_GUI),1) +speedo_spkgs += \ + libffi glib pkg-config +endif +endif + +speedo_spkgs += \ + gpgme + +ifeq ($(TARGETOS),w32) +ifeq ($(WITH_GUI),1) +speedo_spkgs += \ + libpng \ + gdk-pixbuf atk pixman cairo pango gtk+ +endif +endif + +ifeq ($(TARGETOS),w32) + +speedo_spkgs += pinentry +ifeq ($(WITH_GUI),1) +speedo_spkgs += gpa gpgex +endif + +else + +ifeq ($(WITH_GUI),1) +speedo_spkgs += pinentry gpa +endif + +endif + + +# =====END LIST OF PACKAGES===== + + +# Packages which are additionally build for 64 bit Windows. They are +# only used for gpgex and thus we need to build them only if we want +# a full installer. +speedo_w64_spkgs = +ifeq ($(WITH_GUI),1) +speedo_w64_spkgs += libgpg-error libiconv gettext libassuan gpgex +endif + +# Packages which use the gnupg autogen.sh build style +speedo_gnupg_style = \ + libgpg-error npth libgcrypt \ + libassuan libksba gnupg gpgme \ + pinentry gpa gpgex + +# Packages which use only make and no build directory +speedo_make_only_style = \ + zlib bzip2 + +# Get the content of the software DB. +ifeq ($(CUSTOM_SWDB),1) +getswdb_options = --skip-download --skip-verify +else +getswdb_options = +endif +ifeq ($(SELFCHECK),0) +getswdb_options += --skip-selfcheck +endif +ifeq ($(UPD_SWDB),1) +SWDB := $(shell $(topsrc)/build-aux/getswdb.sh $(getswdb_options) && echo okay) +ifeq ($(strip $(SWDB)),) +ifneq ($(WHAT),git) +$(error Error getting GnuPG software version database) +endif +endif + +# Version numbers of the released packages +gnupg_ver_this = $(shell cat $(topsrc)/VERSION) + +gnupg_ver := $(shell awk '$$1=="gnupg21_ver" {print $$2}' swdb.lst) + +libgpg_error_ver := $(shell awk '$$1=="libgpg_error_ver" {print $$2}' swdb.lst) +libgpg_error_sha1:= $(shell awk '$$1=="libgpg_error_sha1" {print $$2}' swdb.lst) +libgpg_error_sha2:= $(shell awk '$$1=="libgpg_error_sha2" {print $$2}' swdb.lst) + +npth_ver := $(shell awk '$$1=="npth_ver" {print $$2}' swdb.lst) +npth_sha1 := $(shell awk '$$1=="npth_sha1" {print $$2}' swdb.lst) +npth_sha2 := $(shell awk '$$1=="npth_sha2" {print $$2}' swdb.lst) + +libgcrypt_ver := $(shell awk '$$1=="libgcrypt_ver" {print $$2}' swdb.lst) +libgcrypt_sha1 := $(shell awk '$$1=="libgcrypt_sha1" {print $$2}' swdb.lst) +libgcrypt_sha2 := $(shell awk '$$1=="libgcrypt_sha2" {print $$2}' swdb.lst) + +libassuan_ver := $(shell awk '$$1=="libassuan_ver" {print $$2}' swdb.lst) +libassuan_sha1 := $(shell awk '$$1=="libassuan_sha1" {print $$2}' swdb.lst) +libassuan_sha2 := $(shell awk '$$1=="libassuan_sha2" {print $$2}' swdb.lst) + +libksba_ver := $(shell awk '$$1=="libksba_ver" {print $$2}' swdb.lst) +libksba_sha1 := $(shell awk '$$1=="libksba_sha1" {print $$2}' swdb.lst) +libksba_sha2 := $(shell awk '$$1=="libksba_sha2" {print $$2}' swdb.lst) + +gpgme_ver := $(shell awk '$$1=="gpgme_ver" {print $$2}' swdb.lst) +gpgme_sha1 := $(shell awk '$$1=="gpgme_sha1" {print $$2}' swdb.lst) +gpgme_sha2 := $(shell awk '$$1=="gpgme_sha2" {print $$2}' swdb.lst) + +pinentry_ver := $(shell awk '$$1=="pinentry_ver" {print $$2}' swdb.lst) +pinentry_sha1 := $(shell awk '$$1=="pinentry_sha1" {print $$2}' swdb.lst) +pinentry_sha2 := $(shell awk '$$1=="pinentry_sha2" {print $$2}' swdb.lst) + +gpa_ver := $(shell awk '$$1=="gpa_ver" {print $$2}' swdb.lst) +gpa_sha1 := $(shell awk '$$1=="gpa_sha1" {print $$2}' swdb.lst) +gpa_sha2 := $(shell awk '$$1=="gpa_sha2" {print $$2}' swdb.lst) + +gpgex_ver := $(shell awk '$$1=="gpgex_ver" {print $$2}' swdb.lst) +gpgex_sha1 := $(shell awk '$$1=="gpgex_sha1" {print $$2}' swdb.lst) +gpgex_sha2 := $(shell awk '$$1=="gpgex_sha2" {print $$2}' swdb.lst) + +zlib_ver := $(shell awk '$$1=="zlib_ver" {print $$2}' swdb.lst) +zlib_sha1 := $(shell awk '$$1=="zlib_sha1_gz" {print $$2}' swdb.lst) +zlib_sha2 := $(shell awk '$$1=="zlib_sha2_gz" {print $$2}' swdb.lst) + +bzip2_ver := $(shell awk '$$1=="bzip2_ver" {print $$2}' swdb.lst) +bzip2_sha1 := $(shell awk '$$1=="bzip2_sha1_gz" {print $$2}' swdb.lst) +bzip2_sha2 := $(shell awk '$$1=="bzip2_sha2_gz" {print $$2}' swdb.lst) + +adns_ver := $(shell awk '$$1=="adns_ver" {print $$2}' swdb.lst) +adns_sha1 := $(shell awk '$$1=="adns_sha1" {print $$2}' swdb.lst) +adns_sha2 := $(shell awk '$$1=="adns_sha2" {print $$2}' swdb.lst) + +sqlite_ver := $(shell awk '$$1=="sqlite_ver" {print $$2}' swdb.lst) +sqlite_sha1 := $(shell awk '$$1=="sqlite_sha1_gz" {print $$2}' swdb.lst) +sqlite_sha2 := $(shell awk '$$1=="sqlite_sha2_gz" {print $$2}' swdb.lst) + + +$(info Information from the version database) +$(info GnuPG ..........: $(gnupg_ver) (building $(gnupg_ver_this))) +$(info Libgpg-error ...: $(libgpg_error_ver)) +$(info Npth ...........: $(npth_ver)) +$(info Libgcrypt ......: $(libgcrypt_ver)) +$(info Libassuan ......: $(libassuan_ver)) +$(info Zlib ...........: $(zlib_ver)) +$(info Bzip2 ..........: $(bzip2_ver)) +$(info ADNS ...........: $(adns_ver)) +$(info SQLite .........: $(sqlite_ver)) +$(info GPGME ..........: $(gpgme_ver)) +$(info Pinentry .......: $(pinentry_ver)) +$(info GPA ............: $(gpa_ver)) +$(info GpgEX.... ......: $(gpgex_ver)) +endif + +# Version number for external packages +pkg_config_ver = 0.23 +libiconv_ver = 1.14 +gettext_ver = 0.18.2.1 +libffi_ver = 3.0.13 +glib_ver = 2.34.3 +libpng_ver = 1.4.12 +gdk_pixbuf_ver = 2.26.5 +atk_ver = 1.32.0 +pango_ver = 1.29.4 +pixman_ver = 0.32.4 +cairo_ver = 1.12.16 +gtk__ver = 2.24.17 + +# The GIT repository. Using a local repo is much faster. +#gitrep = git://git.gnupg.org +gitrep = ${HOME}/s + +# The tarball directories +pkgrep = ftp://ftp.gnupg.org/gcrypt +pkg10rep = ftp://ftp.g10code.com/g10code +pkg2rep = $(TARBALLS) + +# For each package, the following variables can be defined: +# +# speedo_pkg_PACKAGE_git: The GIT repository that should be built. +# speedo_pkg_PACKAGE_gitref: The GIT revision to checkout +# +# speedo_pkg_PACKAGE_tar: URL to the tar file that should be built. +# +# Exactly one of the above variables is required. Note that this +# version of speedo does not cache repositories or tar files, and does +# not test the integrity of the downloaded software. If you care +# about this, you can also specify filenames to locally verified files. +# Filenames are differentiated from URLs by starting with a slash '/'. +# +# speedo_pkg_PACKAGE_configure: Extra arguments to configure. +# +# speedo_pkg_PACKAGE_make_args: Extra arguments to make. +# +# speedo_pkg_PACKAGE_make_args_inst: Extra arguments to make install. +# +# Note that you can override the defaults in this file in a local file +# "config.mk" + +ifeq ($(WHAT),this) +else ifeq ($(WHAT),git) + speedo_pkg_libgpg_error_git = $(gitrep)/libgpg-error + speedo_pkg_libgpg_error_gitref = master + speedo_pkg_npth_git = $(gitrep)/npth + speedo_pkg_npth_gitref = master + speedo_pkg_libassuan_git = $(gitrep)/libassuan + speedo_pkg_libassuan_gitref = master + speedo_pkg_libgcrypt_git = $(gitrep)/libgcrypt + speedo_pkg_libgcrypt_gitref = master + speedo_pkg_libksba_git = $(gitrep)/libksba + speedo_pkg_libksba_gitref = master + speedo_pkg_gpgme_git = $(gitrep)/gpgme + speedo_pkg_gpgme_gitref = master + speedo_pkg_pinentry_git = $(gitrep)/pinentry + speedo_pkg_pinentry_gitref = master + speedo_pkg_gpa_git = $(gitrep)/gpa + speedo_pkg_gpa_gitref = master + speedo_pkg_gpgex_git = $(gitrep)/gpgex + speedo_pkg_gpgex_gitref = master +else ifeq ($(WHAT),release) + speedo_pkg_libgpg_error_tar = \ + $(pkgrep)/libgpg-error/libgpg-error-$(libgpg_error_ver).tar.bz2 + speedo_pkg_npth_tar = \ + $(pkgrep)/npth/npth-$(npth_ver).tar.bz2 + speedo_pkg_libassuan_tar = \ + $(pkgrep)/libassuan/libassuan-$(libassuan_ver).tar.bz2 + speedo_pkg_libgcrypt_tar = \ + $(pkgrep)/libgcrypt/libgcrypt-$(libgcrypt_ver).tar.bz2 + speedo_pkg_libksba_tar = \ + $(pkgrep)/libksba/libksba-$(libksba_ver).tar.bz2 + speedo_pkg_gpgme_tar = \ + $(pkgrep)/gpgme/gpgme-$(gpgme_ver).tar.bz2 + speedo_pkg_pinentry_tar = \ + $(pkgrep)/pinentry/pinentry-$(pinentry_ver).tar.bz2 + speedo_pkg_gpa_tar = \ + $(pkgrep)/gpa/gpa-$(gpa_ver).tar.bz2 + speedo_pkg_gpgex_tar = \ + $(pkg10rep)/gpgex/gpgex-$(gpgex_ver).tar.bz2 +else + $(error invalid value for WHAT (use on of: git release this)) +endif + +speedo_pkg_pkg_config_tar = $(pkg2rep)/pkg-config-$(pkg_config_ver).tar.gz +speedo_pkg_zlib_tar = $(pkgrep)/zlib/zlib-$(zlib_ver).tar.gz +speedo_pkg_bzip2_tar = $(pkgrep)/bzip2/bzip2-$(bzip2_ver).tar.gz +speedo_pkg_sqlite_tar = $(pkgrep)/sqlite/sqlite-autoconf-$(sqlite_ver).tar.gz +speedo_pkg_adns_tar = $(pkg10rep)/adns/adns-$(adns_ver).tar.bz2 +speedo_pkg_libiconv_tar = $(pkg2rep)/libiconv-$(libiconv_ver).tar.gz +speedo_pkg_gettext_tar = $(pkg2rep)/gettext-$(gettext_ver).tar.gz +speedo_pkg_libffi_tar = $(pkg2rep)/libffi-$(libffi_ver).tar.gz +speedo_pkg_glib_tar = $(pkg2rep)/glib-$(glib_ver).tar.xz +speedo_pkg_libpng_tar = $(pkg2rep)/libpng-$(libpng_ver).tar.bz2 +speedo_pkg_gdk_pixbuf_tar = $(pkg2rep)/gdk-pixbuf-$(gdk_pixbuf_ver).tar.xz +speedo_pkg_atk_tar = $(pkg2rep)/atk-$(atk_ver).tar.bz2 +speedo_pkg_pango_tar = $(pkg2rep)/pango-$(pango_ver).tar.bz2 +speedo_pkg_pixman_tar = $(pkg2rep)/pixman-$(pixman_ver).tar.gz +speedo_pkg_cairo_tar = $(pkg2rep)/cairo-$(cairo_ver).tar.xz +speedo_pkg_gtk__tar = $(pkg2rep)/gtk+-$(gtk__ver).tar.xz + + +# +# Package build options +# + +speedo_pkg_libgpg_error_configure = --enable-static +speedo_pkg_w64_libgpg_error_configure = --enable-static + +speedo_pkg_libassuan_configure = --enable-static +speedo_pkg_w64_libassuan_configure = --enable-static + +speedo_pkg_libgcrypt_configure = --disable-static + +speedo_pkg_libksba_configure = --disable-static + +ifeq ($(TARGETOS),w32) +speedo_pkg_gnupg_configure = \ + --enable-gpg2-is-gpg --disable-g13 --disable-ntbtls \ + --enable-build-timestamp +else +speedo_pkg_gnupg_configure = --disable-g13 +endif +speedo_pkg_gnupg_extracflags = -g + +# Create the version info files only for W32 so that they won't get +# installed if for example INSTALL_PREFIX=/usr/local is used. +ifeq ($(TARGETOS),w32) +define speedo_pkg_gnupg_post_install +(set -e; \ + sed -n 's/.*PACKAGE_VERSION "\(.*\)"/\1/p' config.h >$(idir)/INST_VERSION; \ + sed -n 's/.*W32INFO_VI_PRODUCTVERSION \(.*\)/\1/p' common/w32info-rc.h \ + |sed 's/,/./g' >$(idir)/INST_PROD_VERSION ) +endef +endif + +# The LDFLAGS is needed for -lintl for glib. +ifeq ($(WITH_GUI),1) +speedo_pkg_gpgme_configure = \ + --enable-static --enable-w32-glib --disable-w32-qt \ + --with-gpg-error-prefix=$(idir) \ + LDFLAGS=-L$(idir)/lib +else +speedo_pkg_gpgme_configure = \ + --disable-static --disable-w32-glib --disable-w32-qt \ + --with-gpg-error-prefix=$(idir) \ + LDFLAGS=-L$(idir)/lib +endif + + +ifeq ($(TARGETOS),w32) +speedo_pkg_pinentry_configure = --disable-pinentry-gtk2 +else +speedo_pkg_pinentry_configure = --enable-pinentry-gtk2 +endif +speedo_pkg_pinentry_configure += \ + --disable-pinentry-qt4 \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib \ + CXXFLAGS=-static-libstdc++ + + +speedo_pkg_gpa_configure = \ + --with-libiconv-prefix=$(idir) --with-libintl-prefix=$(idir) \ + --with-gpgme-prefix=$(idir) --with-zlib=$(idir) \ + --with-libassuan-prefix=$(idir) --with-gpg-error-prefix=$(idir) + +speedo_pkg_gpgex_configure = \ + --with-gpg-error-prefix=$(idir) \ + --with-libassuan-prefix=$(idir) \ + --enable-gpa-only + +speedo_pkg_w64_gpgex_configure = \ + --with-gpg-error-prefix=$(idir6) \ + --with-libassuan-prefix=$(idir6) \ + --enable-gpa-only + + +# +# External packages +# + +ifeq ($(TARGETOS),w32) +speedo_pkg_zlib_make_args = \ + -fwin32/Makefile.gcc PREFIX=$(host)- IMPLIB=libz.dll.a + +speedo_pkg_zlib_make_args_inst = \ + -fwin32/Makefile.gcc \ + BINARY_PATH=$(idir)/bin INCLUDE_PATH=$(idir)/include \ + LIBRARY_PATH=$(idir)/lib SHARED_MODE=1 IMPLIB=libz.dll.a + +# Zlib needs some special magic to generate a libtool file. +# We also install the pc file here. +define speedo_pkg_zlib_post_install +(set -e; mkdir $(idir)/lib/pkgconfig || true; \ +cp $(auxsrc)/zlib.pc $(idir)/lib/pkgconfig/; \ +cd $(idir); \ +echo "# Generated by libtool" > lib/libz.la \ +echo "dlname='../bin/zlib1.dll'" >> lib/libz.la; \ +echo "library_names='libz.dll.a'" >> lib/libz.la; \ +echo "old_library='libz.a'" >> lib/libz.la; \ +echo "dependency_libs=''" >> lib/libz.la; \ +echo "current=1" >> lib/libz.la; \ +echo "age=2" >> lib/libz.la; \ +echo "revision=5" >> lib/libz.la; \ +echo "installed=yes" >> lib/libz.la; \ +echo "shouldnotlink=no" >> lib/libz.la; \ +echo "dlopen=''" >> lib/libz.la; \ +echo "dlpreopen=''" >> lib/libz.la; \ +echo "libdir=\"$(idir)/lib\"" >> lib/libz.la) +endef + +endif + +ifeq ($(TARGETOS),w32) +speedo_pkg_bzip2_make_args = \ + CC="$(host)-gcc" AR="$(host)-ar" RANLIB="$(host)-ranlib" + +speedo_pkg_bzip2_make_args_inst = \ + PREFIX=$(idir) CC="$(host)-gcc" AR="$(host)-ar" RANLIB="$(host)-ranlib" +endif + +speedo_pkg_w64_libiconv_configure = \ + --enable-shared=no --enable-static=yes + +speedo_pkg_gettext_configure = \ + --with-lib-prefix=$(idir) --with-libiconv-prefix=$(idir) \ + CPPFLAGS=-I$(idir)/include LDFLAGS=-L$(idir)/lib +speedo_pkg_w64_gettext_configure = \ + --with-lib-prefix=$(idir) --with-libiconv-prefix=$(idir) \ + CPPFLAGS=-I$(idir6)/include LDFLAGS=-L$(idir6)/lib +speedo_pkg_gettext_extracflags = -O2 +# We only need gettext-runtime and there is sadly no top level +# configure option for this +speedo_pkg_gettext_make_dir = gettext-runtime + + +speedo_pkg_glib_configure = \ + --disable-modular-tests \ + --with-libiconv=gnu \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib \ + CCC=$(host)-g++ \ + LIBFFI_CFLAGS=-I$(idir)/lib/libffi-$(libffi_ver)/include \ + LIBFFI_LIBS=\"-L$(idir)/lib -lffi\" +ifeq ($(TARGETOS),w32) +speedo_pkg_glib_extracflags = -march=i486 +endif + +ifeq ($(TARGETOS),w32) +speedo_pkg_libpng_configure = \ + CPPFLAGS=\"-I$(idir)/include -DPNG_BUILD_DLL\" \ + LDFLAGS=\"-L$(idir)/lib\" LIBPNG_DEFINES=\"-DPNG_BUILD_DLL\" +else +speedo_pkg_libpng_configure = \ + CPPFLAGS=\"-I$(idir)/include\" \ + LDFLAGS=\"-L$(idir)/lib\" +endif + +ifneq ($(TARGETOS),w32) +speedo_pkg_gdk_pixbuf_configure = --without-libtiff --without-libjpeg +endif + +speedo_pkg_pixman_configure = \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib + +ifeq ($(TARGETOS),w32) +speedo_pkg_cairo_configure = \ + --disable-qt --disable-ft --disable-fc \ + --enable-win32 --enable-win32-font \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib +else +speedo_pkg_cairo_configure = \ + --disable-qt \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib +endif + +speedo_pkg_pango_configure = \ + --disable-gtk-doc \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib + +speedo_pkg_gtk__configure = \ + --disable-cups \ + CPPFLAGS=-I$(idir)/include \ + LDFLAGS=-L$(idir)/lib + + +# --------- + +all: all-speedo + +report: report-speedo + +clean: clean-speedo + +ifeq ($(TARGETOS),w32) +STRIP = i686-w64-mingw32-strip +else +STRIP = strip +endif +W32CC = i686-w64-mingw32-gcc + +-include config.mk + +# +# The generic speedo code +# + +MKDIR=mkdir +MAKENSIS=makensis +SHA1SUM := $(shell $(topsrc)/build-aux/getswdb.sh --find-sha1sum) +ifeq ($(SHA1SUM),false) +$(error The sha1sum tool is missing) +endif +SHA2SUM := $(shell $(topsrc)/build-aux/getswdb.sh --find-sha256sum) +ifeq ($(SHA2SUM),false) +$(error The sha256sum tool is missing) +endif + + +BUILD_ISODATE=$(shell date -u +%Y-%m-%d) +BUILD_DATESTR=$(subst -,,$(BUILD_ISODATE)) + +# The next two macros will work only after gnupg has been build. +ifeq ($(TARGETOS),w32) +INST_VERSION=$(shell head -1 $(idir)/INST_VERSION) +INST_PROD_VERSION=$(shell head -1 $(idir)/INST_PROD_VERSION) +endif + +# List with packages +speedo_build_list = $(speedo_spkgs) +speedo_w64_build_list = $(speedo_w64_spkgs) + +# To avoid running external commands during the read phase (":=" style +# assignments), we check that the targetos has been given +ifneq ($(TARGETOS),) + +# Determine build and host system +build := $(shell $(topsrc)/autogen.sh --silent --print-build) +ifeq ($(TARGETOS),w32) + speedo_autogen_buildopt := --build-w32 + speedo_autogen_buildopt6 := --build-w64 + host := $(shell $(topsrc)/autogen.sh --silent --print-host --build-w32) + host6:= $(shell $(topsrc)/autogen.sh --silent --print-host --build-w64) + speedo_host_build_option := --host=$(host) --build=$(build) + speedo_host_build_option6 := --host=$(host6) --build=$(build) + speedo_w32_cflags := -mms-bitfields +else + speedo_autogen_buildopt := + host := + speedo_host_build_option := + speedo_w32_cflags := +endif + +ifeq ($(MAKE_J),) + speedo_makeopt= +else + speedo_makeopt=-j$(MAKE_J) +endif + +# End non-empty TARGETOS +endif + + + +# The playground area is our scratch area, where we unpack, build and +# install the packages. +$(stampdir)/stamp-directories: + $(MKDIR) $(root) || true + $(MKDIR) $(stampdir) || true + $(MKDIR) $(sdir) || true + $(MKDIR) $(bdir) || true + $(MKDIR) $(idir) || true +ifeq ($(TARGETOS),w32) + $(MKDIR) $(bdir6) || true + $(MKDIR) $(idir6) || true +endif + touch $(stampdir)/stamp-directories + +# Frob the name $1 by converting all '-' and '+' characters to '_'. +define FROB_macro +$(subst +,_,$(subst -,_,$(1))) +endef + +# Get the variable $(1) (which may contain '-' and '+' characters). +define GETVAR +$($(call FROB_macro,$(1))) +endef + +# Set a couple of common variables. +define SETVARS + pkg="$(1)"; \ + git="$(call GETVAR,speedo_pkg_$(1)_git)"; \ + gitref="$(call GETVAR,speedo_pkg_$(1)_gitref)"; \ + tar="$(call GETVAR,speedo_pkg_$(1)_tar)"; \ + sha2="$(call GETVAR,$(1)_sha2)"; \ + sha1="$(call GETVAR,$(1)_sha1)"; \ + pkgsdir="$(sdir)/$(1)"; \ + if [ "$(1)" = "gnupg" ]; then \ + git=''; \ + gitref=''; \ + tar=''; \ + pkgsdir="$(topsrc)"; \ + fi; \ + pkgbdir="$(bdir)/$(1)"; \ + pkgcfg="$(call GETVAR,speedo_pkg_$(1)_configure)"; \ + tmp="$(speedo_w32_cflags) \ + $(call GETVAR,speedo_pkg_$(1)_extracflags)"; \ + if [ x$$$$(echo "$$$$tmp" | tr -d '[:space:]')x != xx ]; then \ + pkgextracflags="CFLAGS=\"$$$$tmp\""; \ + else \ + pkgextracflags=; \ + fi; \ + pkgmkdir="$(call GETVAR,speedo_pkg_$(1)_make_dir)"; \ + pkgmkargs="$(call GETVAR,speedo_pkg_$(1)_make_args)"; \ + pkgmkargs_inst="$(call GETVAR,speedo_pkg_$(1)_make_args_inst)"; \ + pkgmkargs_uninst="$(call GETVAR,speedo_pkg_$(1)_make_args_uninst)"; \ + export PKG_CONFIG="/usr/bin/pkg-config"; \ + export PKG_CONFIG_PATH="$(idir)/lib/pkgconfig"; \ + [ "$(TARGETOS)" != native ] && export PKG_CONFIG_LIBDIR=""; \ + export SYSROOT="$(idir)"; \ + export PATH="$(idir)/bin:$${PATH}"; \ + export LD_LIBRARY_PATH="$(idir)/lib:$${LD_LIBRARY_PATH}" +endef + +define SETVARS_W64 + pkg="$(1)"; \ + git="$(call GETVAR,speedo_pkg_$(1)_git)"; \ + gitref="$(call GETVAR,speedo_pkg_$(1)_gitref)"; \ + tar="$(call GETVAR,speedo_pkg_$(1)_tar)"; \ + sha2="$(call GETVAR,$(1)_sha2)"; \ + sha1="$(call GETVAR,$(1)_sha1)"; \ + pkgsdir="$(sdir)/$(1)"; \ + if [ "$(1)" = "gnupg" ]; then \ + git=''; \ + gitref=''; \ + tar=''; \ + pkgsdir="$(topsrc)"; \ + fi; \ + pkgbdir="$(bdir6)/$(1)"; \ + pkgcfg="$(call GETVAR,speedo_pkg_w64_$(1)_configure)"; \ + tmp="$(speedo_w32_cflags) \ + $(call GETVAR,speedo_pkg_$(1)_extracflags)"; \ + if [ x$$$$(echo "$$$$tmp" | tr -d '[:space:]')x != xx ]; then \ + pkgextracflags="CFLAGS=\"$$$$tmp\""; \ + else \ + pkgextracflags=; \ + fi; \ + pkgmkdir="$(call GETVAR,speedo_pkg_$(1)_make_dir)"; \ + pkgmkargs="$(call GETVAR,speedo_pkg_$(1)_make_args)"; \ + pkgmkargs_inst="$(call GETVAR,speedo_pkg_$(1)_make_args_inst)"; \ + pkgmkargs_uninst="$(call GETVAR,speedo_pkg_$(1)_make_args_uninst)"; \ + export PKG_CONFIG="/usr/bin/pkg-config"; \ + export PKG_CONFIG_PATH="$(idir6)/lib/pkgconfig"; \ + [ "$(TARGETOS)" != native ] && export PKG_CONFIG_LIBDIR=""; \ + export SYSROOT="$(idir6)"; \ + export PATH="$(idir6)/bin:$${PATH}"; \ + export LD_LIBRARY_PATH="$(idir6)/lib:$${LD_LIBRARY_PATH}" +endef + + +# Template for source packages. + +# Note that the gnupg package is special: The package source dir is +# the same as the topsrc dir and thus we need to detect the gnupg +# package and cd to that directory. We also test that no in-source build +# has been done. autogen.sh is not run for gnupg. +# +define SPKG_template + +$(stampdir)/stamp-$(1)-00-unpack: $(stampdir)/stamp-directories + @echo "speedo: /*" + @echo "speedo: * $(1)" + @echo "speedo: */" + @(set -e; cd $(sdir); \ + $(call SETVARS,$(1)); \ + if [ "$(WHAT)" = "this" ]; then \ + echo "speedo: using included source"; \ + elif [ "$(1)" = "gnupg" ]; then \ + cd $$$${pkgsdir}; \ + if [ -f config.log ]; then \ + echo "GnuPG has already been build in-source" >&2 ;\ + echo "Please run \"make distclean\" and retry" >&2 ;\ + exit 1 ; \ + fi; \ + echo "speedo: unpacking gnupg not needed"; \ + elif [ -n "$$$${git}" ]; then \ + echo "speedo: unpacking $(1) from $$$${git}:$$$${gitref}"; \ + git clone -b "$$$${gitref}" "$$$${git}" "$$$${pkg}"; \ + cd "$$$${pkg}"; \ + AUTOGEN_SH_SILENT=1 ./autogen.sh; \ + elif [ -n "$$$${tar}" ]; then \ + echo "speedo: unpacking $(1) from $$$${tar}"; \ + case "$$$${tar}" in \ + *.gz) pretar=zcat ;; \ + *.bz2) pretar=bzcat ;; \ + *.xz) pretar=xzcat ;; \ + *) pretar=cat ;; \ + esac; \ + [ -f tmp.tgz ] && rm tmp.tgz; \ + case "$$$${tar}" in \ + /*) $$$${pretar} < $$$${tar} | tar xf - ;; \ + *) wget -q -O - $$$${tar} | tee tmp.tgz \ + | $$$${pretar} | tar x$$$${opt}f - ;; \ + esac; \ + if [ -f tmp.tgz ]; then \ + if [ -n "$$$${sha2}" ]; then \ + tmp=$$$$($(SHA2SUM) /dev/null && \ + $(MAKE) --no-print-directory \ + $$$${pkgmkargs_uninst} uninstall V=0 ) || true;\ + if [ "$(1)" = "gnupg" ]; then \ + rm -fR "$$$${pkgbdir}" || true ;\ + else \ + rm -fR "$$$${pkgsdir}" "$$$${pkgbdir}" || true;\ + fi) + -rm -f $(stampdir)/stamp-final-$(1) $(stampdir)/stamp-$(1)-* + + +.PHONY : build-$(1) +build-$(1): $(stampdir)/stamp-final-$(1) + + +.PHONY : report-$(1) +report-$(1): + @($(call SETVARS,$(1)); \ + echo -n $(1):\ ; \ + if [ -n "$$$${git}" ]; then \ + if [ -e "$$$${pkgsdir}/.git" ]; then \ + cd "$$$${pkgsdir}" && \ + git describe ; \ + else \ + echo missing; \ + fi \ + elif [ -n "$$$${tar}" ]; then \ + base=`echo "$$$${tar}" | sed -e 's,^.*/,,' \ + | sed -e 's,\.tar.*$$$$,,'`; \ + echo $$$${base} ; \ + fi) + +endef + + +# Insert the template for each source package. +$(foreach spkg, $(speedo_spkgs), $(eval $(call SPKG_template,$(spkg)))) + +$(stampdir)/stamp-final: $(stampdir)/stamp-directories +ifeq ($(TARGETOS),w32) +$(stampdir)/stamp-final: $(addprefix $(stampdir)/stamp-w64-final-,$(speedo_w64_build_list)) +endif +$(stampdir)/stamp-final: $(addprefix $(stampdir)/stamp-final-,$(speedo_build_list)) + touch $(stampdir)/stamp-final + +all-speedo: $(stampdir)/stamp-final + +report-speedo: $(addprefix report-,$(speedo_build_list)) + +# Just to check if we catched all stamps. +clean-stamps: + $(RM) -fR $(stampdir) + +clean-speedo: + $(RM) -fR PLAY + + +# +# Windows installer +# +# {{{ +ifeq ($(TARGETOS),w32) + +dist-source: installer + for i in 00 01 02 03; do sleep 1;touch PLAY/stamps/stamp-*-${i}-*;done + (set -e;\ + tarname="$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).tar" ;\ + [ -f "$$tarname" ] && rm "$$tarname" ;\ + tar -C $(topsrc) -cf "$$tarname" --exclude-backups --exclude-vc \ + --transform='s,^\./,$(INST_NAME)-$(INST_VERSION)/,' \ + --anchored --exclude './PLAY' . ;\ + tar --totals -rf "$$tarname" --exclude-backups --exclude-vc \ + --transform='s,^,$(INST_NAME)-$(INST_VERSION)/,' \ + PLAY/stamps/stamp-*-00-unpack PLAY/src swdb.lst swdb.lst.sig ;\ + [ -f "$$tarname".xz ] && rm "$$tarname".xz;\ + xz "$$tarname" ;\ + ) + + +# Extract the two latest news entries. */ +$(bdir)/NEWS.tmp: $(topsrc)/NEWS + awk '/^Notewo/ {if(okay>1){exit}; okay++};okay {print $0}' \ + <$(topsrc)/NEWS >$(bdir)/NEWS.tmp + +$(bdir)/README.txt: $(bdir)/NEWS.tmp $(topsrc)/README $(w32src)/README.txt \ + $(w32src)/pkg-copyright.txt + sed -e '/^;.*/d;' \ + -e '/!NEWSFILE!/{r $(bdir)/NEWS.tmp' -e 'd;}' \ + -e '/!GNUPGREADME!/{r $(topsrc)/README' -e 'd;}' \ + -e '/!PKG-COPYRIGHT!/{r $(w32src)/pkg-copyright.txt' -e 'd;}' \ + -e 's,!VERSION!,$(INST_VERSION),g' \ + < $(w32src)/README.txt \ + | sed -e '/^#/d' \ + | awk '{printf "%s\r\n", $$0}' >$(bdir)/README.txt + +$(bdir)/g4wihelp.dll: $(w32src)/g4wihelp.c $(w32src)/exdll.h + (set -e; cd $(bdir); \ + $(W32CC) -I. -shared -O2 -o g4wihelp.dll $(w32src)/g4wihelp.c \ + -lwinmm -lgdi32; \ + $(STRIP) g4wihelp.dll) + +w32_insthelpers: $(bdir)/g4wihelp.dll + +$(bdir)/inst-options.ini: $(w32src)/inst-options.ini + cat $(w32src)/inst-options.ini >$(bdir)/inst-options.ini + +extra_installer_options = +ifeq ($(WITH_GUI),1) +extra_installer_options += -DWITH_GUI=1 +endif + +installer: all w32_insthelpers $(w32src)/inst-options.ini $(bdir)/README.txt + $(MAKENSIS) -V2 \ + -DINST_DIR=$(idir) \ + -DINST6_DIR=$(idir6) \ + -DBUILD_DIR=$(bdir) \ + -DTOP_SRCDIR=$(topsrc) \ + -DW32_SRCDIR=$(w32src) \ + -DBUILD_ISODATE=$(BUILD_ISODATE) \ + -DBUILD_DATESTR=$(BUILD_DATESTR) \ + -DNAME=$(INST_NAME) \ + -DVERSION=$(INST_VERSION) \ + -DPROD_VERSION=$(INST_PROD_VERSION) \ + $(extra_installer_options) $(w32src)/inst.nsi + @echo "Ready: $(idir)/$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).exe" + + +define MKSWDB_commands + ( pref="#+macro: gnupg21_w32_" ;\ + echo "$${pref}ver $(INST_VERSION)_$(BUILD_DATESTR)" ;\ + echo "$${pref}date $(2)" ;\ + echo "$${pref}size $$(wc -c <$(1)|awk '{print int($$1/1024)}')k";\ + echo "$${pref}sha1 $$(sha1sum <$(1)|cut -d' ' -f1)" ;\ + echo "$${pref}sha2 $$(sha256sum <$(1)|cut -d' ' -f1)" ;\ + ) | tee $(1).swdb +endef + + +# Build the installer from the source tarball. +installer-from-source: dist-source + (set -e;\ + [ -d PLAY-release ] && rm -rf PLAY-release; \ + mkdir PLAY-release;\ + cd PLAY-release; \ + tar xJf "../$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).tar.xz";\ + cd $(INST_NAME)-$(INST_VERSION); \ + $(MAKE) -f build-aux/speedo.mk this-w32-installer SELFCHECK=0;\ + reldate="$$(date -u +%Y-%m-%d)" ;\ + exefile="$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).exe" ;\ + cp "PLAY/inst/$$exefile" ../.. ;\ + exefile="../../$$exefile" ;\ + $(call MKSWDB_commands,$${exefile},$${reldate}); \ + ) + +# This target repeats some of the installer-from-source steps but it +# is intended to be called interactively, so that the passphrase can be +# entered. +sign-installer: + @(set -e; \ + cd PLAY-release; \ + cd $(INST_NAME)-$(INST_VERSION); \ + reldate="$$(date -u +%Y-%m-%d)" ;\ + exefile="$(INST_NAME)-$(INST_VERSION)_$(BUILD_DATESTR).exe" ;\ + echo "speedo: /*" ;\ + echo "speedo: * Signing installer" ;\ + echo "speedo: * Key: $(AUTHENTICODE_KEY)";\ + echo "speedo: */" ;\ + osslsigncode sign -pkcs12 $(AUTHENTICODE_KEY) -askpass \ + -h sha256 -in "PLAY/inst/$$exefile" -out "../../$$exefile" ;\ + exefile="../../$$exefile" ;\ + $(call MKSWDB_commands,$${exefile},$${reldate}); \ + echo "speedo: /*" ;\ + echo "speedo: * Verification result" ;\ + echo "speedo: */" ;\ + osslsigncode verify $${exefile} \ + ) + + + +endif +# }}} W32 + + +# +# Check availibility of standard tools +# +check-tools: + + +# +# Mark phony targets +# +.PHONY: all all-speedo report-speedo clean-stamps clean-speedo installer \ + w32_insthelpers check-tools diff --git a/build-aux/speedo/patches/atk-1.32.0.patch b/build-aux/speedo/patches/atk-1.32.0.patch new file mode 100755 index 0000000..51d7975 --- /dev/null +++ b/build-aux/speedo/patches/atk-1.32.0.patch @@ -0,0 +1,671 @@ +#! /bin/sh +patch -p1 -l -f $* < $0 +exit $? + + +diff -urpb orig/atk-1.32.0/atk/atkaction.c atk/atk/atkaction.c +--- orig/atk-1.32.0/atk/atkaction.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkaction.c 2014-02-17 12:30:53.263192763 +0100 +@@ -101,7 +101,7 @@ atk_action_get_n_actions (AtkAction *ob + * Returns a description string, or %NULL + * if @action does not implement this interface. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_action_get_description (AtkAction *obj, + gint i) + { +@@ -140,7 +140,7 @@ atk_action_get_description (AtkAction *o + * Returns a name string, or %NULL + * if @action does not implement this interface. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_action_get_name (AtkAction *obj, + gint i) + { +@@ -166,7 +166,7 @@ atk_action_get_name (AtkAction *obj, + * Returns a name string, or %NULL + * if @action does not implement this interface. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_action_get_localized_name (AtkAction *obj, + gint i) + { +@@ -203,7 +203,7 @@ atk_action_get_localized_name (AtkAction + * if there is no keybinding for this action. + * + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_action_get_keybinding (AtkAction *obj, + gint i) + { +Only in atk/atk: atkaction.c~ +diff -urpb orig/atk-1.32.0/atk/atkaction.h atk/atk/atkaction.h +--- orig/atk-1.32.0/atk/atkaction.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkaction.h 2014-02-17 12:30:58.907192071 +0100 +@@ -55,16 +55,16 @@ struct _AtkActionIface + gboolean (*do_action) (AtkAction *action, + gint i); + gint (*get_n_actions) (AtkAction *action); +- G_CONST_RETURN gchar* (*get_description) (AtkAction *action, ++ const gchar* (*get_description) (AtkAction *action, + gint i); +- G_CONST_RETURN gchar* (*get_name) (AtkAction *action, ++ const gchar* (*get_name) (AtkAction *action, + gint i); +- G_CONST_RETURN gchar* (*get_keybinding) (AtkAction *action, ++ const gchar* (*get_keybinding) (AtkAction *action, + gint i); + gboolean (*set_description) (AtkAction *action, + gint i, + const gchar *desc); +- G_CONST_RETURN gchar* (*get_localized_name)(AtkAction *action, ++ const gchar* (*get_localized_name)(AtkAction *action, + gint i); + AtkFunction pad2; + }; +@@ -85,11 +85,11 @@ GType atk_action_get_type (void); + gboolean atk_action_do_action (AtkAction *action, + gint i); + gint atk_action_get_n_actions (AtkAction *action); +-G_CONST_RETURN gchar* atk_action_get_description (AtkAction *action, ++const gchar* atk_action_get_description (AtkAction *action, + gint i); +-G_CONST_RETURN gchar* atk_action_get_name (AtkAction *action, ++const gchar* atk_action_get_name (AtkAction *action, + gint i); +-G_CONST_RETURN gchar* atk_action_get_keybinding (AtkAction *action, ++const gchar* atk_action_get_keybinding (AtkAction *action, + gint i); + gboolean atk_action_set_description (AtkAction *action, + gint i, +@@ -97,7 +97,7 @@ gboolean atk_action_set_des + + /* NEW in ATK 1.1: */ + +-G_CONST_RETURN gchar* atk_action_get_localized_name (AtkAction *action, ++const gchar* atk_action_get_localized_name (AtkAction *action, + gint i); + + /* +Only in atk/atk: atkaction.h~ +diff -urpb orig/atk-1.32.0/atk/atkdocument.c atk/atk/atkdocument.c +--- orig/atk-1.32.0/atk/atkdocument.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkdocument.c 2014-02-17 12:30:58.535192391 +0100 +@@ -93,7 +93,7 @@ atk_document_base_init (AtkDocumentIface + * + * Returns: a string indicating the document type + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_document_get_document_type (AtkDocument *document) + { + AtkDocumentIface *iface; +@@ -155,7 +155,7 @@ atk_document_get_document (AtkDocument * + * locale of the document content as a whole, or NULL if + * the document content does not specify a locale. + **/ +-G_CONST_RETURN gchar * ++const gchar * + atk_document_get_locale (AtkDocument *document) + { + AtkDocumentIface *iface; +@@ -219,7 +219,7 @@ atk_document_get_attributes (AtkDocument + * document, or NULL if a value for #attribute_name has not been specified + * for this document. + */ +-G_CONST_RETURN gchar * ++const gchar * + atk_document_get_attribute_value (AtkDocument *document, + const gchar *attribute_name) + { +Only in atk/atk: atkdocument.c~ +diff -urpb orig/atk-1.32.0/atk/atkdocument.h atk/atk/atkdocument.h +--- orig/atk-1.32.0/atk/atkdocument.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkdocument.h 2014-02-17 12:31:31.691190631 +0100 +@@ -49,12 +49,12 @@ typedef struct _AtkDocumentIface AtkDocu + struct _AtkDocumentIface + { + GTypeInterface parent; +- G_CONST_RETURN gchar* ( *get_document_type) (AtkDocument *document); ++ const gchar* ( *get_document_type) (AtkDocument *document); + gpointer ( *get_document) (AtkDocument *document); + +- G_CONST_RETURN gchar* ( *get_document_locale) (AtkDocument *document); ++ const gchar* ( *get_document_locale) (AtkDocument *document); + AtkAttributeSet * ( *get_document_attributes) (AtkDocument *document); +- G_CONST_RETURN gchar* ( *get_document_attribute_value) (AtkDocument *document, ++ const gchar* ( *get_document_attribute_value) (AtkDocument *document, + const gchar *attribute_name); + gboolean ( *set_document_attribute) (AtkDocument *document, + const gchar *attribute_name, +@@ -68,11 +68,11 @@ struct _AtkDocumentIface + + GType atk_document_get_type (void); + +-G_CONST_RETURN gchar* atk_document_get_document_type (AtkDocument *document); ++const gchar* atk_document_get_document_type (AtkDocument *document); + gpointer atk_document_get_document (AtkDocument *document); +-G_CONST_RETURN gchar* atk_document_get_locale (AtkDocument *document); ++const gchar* atk_document_get_locale (AtkDocument *document); + AtkAttributeSet* atk_document_get_attributes (AtkDocument *document); +-G_CONST_RETURN gchar* atk_document_get_attribute_value (AtkDocument *document, ++const gchar* atk_document_get_attribute_value (AtkDocument *document, + const gchar *attribute_name); + gboolean atk_document_set_attribute_value (AtkDocument *document, + const gchar *attribute_name, +Only in atk/atk: atkdocument.h~ +diff -urpb orig/atk-1.32.0/atk/atkimage.c atk/atk/atkimage.c +--- orig/atk-1.32.0/atk/atkimage.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkimage.c 2014-02-17 12:30:58.119192299 +0100 +@@ -46,7 +46,7 @@ atk_image_get_type (void) + * + * Returns: a string representing the image description + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_image_get_image_description (AtkImage *image) + { + AtkImageIface *iface; +@@ -192,7 +192,7 @@ atk_image_get_image_position (AtkImage * + * Returns a string corresponding to the POSIX LC_MESSAGES locale used by the image description, or NULL if the image does not specify a locale. + * + */ +-G_CONST_RETURN gchar* ++const gchar* + atk_image_get_image_locale (AtkImage *image) + { + +Only in atk/atk: atkimage.c~ +diff -urpb orig/atk-1.32.0/atk/atkimage.h atk/atk/atkimage.h +--- orig/atk-1.32.0/atk/atkimage.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkimage.h 2014-02-17 12:28:31.599200223 +0100 +@@ -53,13 +53,13 @@ struct _AtkImageIface + gint *x, + gint *y, + AtkCoordType coord_type); +- G_CONST_RETURN gchar* ( *get_image_description) (AtkImage *image); ++ const gchar* ( *get_image_description) (AtkImage *image); + void ( *get_image_size) (AtkImage *image, + gint *width, + gint *height); + gboolean ( *set_image_description) (AtkImage *image, + const gchar *description); +- G_CONST_RETURN gchar* ( *get_image_locale) (AtkImage *image); ++ const gchar* ( *get_image_locale) (AtkImage *image); + + AtkFunction pad1; + +@@ -67,7 +67,7 @@ struct _AtkImageIface + + GType atk_image_get_type (void); + +-G_CONST_RETURN gchar* atk_image_get_image_description (AtkImage *image); ++const gchar* atk_image_get_image_description (AtkImage *image); + + void atk_image_get_image_size (AtkImage *image, + gint *width, +@@ -80,7 +80,7 @@ void atk_image_get_image_position + gint *y, + AtkCoordType coord_type); + +-G_CONST_RETURN gchar* atk_image_get_image_locale (AtkImage *image); ++const gchar* atk_image_get_image_locale (AtkImage *image); + + G_END_DECLS + +Only in atk/atk: atkimage.h~ +diff -urpb orig/atk-1.32.0/atk/atkobject.c atk/atk/atkobject.c +--- orig/atk-1.32.0/atk/atkobject.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkobject.c 2014-02-17 12:28:39.467199803 +0100 +@@ -285,9 +285,9 @@ static void atk_object_real_g + GValue *value, + GParamSpec *pspec); + static void atk_object_finalize (GObject *object); +-static G_CONST_RETURN gchar* ++static const gchar* + atk_object_real_get_name (AtkObject *object); +-static G_CONST_RETURN gchar* ++static const gchar* + atk_object_real_get_description + (AtkObject *object); + static AtkObject* atk_object_real_get_parent (AtkObject *object); +@@ -692,7 +692,7 @@ atk_implementor_get_type (void) + * + * Returns: a character string representing the accessible name of the object. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_object_get_name (AtkObject *accessible) + { + AtkObjectClass *klass; +@@ -716,7 +716,7 @@ atk_object_get_name (AtkObject *accessib + * of the accessible. + * + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_object_get_description (AtkObject *accessible) + { + AtkObjectClass *klass; +@@ -1123,7 +1123,7 @@ atk_object_notify_state_change (AtkObjec + AtkState state, + gboolean value) + { +- G_CONST_RETURN gchar* name; ++ const gchar* name; + + g_return_if_fail (ATK_IS_OBJECT (accessible)); + +@@ -1319,13 +1319,13 @@ atk_object_finalize (GObject *object) + G_OBJECT_CLASS (parent_class)->finalize (object); + } + +-static G_CONST_RETURN gchar* ++static const gchar* + atk_object_real_get_name (AtkObject *object) + { + return object->name; + } + +-static G_CONST_RETURN gchar* ++static const gchar* + atk_object_real_get_description (AtkObject *object) + { + return object->description; +@@ -1487,7 +1487,7 @@ atk_object_notify (GObject *obj, + * + * Returns: the string describing the AtkRole + */ +-G_CONST_RETURN gchar* ++const gchar* + atk_role_get_name (AtkRole role) + { + if (role >= 0 && role < ATK_ROLE_LAST_DEFINED) +@@ -1514,7 +1514,7 @@ atk_role_get_name (AtkRole role) + * + * Returns: the localized string describing the AtkRole + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_role_get_localized_name (AtkRole role) + { + gettext_initialization (); +Only in atk/atk: atkobject.c~ +diff -urpb orig/atk-1.32.0/atk/atkobject.h atk/atk/atkobject.h +--- orig/atk-1.32.0/atk/atkobject.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkobject.h 2014-02-17 12:28:48.851199302 +0100 +@@ -381,11 +381,11 @@ struct _AtkObjectClass + /* + * Gets the accessible name of the object + */ +- G_CONST_RETURN gchar* (* get_name) (AtkObject *accessible); ++ const gchar* (* get_name) (AtkObject *accessible); + /* + * Gets the accessible description of the object + */ +- G_CONST_RETURN gchar* (* get_description) (AtkObject *accessible); ++ const gchar* (* get_description) (AtkObject *accessible); + /* + * Gets the accessible parent of the object + */ +@@ -535,8 +535,8 @@ AtkObject* atk_implementor_ + * Properties directly supported by AtkObject + */ + +-G_CONST_RETURN gchar* atk_object_get_name (AtkObject *accessible); +-G_CONST_RETURN gchar* atk_object_get_description (AtkObject *accessible); ++const gchar* atk_object_get_name (AtkObject *accessible); ++const gchar* atk_object_get_description (AtkObject *accessible); + AtkObject* atk_object_get_parent (AtkObject *accessible); + gint atk_object_get_n_accessible_children (AtkObject *accessible); + AtkObject* atk_object_ref_accessible_child (AtkObject *accessible, +@@ -571,7 +571,7 @@ void atk_object_notify_s + void atk_object_initialize (AtkObject *accessible, + gpointer data); + +-G_CONST_RETURN gchar* atk_role_get_name (AtkRole role); ++const gchar* atk_role_get_name (AtkRole role); + AtkRole atk_role_for_name (const gchar *name); + + +@@ -582,7 +582,7 @@ gboolean atk_object_add_rel + gboolean atk_object_remove_relationship (AtkObject *object, + AtkRelationType relationship, + AtkObject *target); +-G_CONST_RETURN gchar* atk_role_get_localized_name (AtkRole role); ++const gchar* atk_role_get_localized_name (AtkRole role); + + /* */ + +Only in atk/atk: atkobject.h~ +diff -urpb orig/atk-1.32.0/atk/atkrelation.c atk/atk/atkrelation.c +--- orig/atk-1.32.0/atk/atkrelation.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkrelation.c 2014-02-17 12:29:04.307198532 +0100 +@@ -130,7 +130,7 @@ atk_relation_type_register (const gchar + * + * Returns: the string describing the AtkRelationType + */ +-G_CONST_RETURN gchar* ++const gchar* + atk_relation_type_get_name (AtkRelationType type) + { + GTypeClass *type_class; +Only in atk/atk: atkrelation.c~ +diff -urpb orig/atk-1.32.0/atk/atkrelation.h atk/atk/atkrelation.h +--- orig/atk-1.32.0/atk/atkrelation.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkrelation.h 2014-02-17 12:29:12.167198142 +0100 +@@ -61,7 +61,7 @@ struct _AtkRelationClass + GType atk_relation_get_type (void); + + AtkRelationType atk_relation_type_register (const gchar *name); +-G_CONST_RETURN gchar* atk_relation_type_get_name (AtkRelationType type); ++const gchar* atk_relation_type_get_name (AtkRelationType type); + AtkRelationType atk_relation_type_for_name (const gchar *name); + + /* +Only in atk/atk: atkrelation.h~ +diff -urpb orig/atk-1.32.0/atk/atkstate.c atk/atk/atkstate.c +--- orig/atk-1.32.0/atk/atkstate.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkstate.c 2014-02-17 12:29:19.023197754 +0100 +@@ -57,7 +57,7 @@ atk_state_type_register (const gchar *na + * + * Returns: the string describing the AtkStateType + */ +-G_CONST_RETURN gchar* ++const gchar* + atk_state_type_get_name (AtkStateType type) + { + GTypeClass *type_class; +Only in atk/atk: atkstate.c~ +diff -urpb orig/atk-1.32.0/atk/atkstate.h atk/atk/atkstate.h +--- orig/atk-1.32.0/atk/atkstate.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkstate.h 2014-02-17 12:26:44.459205944 +0100 +@@ -170,7 +170,7 @@ typedef guint64 AtkState; + + AtkStateType atk_state_type_register (const gchar *name); + +-G_CONST_RETURN gchar* atk_state_type_get_name (AtkStateType type); ++const gchar* atk_state_type_get_name (AtkStateType type); + AtkStateType atk_state_type_for_name (const gchar *name); + + G_END_DECLS +Only in atk/atk: atkstate.h~ +diff -urpb orig/atk-1.32.0/atk/atkstreamablecontent.c atk/atk/atkstreamablecontent.c +--- orig/atk-1.32.0/atk/atkstreamablecontent.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkstreamablecontent.c 2014-02-17 12:30:57.659192412 +0100 +@@ -73,7 +73,7 @@ atk_streamable_content_get_n_mime_types + * Returns : a gchar* representing the specified mime type; the caller + * should not free the character string. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_streamable_content_get_mime_type (AtkStreamableContent *streamable, + gint i) + { +Only in atk/atk: atkstreamablecontent.c~ +diff -urpb orig/atk-1.32.0/atk/atkstreamablecontent.h atk/atk/atkstreamablecontent.h +--- orig/atk-1.32.0/atk/atkstreamablecontent.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkstreamablecontent.h 2014-02-17 12:29:49.487196042 +0100 +@@ -54,11 +54,11 @@ struct _AtkStreamableContentIface + * at index 0 should be considered the "default" data type for the stream. + * + * This assumes that the strings for the mime types are stored in the +- * AtkStreamableContent. Alternatively the G_CONST_RETURN could be removed ++ * AtkStreamableContent. Alternatively the const could be removed + * and the caller would be responsible for calling g_free() on the + * returned value. + */ +- G_CONST_RETURN gchar* (* get_mime_type) (AtkStreamableContent *streamable, ++ const gchar* (* get_mime_type) (AtkStreamableContent *streamable, + gint i); + /* + * One possible implementation for this method is that it constructs the +@@ -80,7 +80,7 @@ struct _AtkStreamableContentIface + * constructed. Note that it is possible for get_uri to return NULL but for + * get_stream to work nonetheless, since not all GIOChannels connect to URIs. + */ +- G_CONST_RETURN gchar* (* get_uri) (AtkStreamableContent *streamable, ++ const gchar* (* get_uri) (AtkStreamableContent *streamable, + const gchar *mime_type); + + +@@ -92,7 +92,7 @@ GType atk_streamable_co + + gint atk_streamable_content_get_n_mime_types (AtkStreamableContent *streamable); + +-G_CONST_RETURN gchar* atk_streamable_content_get_mime_type (AtkStreamableContent *streamable, ++const gchar* atk_streamable_content_get_mime_type (AtkStreamableContent *streamable, + gint i); + GIOChannel* atk_streamable_content_get_stream (AtkStreamableContent *streamable, + const gchar *mime_type); +Only in atk/atk: atkstreamablecontent.h~ +diff -urpb orig/atk-1.32.0/atk/atktable.c atk/atk/atktable.c +--- orig/atk-1.32.0/atk/atktable.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atktable.c 2014-02-17 12:30:57.319192444 +0100 +@@ -300,7 +300,7 @@ atk_table_get_n_columns (AtkTable *table + * Returns: a gchar* representing the column description, or %NULL + * if value does not implement this interface. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_table_get_column_description (AtkTable *table, + gint column) + { +@@ -404,7 +404,7 @@ atk_table_get_n_rows (AtkTable *table) + * Returns: a gchar* representing the row description, or %NULL + * if value does not implement this interface. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_table_get_row_description (AtkTable *table, + gint row) + { +Only in atk/atk: atktable.c~ +diff -urpb orig/atk-1.32.0/atk/atktable.h atk/atk/atktable.h +--- orig/atk-1.32.0/atk/atktable.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atktable.h 2014-02-17 12:30:08.035195073 +0100 +@@ -69,12 +69,12 @@ struct _AtkTableIface + gint column); + AtkObject* + (* get_caption) (AtkTable *table); +- G_CONST_RETURN gchar* ++ const gchar* + (* get_column_description) (AtkTable *table, + gint column); + AtkObject* (* get_column_header) (AtkTable *table, + gint column); +- G_CONST_RETURN gchar* ++ const gchar* + (* get_row_description) (AtkTable *table, + gint row); + AtkObject* (* get_row_header) (AtkTable *table, +@@ -163,12 +163,12 @@ gint atk_table_get_row_exte + gint column); + AtkObject* + atk_table_get_caption (AtkTable *table); +-G_CONST_RETURN gchar* ++const gchar* + atk_table_get_column_description (AtkTable *table, + gint column); + AtkObject* atk_table_get_column_header (AtkTable *table, + gint column); +-G_CONST_RETURN gchar* ++const gchar* + atk_table_get_row_description (AtkTable *table, + gint row); + AtkObject* atk_table_get_row_header (AtkTable *table, +Only in atk/atk: atktable.h~ +diff -urpb orig/atk-1.32.0/atk/atktext.c atk/atk/atktext.c +--- orig/atk-1.32.0/atk/atktext.c 2010-09-27 09:07:09.000000000 +0200 ++++ atk/atk/atktext.c 2014-02-17 12:30:56.871192495 +0100 +@@ -1054,7 +1054,7 @@ atk_text_attribute_register (const gchar + * + * Returns: a string containing the name; this string should not be freed + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_text_attribute_get_name (AtkTextAttribute attr) + { + GTypeClass *type_class; +@@ -1150,7 +1150,7 @@ atk_text_attribute_for_name (const gchar + * Returns: a string containing the value; this string should not be freed; + * NULL is returned if there are no values maintained for the attr value. + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_text_attribute_get_value (AtkTextAttribute attr, + gint index) + { +Only in atk/atk: atktext.c~ +diff -urpb orig/atk-1.32.0/atk/atktext.h atk/atk/atktext.h +--- orig/atk-1.32.0/atk/atktext.h 2010-09-27 09:07:09.000000000 +0200 ++++ atk/atk/atktext.h 2014-02-17 12:30:56.475192626 +0100 +@@ -355,9 +355,9 @@ AtkTextRange** atk_text_get_bounded_ran + AtkTextClipType y_clip_type); + void atk_text_free_ranges (AtkTextRange **ranges); + void atk_attribute_set_free (AtkAttributeSet *attrib_set); +-G_CONST_RETURN gchar* atk_text_attribute_get_name (AtkTextAttribute attr); ++const gchar* atk_text_attribute_get_name (AtkTextAttribute attr); + AtkTextAttribute atk_text_attribute_for_name (const gchar *name); +-G_CONST_RETURN gchar* atk_text_attribute_get_value (AtkTextAttribute attr, ++const gchar* atk_text_attribute_get_value (AtkTextAttribute attr, + gint index_); + + G_END_DECLS +Only in atk/atk: atktext.h~ +diff -urpb orig/atk-1.32.0/atk/atkutil.c atk/atk/atkutil.c +--- orig/atk-1.32.0/atk/atkutil.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkutil.c 2014-02-17 12:30:33.651193705 +0100 +@@ -340,7 +340,7 @@ atk_get_focus_object (void) + * + * Returns: name string for the GUI toolkit implementing ATK for this application + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_get_toolkit_name (void) + { + const gchar *retval; +@@ -365,7 +365,7 @@ atk_get_toolkit_name (void) + * + * Returns: version string for the GUI toolkit implementing ATK for this application + **/ +-G_CONST_RETURN gchar* ++const gchar* + atk_get_toolkit_version (void) + { + const gchar *retval; +@@ -391,7 +391,7 @@ atk_get_toolkit_version (void) + * Returns: version string for ATK + **/ + +-G_CONST_RETURN gchar * ++const gchar * + atk_get_version (void) + { + return VERSION; +Only in atk/atk: atkutil.c~ +diff -urpb orig/atk-1.32.0/atk/atkutil.h atk/atk/atkutil.h +--- orig/atk-1.32.0/atk/atkutil.h 2010-09-06 08:45:45.000000000 +0200 ++++ atk/atk/atkutil.h 2014-02-17 12:30:40.635193333 +0100 +@@ -147,8 +147,8 @@ struct _AtkUtilClass + gpointer data); + void (* remove_key_event_listener) (guint listener_id); + AtkObject* (* get_root) (void); +- G_CONST_RETURN gchar* (* get_toolkit_name) (void); +- G_CONST_RETURN gchar* (* get_toolkit_version) (void); ++ const gchar* (* get_toolkit_name) (void); ++ const gchar* (* get_toolkit_version) (void); + }; + GType atk_util_get_type (void); + +@@ -229,17 +229,17 @@ AtkObject* atk_get_focus_object (void); + /* + * Returns name string for the GUI toolkit. + */ +-G_CONST_RETURN gchar *atk_get_toolkit_name (void); ++const gchar *atk_get_toolkit_name (void); + + /* + * Returns version string for the GUI toolkit. + */ +-G_CONST_RETURN gchar *atk_get_toolkit_version (void); ++const gchar *atk_get_toolkit_version (void); + + /* + * Gets the current version of ATK + */ +-G_CONST_RETURN gchar *atk_get_version (void); ++const gchar *atk_get_version (void); + + /* --- GType boilerplate --- */ + /* convenience macros for atk type implementations, which for a type GtkGadgetAccessible will: +Only in atk/atk: atkutil.h~ +diff -urpb orig/atk-1.32.0/tests/testrelation.c atk/tests/testrelation.c +--- orig/atk-1.32.0/tests/testrelation.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/tests/testrelation.c 2014-02-17 12:53:42.095119569 +0100 +@@ -28,7 +28,7 @@ static gboolean + test_relation (void) + { + AtkRelationType type1, type2; +- G_CONST_RETURN gchar *name; ++ const gchar *name; + AtkObject *obj; + gboolean ret_value; + AtkRelationSet *set; +@@ -169,7 +169,7 @@ static gboolean + test_role (void) + { + AtkRole role1, role2; +- G_CONST_RETURN gchar *name; ++ const gchar *name; + + name = atk_role_get_name (ATK_ROLE_PAGE_TAB); + g_return_val_if_fail (name, FALSE); +@@ -230,7 +230,7 @@ static gboolean + test_text_attr (void) + { + AtkTextAttribute attr1, attr2; +- G_CONST_RETURN gchar *name; ++ const gchar *name; + + name = atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP); + g_return_val_if_fail (name, FALSE); +Only in atk/tests/: testrelation.c~ +diff -urpb orig/atk-1.32.0/tests/teststateset.c atk/tests/teststateset.c +--- orig/atk-1.32.0/tests/teststateset.c 2010-09-06 08:45:45.000000000 +0200 ++++ atk/tests/teststateset.c 2014-02-17 12:53:55.675118832 +0100 +@@ -208,7 +208,7 @@ static gboolean + test_state (void) + { + AtkStateType type1, type2; +- G_CONST_RETURN gchar *name; ++ const gchar *name; + + name = atk_state_type_get_name (ATK_STATE_VISIBLE); + g_return_val_if_fail (name, FALSE); + + +--- orig/atk-1.32.0/atk/Makefile.in 2010-09-27 09:53:57.000000000 +0200 ++++ atk/atk/Makefile.in 2014-02-17 12:52:40.443122866 +0100 +@@ -40,7 +40,7 @@ host_triplet = @host@ + @HAVE_INTROSPECTION_TRUE@am__append_2 = $(gir_DATA) $(typelibs_DATA) + + # ---------- Win32 stuff ---------- +-@OS_WIN32_TRUE@am__append_3 = -export-symbols $(srcdir)/atk.def -no-undefined -Wl,atk-win32-res.o ++@OS_WIN32_TRUE@am__append_3 = -export-symbols atk.def -no-undefined -Wl,atk-win32-res.o + @OS_WIN32_FALSE@libatk_1_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + @OS_WIN32_FALSE@ $(am__DEPENDENCIES_1) + subdir = atk +@@ -861,7 +861,7 @@ s-enum-types-c: @REBUILD@ $(atk_headers) + @HAVE_INTROSPECTION_TRUE@Atk-1.0.gir: libatk-1.0.la Makefile + + @OS_WIN32_TRUE@install-def-file: +-@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/atk.def $(DESTDIR)$(libdir)/atk-1.0.def ++@OS_WIN32_TRUE@ $(INSTALL) atk.def $(DESTDIR)$(libdir)/atk-1.0.def + @OS_WIN32_TRUE@uninstall-def-file: + @OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/atk-1.0.def + @OS_WIN32_FALSE@install-def-file: diff --git a/build-aux/speedo/patches/libiconv-1.14.patch b/build-aux/speedo/patches/libiconv-1.14.patch new file mode 100755 index 0000000..5e60689 --- /dev/null +++ b/build-aux/speedo/patches/libiconv-1.14.patch @@ -0,0 +1,19 @@ +#! /bin/sh +patch -p0 -l -f $* < $0 +exit $? + +On some systems the gets macro has been removed and thus the test +leads to an unresolved symbol error. + +--- srclib/stdio.in.h~ 2011-08-07 15:42:06.000000000 +0200 ++++ srclib/stdio.in.h 2014-09-04 13:07:07.079024312 +0200 +@@ -691,11 +691,6 @@ + _GL_CXXALIAS_SYS (gets, char *, (char *s)); + # undef gets + # endif +-_GL_CXXALIASWARN (gets); +-/* It is very rare that the developer ever has full control of stdin, +- so any use of gets warrants an unconditional warning. Assume it is +- always declared, since it is required by C89. */ +-_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead"); + #endif diff --git a/build-aux/speedo/patches/pango-1.29.4.patch b/build-aux/speedo/patches/pango-1.29.4.patch new file mode 100755 index 0000000..edeee85 --- /dev/null +++ b/build-aux/speedo/patches/pango-1.29.4.patch @@ -0,0 +1,27 @@ +#! /bin/sh +patch -p0 -l -f $* < $0 +exit $? + +Without that patch the module is build with wrong symbols and thus +can't be loaded by pango. I don't know why they have this defines +just in this module. It entirely defeats the feature of loading +modules dynamically - maybe this was just a quick hack for including +the code directly - however, I was not able to make that work either. + + +--- modules/basic/basic-win32.c~ 2011-09-28 16:34:33.000000000 +0200 ++++ modules/basic/basic-win32.c 2014-02-20 20:01:10.107723565 +0100 +@@ -33,9 +33,10 @@ + + extern HFONT _pango_win32_font_get_hfont (PangoFont *font); + +-#ifndef PANGO_MODULE_PREFIX +-#define PANGO_MODULE_PREFIX _pango_basic_win32 +-#endif ++/* #ifndef PANGO_MODULE_PREFIX */ ++/* #define PANGO_MODULE_PREFIX _pango_basic_win32 */ ++/* #endif */ ++#undef PANGO_MODULE_PREFIX + + #include "pango-engine.h" + #include "pango-utils.h" diff --git a/build-aux/speedo/patches/sqlite.patch b/build-aux/speedo/patches/sqlite.patch new file mode 100755 index 0000000..f81a414 --- /dev/null +++ b/build-aux/speedo/patches/sqlite.patch @@ -0,0 +1,42 @@ +#! /bin/sh +grep static-libgcc Makefile.am >/dev/null && exit 0 +patch -p0 -l -f $* < $0 +exit $? + +Use -static-libgcc to avoid linking to libgcc_s_sjlj-1.dll. + +Since gcc 4.8 there is a regression in that plain C programs may link +to libgcc_s.a which has a dependency on libgcc_s_sjlj.dll. This is +for example triggered by using long long arithmetic on a 32 bit +Windows (e.g symbol __udivdi3). + +As usual the gcc maintainers don't care about backward compatibility +and declare that as some kind of compatibility fix and not as +regression from 4.7 and all earlier versions. + +Note that we ignore this patch if it seems to be already applied +upstream. + +--- Makefile.am~ 2016-04-18 20:56:32.000000000 +0200 ++++ Makefile.am 2016-05-04 12:08:53.254035717 +0200 +@@ -3,7 +3,7 @@ + + lib_LTLIBRARIES = libsqlite3.la + libsqlite3_la_SOURCES = sqlite3.c +-libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 ++libsqlite3_la_LDFLAGS = -XCClinker -static-libgcc -no-undefined -version-info 8:6:8 + + bin_PROGRAMS = sqlite3 + sqlite3_SOURCES = shell.c sqlite3.h + +--- Makefile.in~ 2016-04-18 20:56:36.000000000 +0200 ++++ Makefile.in 2016-05-04 12:13:36.570020590 +0200 +@@ -365,7 +365,7 @@ + AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE + lib_LTLIBRARIES = libsqlite3.la + libsqlite3_la_SOURCES = sqlite3.c +-libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 ++libsqlite3_la_LDFLAGS = -XCClinker -static-libgcc -no-undefined -version-info 8:6:8 + sqlite3_SOURCES = shell.c sqlite3.h + EXTRA_sqlite3_SOURCES = sqlite3.c + sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@ diff --git a/build-aux/speedo/w32/README.txt b/build-aux/speedo/w32/README.txt new file mode 100644 index 0000000..0d72fe6 --- /dev/null +++ b/build-aux/speedo/w32/README.txt @@ -0,0 +1,75 @@ +;; README.txt -*- coding: latin-1; -*- +;; This is the README installed for Windows. Lines with a +;; semicolon in the first column are considered a comment and not +;; included in the actually installed version. Certain keywords are +;; replaced by the Makefile; those words are enclosed by exclamation +;; marks. + + GNUPG for Windows + =================== + +This is GnuPG for Windows, version !VERSION!. + +Content: + + 1. Important notes + 2. Changes + 3. GnuPG README file + 4. Legal notices + + +1. Important Notes +================== + +This is the core part of the GnuPG system as used by several other +frontend programs. This installer does not provide any graphical +frontend and thus almost everything needs to be done on the command +line. However, a small native Windows GUI tool is included which is +used by GnuPG to ask for passphrases. It provides only the basic +functionality and is installed under the name "pinentry-basic.exe". +Other software using this core component may install a different +version of such a tool under the name "pinentry.exe" or configure the +gpg-agent to use that version. + +See https://gnupg.org for latest news. HowTo documents and manuals +can be found there but some have also been installed on your machine. + +Development and maintenance of GnuPG is mostly financed by donations; +please see https://gnupg.org/donate/ for details. + + +2. Record of Changes +==================== + +This is a list of changes to the GnuPG core for this and the previous +release. + +!NEWSFILE! + + +3. GnuPG README file +==================== + +Below is the README file as distributed with the GnuPG source. + +!GNUPGREADME! + + +4. Legal notices pertaining to the individual packets +===================================================== + +GnuPG for Windows consist of several independent developed packages, +available under different license conditions. Most of these packages +are however available under the GNU General Public License (GNU GPL). +Common to all is that they are free to use without restrictions, may +be modified and that modifications may be distributed. If the source +file (i.e. gnupg-w32-VERSION_DATE.tar.xz) is distributed along with +the installer and the use of the GNU GPL has been pointed out, +distribution is in all cases possible. + +What follows is a list of copyright statements. + +!PKG-COPYRIGHT! + + +***end of file *** diff --git a/build-aux/speedo/w32/exdll.h b/build-aux/speedo/w32/exdll.h new file mode 100644 index 0000000..bb13ae0 --- /dev/null +++ b/build-aux/speedo/w32/exdll.h @@ -0,0 +1,151 @@ +/* exdll.h for use with gpg4win + * Copyright (C) 1999-2005 Nullsoft, Inc. + * + * This license applies to everything in the NSIS package, except + * where otherwise noted. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ************************************************************ + * 2005-11-14 wk Applied license text to original exdll.h file from + * NSIS 2.0.4 and did some formatting changes. + */ + +#ifndef _EXDLL_H_ +#define _EXDLL_H_ + +/* only include this file from one place in your DLL. (it is all + static, if you use it in two places it will fail) */ + +#define EXDLL_INIT() { \ + g_stringsize=string_size; \ + g_stacktop=stacktop; \ + g_variables=variables; } + +/* For page showing plug-ins */ +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) +#define NOTIFY_BYE_BYE 'x' + +typedef struct _stack_t { + struct _stack_t *next; + char text[1]; /* This should be the length of string_size. */ +} stack_t; + + +static unsigned int g_stringsize; +static stack_t **g_stacktop; +static char *g_variables; + +static int __stdcall popstring(char *str, size_t maxlen); /* 0 on success, 1 on empty stack */ +static void __stdcall pushstring(const char *str); + +enum + { + INST_0, // $0 + INST_1, // $1 + INST_2, // $2 + INST_3, // $3 + INST_4, // $4 + INST_5, // $5 + INST_6, // $6 + INST_7, // $7 + INST_8, // $8 + INST_9, // $9 + INST_R0, // $R0 + INST_R1, // $R1 + INST_R2, // $R2 + INST_R3, // $R3 + INST_R4, // $R4 + INST_R5, // $R5 + INST_R6, // $R6 + INST_R7, // $R7 + INST_R8, // $R8 + INST_R9, // $R9 + INST_CMDLINE, // $CMDLINE + INST_INSTDIR, // $INSTDIR + INST_OUTDIR, // $OUTDIR + INST_EXEDIR, // $EXEDIR + INST_LANG, // $LANGUAGE + __INST_LAST +}; + +typedef struct { + int autoclose; + int all_user_var; + int exec_error; + int abort; + int exec_reboot; + int reboot_called; + int XXX_cur_insttype; /* deprecated */ + int XXX_insttype_changed; /* deprecated */ + int silent; + int instdir_error; + int rtl; + int errlvl; +} exec_flags_t; + +typedef struct { + exec_flags_t *exec_flags; + int (__stdcall *ExecuteCodeSegment)(int, HWND); +} extra_parameters_t; + + +/* Utility functions (not required but often useful). */ +static int __stdcall +popstring(char *str, size_t maxlen) +{ + stack_t *th; + if (!g_stacktop || !*g_stacktop) + return 1; + th=(*g_stacktop); + lstrcpyn (str, th->text, maxlen); + *g_stacktop = th->next; + GlobalFree((HGLOBAL)th); + return 0; +} + +static void __stdcall +pushstring(const char *str) +{ + stack_t *th; + if (!g_stacktop) return; + th=(stack_t*)GlobalAlloc(GPTR,sizeof(stack_t)+g_stringsize); + lstrcpyn(th->text,str,g_stringsize); + th->next=*g_stacktop; + *g_stacktop=th; +} + +static char * __stdcall +getuservariable(const int varnum) +{ + if (varnum < 0 || varnum >= __INST_LAST) return NULL; + return g_variables+varnum*g_stringsize; +} + +static void __stdcall +setuservariable(const int varnum, const char *var) +{ + if (var != NULL && varnum >= 0 && varnum < __INST_LAST) + lstrcpy(g_variables + varnum*g_stringsize, var); +} + + + +#endif/*_EXDLL_H_*/ diff --git a/build-aux/speedo/w32/g4wihelp.c b/build-aux/speedo/w32/g4wihelp.c new file mode 100644 index 0000000..012e4af --- /dev/null +++ b/build-aux/speedo/w32/g4wihelp.c @@ -0,0 +1,1136 @@ +/* g4wihelp.c - NSIS Helper DLL used with gpg4win. -*- coding: latin-1; -*- + * Copyright (C) 2005 g10 Code GmbH + * Copyright (C) 2001 Justin Frankel + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ************************************************************ + * The code for the splash screen has been taken from the Splash + * plugin of the NSIS 2.04 distribution. That code comes without + * explicit copyright notices in the source files or author names, it + * seems that it has been written by Justin Frankel; not sure about + * the year, though. [wk 2005-11-28] + * + * Fixed some compiler warnings. [wk 2014-02-24]. + */ + +#include +#include +#include "exdll.h" + +static HINSTANCE g_hInstance; /* Our Instance. */ +static HWND g_hwndParent; /* Handle of parent window or NULL. */ +static HBITMAP g_hbm; /* Handle of the splash image. */ +static int sleepint; /* Milliseconds to show the spals image. */ + + +/* Standard entry point for DLLs. */ +int WINAPI +DllMain (HANDLE hinst, DWORD reason, LPVOID reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + g_hInstance = hinst; + return TRUE; +} + + + +/* Dummy function for testing. */ +void __declspec(dllexport) +dummy (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + g_hwndParent = hwndParent; + + EXDLL_INIT(); + + // note if you want parameters from the stack, pop them off in order. + // i.e. if you are called via exdll::myFunction file.dat poop.dat + // calling popstring() the first time would give you file.dat, + // and the second time would give you poop.dat. + // you should empty the stack of your parameters, and ONLY your + // parameters. + + // do your stuff here + { + char buf[1024]; + snprintf (buf, sizeof buf, "$R0=%s\r\n$R1=%s\r\n", + getuservariable(INST_R0), + getuservariable(INST_R1)); + MessageBox (g_hwndParent,buf,0,MB_OK); + + snprintf (buf, sizeof buf, + "autoclose =%d\r\n" + "all_user_var =%d\r\n" + "exec_error =%d\r\n" + "abort =%d\r\n" + "exec_reboot =%d\r\n" + "reboot_called=%d\r\n" + "silent =%d\r\n" + "instdir_error=%d\r\n" + "rtl =%d\r\n" + "errlvl =%d\r\n", + extra->exec_flags->autoclose, + extra->exec_flags->all_user_var, + extra->exec_flags->exec_error, + extra->exec_flags->abort, + extra->exec_flags->exec_reboot, + extra->exec_flags->reboot_called, + extra->exec_flags->silent, + extra->exec_flags->instdir_error, + extra->exec_flags->rtl, + extra->exec_flags->errlvl); + MessageBox(g_hwndParent,buf,0,MB_OK); + } +} + + +void __declspec(dllexport) +runonce (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + const char *result; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + CreateMutexA (NULL, 0, getuservariable(INST_R0)); + result = GetLastError ()? "1":"0"; + setuservariable (INST_R0, result); +} + + +void __declspec(dllexport) +playsound (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + char fname[MAX_PATH]; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + if (popstring(fname, sizeof fname)) + return; + PlaySound (fname, NULL, SND_ASYNC|SND_FILENAME|SND_NODEFAULT); +} + + +void __declspec(dllexport) +stopsound (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + g_hwndParent = hwndParent; + EXDLL_INIT(); + PlaySound (NULL, NULL, 0); +} + + +/* Windows procedure to control the splashimage. This one pauses the + execution until the sleep time is over or the user closes this + windows. */ +static LRESULT CALLBACK +splash_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result = 0; + + switch (uMsg) + { + case WM_CREATE: + { + BITMAP bm; + RECT vp; + + GetObject(g_hbm, sizeof(bm), (LPSTR)&bm); + SystemParametersInfo(SPI_GETWORKAREA, 0, &vp, 0); + SetWindowLong(hwnd,GWL_STYLE,0); + SetWindowPos(hwnd,NULL, + vp.left+(vp.right-vp.left-bm.bmWidth)/2, + vp.top+(vp.bottom-vp.top-bm.bmHeight)/2, + bm.bmWidth,bm.bmHeight, + SWP_NOZORDER); + ShowWindow(hwnd,SW_SHOW); + SetTimer(hwnd,1,sleepint,NULL); + } + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + RECT r; + HDC curdc=BeginPaint(hwnd,&ps); + HDC hdc=CreateCompatibleDC(curdc); + HBITMAP oldbm; + GetClientRect(hwnd,&r); + oldbm=(HBITMAP)SelectObject(hdc,g_hbm); + BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top, + hdc,0,0,SRCCOPY); + SelectObject(hdc,oldbm); + DeleteDC(hdc); + EndPaint(hwnd,&ps); + } + break; + + case WM_CLOSE: + break; + + case WM_TIMER: + case WM_LBUTTONDOWN: + DestroyWindow(hwnd); + /*(fall through)*/ + default: + result = DefWindowProc (hwnd, uMsg, wParam, lParam); + } + + return result; +} + + +/* Display a splash screen. Call as + + g4wihelp::showsplash SLEEP FNAME + + With SLEEP being the time in milliseconds to show the splashscreen + and FNAME the complete filename of the image. As of now only BMP + is supported. +*/ +void __declspec(dllexport) +showsplash (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + static WNDCLASS wc; + char sleepstr[30]; + char fname[MAX_PATH]; + int err = 0; + char *p; + char classname[] = "_sp"; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + if (popstring(sleepstr, sizeof sleepstr)) + err = 1; + if (popstring(fname, sizeof fname)) + err = 1; + if (err) + return; + + if (!*fname) + return; /* Nothing to do. */ + + for (sleepint=0, p=sleepstr; *p >= '0' && *p <= '9'; p++) + { + sleepint *= 10; + sleepint += *p - '0'; + } + if (sleepint <= 0) + return; /* Nothing to do. */ + + wc.lpfnWndProc = splash_wndproc; + wc.hInstance = g_hInstance; + wc.hCursor = LoadCursor(NULL,IDC_ARROW); + wc.lpszClassName = classname; + if (!RegisterClass(&wc)) + return; /* Error. */ + + g_hbm = LoadImage (NULL, fname, IMAGE_BITMAP, + 0, 0 , LR_CREATEDIBSECTION|LR_LOADFROMFILE); + if (g_hbm) + { + MSG msg; + HWND hwnd; + + hwnd = CreateWindowEx (WS_EX_TOOLWINDOW, classname, classname, + 0, 0, 0, 0, 0, (HWND)hwndParent, NULL, + g_hInstance, NULL); + + while (IsWindow(hwnd) && GetMessage ( &msg, hwnd, 0, 0)) + { + DispatchMessage (&msg); + } + + DeleteObject (g_hbm); + g_hbm = NULL; + } + UnregisterClass (classname, g_hInstance); +} + + +/* Service Management. */ + +/* Use this to report unexpected errors. FIXME: This is really not + very descriptive. */ +void +service_error (const char *str) +{ + char buf[1024]; + snprintf (buf, sizeof (buf), "error: %s: ec=%d\r\n", str, + GetLastError ()); + MessageBox(g_hwndParent, buf, 0, MB_OK); + + setuservariable (INST_R0, "1"); +} + + +void __declspec(dllexport) +service_create (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + SC_HANDLE sc; + SC_HANDLE service; + const char *result = NULL; + char service_name[256]; + char display_name[256]; + char program[256]; + int err = 0; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: service_name, display_name, program. */ + if (popstring (service_name, sizeof (service_name))) + err = 1; + if (!err && popstring (display_name, sizeof (display_name))) + err = 1; + if (!err && popstring (program, sizeof (program))) + err = 1; + if (err) + { + setuservariable (INST_R0, "1"); + return; + } + + sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (sc == NULL) + { + service_error ("OpenSCManager"); + return; + } + + service = CreateService (sc, service_name, display_name, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + /* Use SERVICE_DEMAND_START for testing. + FIXME: Currently not configurable by caller. */ + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, program, + NULL, NULL, NULL, + /* FIXME: Currently not configurable by caller. */ + /* FIXME: LocalService or NetworkService + don't work for dirmngr right now. NOTE! + If you change it here, you also should + adjust make-msi.pl for the msi + installer. In the future, this should + be an argument to the function and then + the make-msi.pl script can extract it + from the invocation. */ + NULL /* "NT AUTHORITY\\LocalService" */, + NULL); + if (service == NULL) + { + service_error ("CreateService"); + CloseServiceHandle (sc); + return; + } + CloseServiceHandle (service); + + result = GetLastError () ? "1":"0"; + setuservariable (INST_R0, result); + return; +} + + +/* Requires g_hwndParent to be set! */ +SC_HANDLE +service_lookup (char *service_name) +{ + SC_HANDLE sc; + SC_HANDLE service; + + sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (sc == NULL) + { + service_error ("OpenSCManager"); + return NULL; + } + service = OpenService (sc, service_name, SC_MANAGER_ALL_ACCESS); + if (service == NULL) + { + /* Fail silently here. */ + CloseServiceHandle (sc); + return NULL; + } + CloseServiceHandle (sc); + return service; +} + + +/* Returns status. */ +void __declspec(dllexport) +service_query (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + SC_HANDLE service; + const char *result = NULL; + char service_name[256]; + int err = 0; + SERVICE_STATUS status; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: service_name argc [argv]. */ + if (popstring (service_name, sizeof (service_name))) + err = 1; + if (err) + { + setuservariable (INST_R0, "ERROR"); + return; + } + + service = service_lookup (service_name); + if (service == NULL) + if (err == 0) + { + setuservariable (INST_R0, "MISSING"); + return; + } + + err = QueryServiceStatus (service, &status); + if (err == 0) + { + setuservariable (INST_R0, "ERROR"); + CloseServiceHandle (service); + return; + } + CloseServiceHandle (service); + + switch (status.dwCurrentState) + { + case SERVICE_START_PENDING: + result = "START_PENDING"; + break; + case SERVICE_RUNNING: + result = "RUNNING"; + break; + case SERVICE_PAUSE_PENDING: + result = "PAUSE_PENDING"; + break; + case SERVICE_PAUSED: + result = "PAUSED"; + break; + case SERVICE_CONTINUE_PENDING: + result = "CONTINUE_PENDING"; + break; + case SERVICE_STOP_PENDING: + result = "STOP_PENDING"; + break; + case SERVICE_STOPPED: + result = "STOPPED"; + break; + default: + result = "UNKNOWN"; + } + setuservariable (INST_R0, result); + return; +} + + +void __declspec(dllexport) +service_start (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + SC_HANDLE service; + const char *result = NULL; + char service_name[256]; + char argc_str[256]; +#define NR_ARGS 10 +#define ARG_MAX 256 + char argv_str[NR_ARGS][ARG_MAX]; + const char *argv[NR_ARGS + 1]; + int argc; + int i; + int err = 0; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: service_name argc [argv]. */ + if (popstring (service_name, sizeof (service_name))) + err = 1; + if (!err && popstring (argc_str, sizeof (argc_str))) + err = 1; + if (!err) + { + argc = atoi (argc_str); + for (i = 0; i < argc; i++) + { + if (popstring (argv_str[i], ARG_MAX)) + { + err = 1; + break; + } + argv[i] = argv_str[i]; + } + argv[i] = NULL; + } + if (err) + { + setuservariable (INST_R0, "1"); + return; + } + + service = service_lookup (service_name); + if (service == NULL) + return; + + err = StartService (service, argc, argc == 0 ? NULL : argv); + if (err == 0) + { + service_error ("StartService"); + CloseServiceHandle (service); + return; + } + CloseServiceHandle (service); + + setuservariable (INST_R0, "0"); + return; +} + + +void __declspec(dllexport) +service_stop (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + SC_HANDLE service; + const char *result = NULL; + char service_name[256]; + int err = 0; + SERVICE_STATUS status; + DWORD timeout = 10000; /* 10 seconds. */ + DWORD start_time; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: service_name argc [argv]. */ + if (popstring (service_name, sizeof (service_name))) + err = 1; + if (err) + { + setuservariable (INST_R0, "1"); + return; + } + + service = service_lookup (service_name); + if (service == NULL) + return; + + err = QueryServiceStatus (service, &status); + if (err == 0) + { + service_error ("QueryService"); + CloseServiceHandle (service); + return; + } + + if (status.dwCurrentState != SERVICE_STOPPED + && status.dwCurrentState != SERVICE_STOP_PENDING) + { + err = ControlService (service, SERVICE_CONTROL_STOP, &status); + if (err == 0) + { + service_error ("ControlService"); + CloseServiceHandle (service); + return; + } + } + + start_time = GetTickCount (); + while (status.dwCurrentState != SERVICE_STOPPED) + { + Sleep (1000); /* One second. */ + if (!QueryServiceStatus (service, &status)) + { + service_error ("QueryService"); + CloseServiceHandle (service); + return; + } + if (status.dwCurrentState == SERVICE_STOPPED) + break; + + if (GetTickCount () - start_time > timeout) + { + char buf[1024]; + snprintf (buf, sizeof (buf), + "time out waiting for service %s to stop\r\n", + service_name); + MessageBox (g_hwndParent, buf, 0, MB_OK); + setuservariable (INST_R0, "1"); + return; + } + } + CloseServiceHandle (service); + setuservariable (INST_R0, "0"); + return; +} + + +void __declspec(dllexport) +service_delete (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + SC_HANDLE service; + const char *result = NULL; + char service_name[256]; + int err = 0; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: service_name argc [argv]. */ + if (popstring (service_name, sizeof (service_name))) + err = 1; + if (err) + { + setuservariable (INST_R0, "1"); + return; + } + + service = service_lookup (service_name); + if (service == NULL) + return; + + err = DeleteService (service); + if (err == 0) + { + service_error ("DeleteService"); + CloseServiceHandle (service); + return; + } + CloseServiceHandle (service); + + setuservariable (INST_R0, "0"); + return; +} + + +#include + +/* Extract config file parameters. FIXME: Not particularly robust. + We expect some reasonable formatting. The parser below is very + limited. It expects a command line option /c=FILE or /C=FILE, + where FILE must be enclosed in double-quotes if it contains spaces. + That file should contain a single section [gpg4win] and KEY=VALUE + pairs for each additional configuration file to install. Comments + are supported only on lines by themselves. VALUE can be quoted in + double-quotes, but does not need to be, unless it has whitespace at + the beginning or end. KEY can, for example, be "gpg.conf" (without + the quotes). */ +void +config_init (char **keys, char **values, int max) +{ + /* First, parse the command line. */ + char *cmdline; + char *begin = NULL; + char *end = NULL; + char mark; + char *fname; + char *ptr; + FILE *conf; + + *keys = NULL; + *values = NULL; + + cmdline = getuservariable (INST_CMDLINE); + + mark = (*cmdline == '"') ? (cmdline++, '"') : ' '; + while (*cmdline && *cmdline != mark) + cmdline++; + if (mark == '"' && *cmdline) + cmdline++; + while (*cmdline && *cmdline == ' ') + cmdline++; + + while (*cmdline) + { + /* We are at the beginning of a new argument. */ + if (cmdline[0] == '/' && (cmdline[1] == 'C' || cmdline[1] == 'c') + && cmdline[2] == '=') + { + cmdline += 3; + begin = cmdline; + } + + while (*cmdline && *cmdline != ' ') + { + /* Skip over quoted parts. */ + if (*cmdline == '"') + { + cmdline++; + while (*cmdline && *cmdline != '"') + cmdline++; + if (*cmdline) + cmdline++; + } + else + cmdline++; + } + if (begin && !end) + { + end = cmdline - 1; + break; + } + while (*cmdline && *cmdline == ' ') + cmdline++; + } + + if (!begin || begin > end) + return; + + /* Strip quotes. */ + if (*begin == '"' && *end == '"') + { + begin++; + end--; + } + if (begin > end) + return; + + fname = malloc (end - begin + 2); + if (!fname) + return; + + ptr = fname; + while (begin <= end) + *(ptr++) = *(begin++); + *ptr = '\0'; + + conf = fopen (fname, "r"); + free (fname); + if (!conf) + return; + + while (max - 1 > 0) + { + char line[256]; + char *ptr2; + + if (fgets (line, sizeof (line), conf) == NULL) + break; + ptr = &line[strlen (line)]; + while (ptr > line && (ptr[-1] == '\n' || ptr[-1] == '\r' + || ptr[-1] == ' ' || ptr[-1] == '\t')) + ptr--; + *ptr = '\0'; + + ptr = line; + while (*ptr && (*ptr == ' ' || *ptr == '\t')) + ptr++; + /* Ignore comment lines. */ + /* FIXME: Ignore section markers. */ + if (*ptr == '\0' || *ptr == ';' || *ptr == '[') + continue; + begin = ptr; + while (*ptr && *ptr != '=' && *ptr != ' ' && *ptr != '\t') + ptr++; + end = ptr - 1; + while (*ptr && (*ptr == ' ' || *ptr == '\t')) + ptr++; + if (*ptr != '=') + continue; + ptr++; + + if (begin > end) + continue; + + /* We found a key. */ + *keys = malloc (end - begin + 2); + if (!keys) + return; + ptr2 = *keys; + while (begin <= end) + *(ptr2++) = *(begin++); + *ptr2 = '\0'; + + *values = NULL; + + while (*ptr && (*ptr == ' ' || *ptr == '\t')) + ptr++; + begin = ptr; + /* In this case, end points to the byte after the value, which + is OK because that is '\0'. */ + end = &line[strlen (line)]; + if (begin > end) + begin = end; + + /* Strip quotes. */ + if (*begin == '"' && end[-1] == '"') + { + begin++; + end--; + *end = '\0'; + } + if (begin > end) + return; + + *values = malloc (end - begin + 1); + ptr2 = *values; + while (begin <= end) + *(ptr2++) = *(begin++); + + keys++; + values++; + max--; + } + + fclose (conf); + *keys = NULL; + *values = NULL; +} + + +char * +config_lookup (char *key) +{ +#define MAX_KEYS 128 + static int initialised = 0; + static char *keys[MAX_KEYS]; + static char *values[MAX_KEYS]; + int i; + + if (initialised == 0) + { + initialised = 1; + config_init (keys, values, MAX_KEYS); + +#if 0 + MessageBox(g_hwndParent, "Configuration File:", 0, MB_OK); + i = 0; + while (keys[i]) + { + char buf[256]; + sprintf (buf, "%s=%s\r\n", keys[i], values[i]); + MessageBox (g_hwndParent, buf, 0, MB_OK); + i++; + } +#endif + } + + i = 0; + while (keys[i]) + { + if (!strcmp (keys[i], key)) + return values[i]; + i++; + } + + return NULL; +} + + +void __declspec(dllexport) +config_fetch (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + char key[256]; + int err = 0; + char *value; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: key. */ + if (popstring (key, sizeof (key))) + err = 1; + if (err) + { + setuservariable (INST_R0, ""); + return; + } + + value = config_lookup (key); + + setuservariable (INST_R0, value == NULL ? "" : value); + return; +} + + +void __declspec(dllexport) +config_fetch_bool (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + char key[256]; + int err = 0; + char *value; + int result; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + /* The expected stack layout: key. */ + if (popstring (key, sizeof (key))) + err = 1; + if (err) + { + setuservariable (INST_R0, ""); + return; + } + + value = config_lookup (key); + if (value == NULL || *value == '\0') + { + setuservariable (INST_R0, ""); + return; + } + + result = 0; + if (!strcasecmp (value, "true") + || !strcasecmp (value, "yes") + || atoi (value) != 0) + result = 1; + + setuservariable (INST_R0, result == 0 ? "0" : "1"); + return; +} + + +/* Return a string from the Win32 Registry or NULL in case of error. + Caller must release the return value. A NULL for root is an alias + for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ +char * +read_w32_registry_string (HKEY root, const char *dir, const char *name) +{ + HKEY root_key; + HKEY key_handle; + DWORD n1, nbytes, type; + char *result = NULL; + + root_key = root; + if (! root_key) + root_key = HKEY_CURRENT_USER; + + if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) ) + { + if (root) + return NULL; /* no need for a RegClose, so return direct */ + /* It seems to be common practise to fall back to HKLM. */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) ) + return NULL; /* still no need for a RegClose, so return direct */ + } + + nbytes = 1; + if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) { + if (root) + goto leave; + /* Try to fallback to HKLM also vor a missing value. */ + RegCloseKey (key_handle); + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) ) + return NULL; /* Nope. */ + if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes)) + goto leave; + } + + result = malloc( (n1=nbytes+1) ); + + if( !result ) + goto leave; + if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) { + free(result); result = NULL; + goto leave; + } + result[nbytes] = 0; /* make sure it is really a string */ + + leave: + RegCloseKey( key_handle ); + return result; +} + + +#define ENV_HK HKEY_LOCAL_MACHINE +#define ENV_REG "SYSTEM\\CurrentControlSet\\Control\\" \ + "Session Manager\\Environment" + /* The following setting can be used for a per-user setting. */ +#if 0 +#define ENV_HK HKEY_CURRENT_USER +#define ENV_REG "Environment" +#endif +/* Due to a bug in Windows7 (kb 2685893) we better put a lower limit + than 8191 on the maximum length of the PATH variable. Note, that + depending on the used toolchain we used to had a 259 byte limit in + the past. */ +#define PATH_LENGTH_LIMIT 2047 + +void __declspec(dllexport) +path_add (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + char dir[PATH_LENGTH_LIMIT]; + char *path; + char *path_new; + int path_new_size; + char *comp; + const char delims[] = ";"; + HKEY key_handle = 0; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + setuservariable (INST_R0, "0"); + +/* MessageBox (g_hwndParent, "XXX 1", 0, MB_OK); */ + + /* The expected stack layout: path component. */ + if (popstring (dir, sizeof (dir))) + return; + +/* MessageBox (g_hwndParent, "XXX 2", 0, MB_OK); */ + + path = read_w32_registry_string (ENV_HK, ENV_REG, "Path"); + if (! path) + { + MessageBox (g_hwndParent, "No PATH variable found", 0, MB_OK); + return; + } + +/* MessageBox (g_hwndParent, "XXX 3", 0, MB_OK); */ + + /* Old path plus semicolon plus dir plus terminating nul. */ + path_new_size = strlen (path) + 1 + strlen (dir) + 1; + if (path_new_size > PATH_LENGTH_LIMIT) + { + MessageBox (g_hwndParent, "PATH env variable too big", 0, MB_OK); + free (path); + return; + } + +/* MessageBox (g_hwndParent, "XXX 4", 0, MB_OK); */ + + path_new = malloc (path_new_size); + if (!path_new) + { + free (path); + return; + } + +/* MessageBox (g_hwndParent, "XXX 5", 0, MB_OK); */ + + strcpy (path_new, path); + strcat (path_new, ";"); + strcat (path_new, dir); + +/* MessageBox (g_hwndParent, "XXX 6", 0, MB_OK); */ +/* MessageBox (g_hwndParent, dir, 0, MB_OK); */ +/* MessageBox (g_hwndParent, "XXX 7", 0, MB_OK); */ + + /* Check if the directory already exists in the path. */ + comp = strtok (path, delims); + do + { +/* MessageBox (g_hwndParent, comp, 0, MB_OK); */ + + if (!strcmp (comp, dir)) + { + free (path); + free (path_new); + return; + } + comp = strtok (NULL, delims); + } + while (comp); + free (path); + +/* MessageBox (g_hwndParent, "XXX 8", 0, MB_OK); */ + + /* Set a key for our CLSID. */ + RegCreateKey (ENV_HK, ENV_REG, &key_handle); + RegSetValueEx (key_handle, "Path", 0, REG_EXPAND_SZ, + path_new, path_new_size); + RegCloseKey (key_handle); + SetEnvironmentVariable("PATH", path_new); + free (path_new); + +/* MessageBox (g_hwndParent, "XXX 9", 0, MB_OK); */ + + setuservariable (INST_R0, "1"); +} + + +void __declspec(dllexport) +path_remove (HWND hwndParent, int string_size, char *variables, + stack_t **stacktop, extra_parameters_t *extra) +{ + char dir[PATH_LENGTH_LIMIT]; + char *path; + char *path_new; + int path_new_size; + char *comp; + const char delims[] = ";"; + HKEY key_handle = 0; + int changed = 0; + int count = 0; + + g_hwndParent = hwndParent; + EXDLL_INIT(); + + setuservariable (INST_R0, "0"); + + /* The expected stack layout: path component. */ + if (popstring (dir, sizeof (dir))) + return; + + path = read_w32_registry_string (ENV_HK, ENV_REG, "Path"); + /* Old path plus semicolon plus dir plus terminating nul. */ + path_new_size = strlen (path) + 1; + path_new = malloc (path_new_size); + if (!path_new) + { + free (path); + return; + } + path_new[0] = '\0'; + + /* Compose the new path. */ + comp = strtok (path, delims); + do + { + if (strcmp (comp, dir)) + { + if (count != 0) + strcat (path_new, ";"); + strcat (path_new, comp); + count++; + } + else + changed = 1; + + comp = strtok (NULL, delims); + } + while (comp); + free (path); + + if (! changed) + return; + + /* Set a key for our CLSID. */ + RegCreateKey (ENV_HK, ENV_REG, &key_handle); + RegSetValueEx (key_handle, "Path", 0, REG_EXPAND_SZ, + path_new, path_new_size); + RegCloseKey (key_handle); + free (path_new); + + setuservariable (INST_R0, "1"); +} diff --git a/build-aux/speedo/w32/gdk-pixbuf-loaders.cache b/build-aux/speedo/w32/gdk-pixbuf-loaders.cache new file mode 100755 index 0000000..78bc18a --- /dev/null +++ b/build-aux/speedo/w32/gdk-pixbuf-loaders.cache @@ -0,0 +1,138 @@ +# GdkPixbuf Image Loader Modules file +# Automatically generated file, do not edit +# Created by gdk-pixbuf-query-loaders.exe from gdk-pixbuf-2.26.5 +# +# LoaderDir = ../lib/gdk-pixbuf-2.0/2.10.0/loaders +# +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-ani.dll" +"ani" 4 "gdk-pixbuf" "The ANI image format" "LGPL" +"application/x-navi-animation" "" +"ani" "" +"RIFF ACON" " xxxx " 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-bmp.dll" +"bmp" 5 "gdk-pixbuf" "The BMP image format" "LGPL" +"image/bmp" "image/x-bmp" "image/x-MS-bmp" "" +"bmp" "" +"BM" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-emf.dll" +"emf" 4 "gdk-pixbuf" "The EMF image format" "LGPL" +"application/emf" "application/x-emf" "image/x-emf" "image/x-mgx-emf" "" +"emf" "" +"\001" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-gif.dll" +"gif" 5 "gdk-pixbuf" "The GIF image format" "LGPL" +"image/gif" "" +"gif" "" +"GIF8" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-ico.dll" +"ico" 4 "gdk-pixbuf" "The ICO image format" "LGPL" +"image/x-icon" "image/x-ico" "" +"ico" "cur" "" +" \001 " "zz znz" 100 +" \002 " "zz znz" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-jpeg.dll" +"jpeg" 5 "gdk-pixbuf" "The JPEG image format" "LGPL" +"image/jpeg" "" +"jpeg" "jpe" "jpg" "" +"\377\330" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-tiff.dll" +"tiff" 5 "gdk-pixbuf" "The TIFF image format" "LGPL" +"image/tiff" "" +"tiff" "tif" "" +"MM *" " z " 100 +"II* " " z" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-wmf.dll" +"wmf" 4 "gdk-pixbuf" "The WMF image format" "LGPL" +"image/x-wmf" "" +"wmf" "apm" "" +"\327\315\306\232" "" 100 +"\001" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-icns.dll" +"icns" 4 "gdk-pixbuf" "The ICNS image format" "GPL" +"image/x-icns" "" +"icns" "" +"icns" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-pcx.dll" +"pcx" 4 "gdk-pixbuf" "The PCX image format" "LGPL" +"image/x-pcx" "" +"pcx" "" +"\n \001" "" 100 +"\n\002\001" "" 100 +"\n\003\001" "" 100 +"\n\004\001" "" 100 +"\n\005\001" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-png.dll" +"png" 5 "gdk-pixbuf" "The PNG image format" "LGPL" +"image/png" "" +"png" "" +"\211PNG\r\n\032\n" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-pnm.dll" +"pnm" 4 "gdk-pixbuf" "The PNM/PBM/PGM/PPM image format family" "LGPL" +"image/x-portable-anymap" "image/x-portable-bitmap" "image/x-portable-graymap" "image/x-portable-pixmap" "" +"pnm" "pbm" "pgm" "ppm" "" +"P1" "" 100 +"P2" "" 100 +"P3" "" 100 +"P4" "" 100 +"P5" "" 100 +"P6" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-qtif.dll" +"qtif" 4 "gdk-pixbuf" "The QTIF image format" "LGPL" +"image/x-quicktime" "image/qtif" "" +"qtif" "qif" "" +"abcdidsc" "xxxx " 100 +"abcdidat" "xxxx " 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-ras.dll" +"ras" 4 "gdk-pixbuf" "The Sun raster image format" "LGPL" +"image/x-cmu-raster" "image/x-sun-raster" "" +"ras" "" +"Y\246j\225" "" 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-tga.dll" +"tga" 4 "gdk-pixbuf" "The Targa image format" "LGPL" +"image/x-tga" "" +"tga" "targa" "" +" \001\001" "x " 100 +" \001\t" "x " 100 +" \002" "xz " 99 +" \003" "xz " 100 +" \n" "xz " 100 +" \v" "xz " 100 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-wbmp.dll" +"wbmp" 4 "gdk-pixbuf" "The WBMP image format" "LGPL" +"image/vnd.wap.wbmp" "" +"wbmp" "" +" " "zz" 1 +" `" "z " 1 +" @" "z " 1 +" " "z " 1 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-xbm.dll" +"xbm" 4 "gdk-pixbuf" "The XBM image format" "LGPL" +"image/x-xbitmap" "" +"xbm" "" +"#define " "" 100 +"/*" "" 50 + +"../lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-xpm.dll" +"xpm" 4 "gdk-pixbuf" "The XPM image format" "LGPL" +"image/x-xpixmap" "" +"xpm" "" +"/* XPM */" "" 100 + + +# eof # diff --git a/build-aux/speedo/w32/gnupg-logo-150x57.bmp b/build-aux/speedo/w32/gnupg-logo-150x57.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f2978f3dc596166a0a298b1fdce71daf7da000cd GIT binary patch literal 3842 zcmbtXdr(}}8ULMo?%sV|HXC4NCE})lQD#d-XLlUXKud^V>a0xz3f&zT1VXw9#DN$f z%mp{IEZm*jX>EPgSFMj~w5@MP>!a3J)z+%@*_sz2Z$cgf0(qH!=Pt%%4EB$Pxy<2w z=X}5K`8!`02PGHGe=QP_N2}ZEE259VMjvoz3h=oz`Td;!7T?BOflapoo9Xa|Th77o z9l+K*Y42{@y9d~EFYx8o~N$F$df&sSsg zg&ORA@u%4FLIZZbRD+!_He%PGS77(c%@}=UCB|N>!`@eahOxgyu890%VY#P~ml zF!sSOuDLIK1O6OznId)4Sfm(cSOj*q-+=x#xWx z9{m7Qdq2d~=x3N7`v}u}KgW@=FK~32h%=bFnn18{Fm7 zg+xx&=gd(g$f~EjUm+dZLYBS;R$9fEA-PWt4IY;Y@}a6l~{kOkHSaCVwlQ3pI4 z6oV~fOeT{#B!2^VvB2t~PU}XR!ImGFWrL!g~!z|j{_O{{skxY?J@;c25t%PVF<66hW2ToN&bWepM1>7Kc0?^@!m094YE zLqGy&jsZ_19@V;An;M(jy0pGQBb*@|B>zaRB*K*4u(&~)IjesHwcZI|%tRdsoQX>M zEqW^We1U372w2lU+PC*NFAZEc&+Sk}yw!94;r_bwP9a-j*+2nO2)PlM6}iTS!nld7 z2TviViA)OQN|j^P3^JcVKF7e^SbJsQd1bn?{F16Zs6CtkG;#c@%+a^2T? z{2ER6aqb~|$xDu;msB&d-n7U=RjLw%$Q9A;5vc9l|le~qG-ad-zs7vIS0c^%}%r(vx=t@lLks^{V?SW6%;gIry|DlWRQ z+YN-pJx4rJTUTF8MQkE_saqm>W;Ynd5T!oHX0QO4V6jV7E~4T^zdKIv_t`PcZYE9& zM8ugE>W@}|$-BG^q?0Iuo2cAi8XKhi+M->NP%so(OXroZV@e&delYXBaROweMw28aBlbhsE_d?%_?k7hEd44RVSUG#8SlQiLpzn$6ry? zcw=9(bIXvKWYsv;MvjQ5En1!%(2P3bc6uiZ*P0a?@#j_!iIv=*n^$!v*7Zck&4v-7 zr>^E3EDIR7KxR-RcWy~dH@!zZ=oJ;l+9)$+PJ=~RCu!mdJt6p4(TCQ>^n~4D38p&? zrZZjCWzr{FDT%9yS2{%WbEzfz$$r+xhRm8Xy-PRWbk$c)^8;%o%}TrkT)moDo`>9F zTzrPMR?1djK;W*S$2?sVub7ti{pdIPXny95Q_EX8@*+btQBYOoim3Qjksk?I^>`=? zN^Mdz9qfcmLDH7ei%h#@fwhLXTv{3xr)_$oyF`UAi6FWp1WAEQ5+ve{=we1n(=MVv z;Pe$P@_VVR;b^$nU_C62jOCnaLF6_>_HxYgFDmk-32V4NCgLYPd2OhiQRGWmSPg-P zug(+Wn}*u_@^}5$u@4vdRZQw zs$fG&8F&Y=9ZBoS?oQ?<*)>d`k;y$CiKH~0##0P-z3Cxm&8tm2VPEG*CNYYmOT77f zpvct2ShtWtY%S84rVEr>v%PO~{bUx*fv?u5tYhpG9tcE3K@ztzT8SRX7?18mtH9NgCTfSjwJH`BG=Rg`i*AyU zw~nxf=JZZ&WpD|naXnYDmBi6QGMY$SHgD zxV$w9H6=jpPGV0I+XYnb1}^Q$K+PTpE|+BC%Fb-m{l$aIKjonQDIco-JPQp^)2=ib zl~2t@ZCM(sp8f{v_Gh4a?>sb=Pe;wZo6z`7Hfl@fqv-(c%D#nWJqtAlzKvRaGwKfB zg8FA~MZ;gei>AMQ56#alK+E%aXno-}G#tu9{R{bMcySpTUs{fqR~MrBPyt#GFG9=9 zE8stJJ6c~^3IE|$Xgpeowj-<2cz7e4UR#CsqifN0Nx9zHu*FPu+vAH-CbbHy%Lin-9W&YBM@c--q_M zeuj>>A42EppQG!YU!e7!N6~%eVffEHhPHRNp#8mHqVxSHa4&~zHX<}(;< zc^5;i?;+6o0fznWqu>7_2HMVIz<&Y3_Ky&3t3asZV}#l-VzBER20Q+Vq0UPf>AH;3 z?kfm(S0mI@gTdZ94E5DxIM9IMK0ihRZ5SD7#8`hjMuSZl8|c7Ts2QPB@7cq%0g}%a43rbjFTEtx^_5a0l@WT1W;GwBqS1 zGgcREanN2c=~*}5sQ{Y{Yo49swKJQ78P>dqc@U0Rvl1U*SI(9mRi^j~cfCo?Ny?i8 zldQ>P%Xr@RnQ3c2PA4Ky`f~u4qnY88_@P9Omg^KKCsk%I&-EE5If5?Z z+G@MQI)`2CJ>X8QmSsa_iEcQxi~1ngEJkTi2gBoh>3f|c=Q$sfy$ggfrE{&mS97Fm zm(s7nC{!^c-W={^!EE%W%42A$QscqN%WSZcnVX3bhYO`p~V(WX-IU~5o(jUQ+ zxuay~ddHD;t+pppV^xtmg7NQ^@+_@>=OdH;?)T8&?PNJIllv$!g%KC#Xq88XrTS4! z6?{#rTc$)fu~hNI@pmsHP)a?P#5-4*AImk9p^8juWEkPCYnzuDGr3k2iRD!g2jJ6UTkIgDu1zj^+Mju_Ol|(O&OI)B!V;FThh5wG*_~p6UWMHUV|)Q zB`~*)RamTwa@ie0RgqL|6c6vGnk3e#tTQ#qW$p1Du>4*iz1Mot<*x zB{K`$!_9GAX=%h>B2Yg~Vz!;usgob(^UQR!*VSC>URxrSEi#*1LgRtx6nL|1GBnL2_8x(xC-nStN+c|B6=g z?N)EEASyTOEOx1yA>B?TC>Kmldxxb4REsh}98?#Fr53Ab<>boAV&xV^SuNflkv``M zZWKaloO63cebx|VKIX?m%@;Kgjap00Bl}V&H!V@H^+eM6E_aL#S`|w7I4sq>i~D2p z>m8OBZq({gDo+WOpqRa^L?=Z#bNRbTtKafoFi+I. + +# Macros to provide for invocation: +# INST_DIR +# INST6_DIR +# BUILD_DIR +# TOP_SRCDIR +# W32_SRCDIR +# BUILD_ISODATE - the build date, e.g. "2014-10-31" +# BUILD_DATESTR - ditto w/o '-', e.g. "20141031" +# NAME +# VERSION +# PROD_VERSION +# +# WITH_GUI - Include the GPA GUI + +!cd "${INST_DIR}" +!addincludedir "${W32_SRCDIR}" +!addplugindir "${BUILD_DIR}" + +# The package name and version. PRETTY_PACKAGE is a user visible name +# only while PACKAGE is useful for filenames etc. PROD_VERSION is the +# product version and needs to be in the format "MAJ.MIN.MIC.BUILDNR". +!define PACKAGE "gnupg" +!define PACKAGE_SHORT "gnupg" +!define PRETTY_PACKAGE "GNU Privacy Guard" +!define PRETTY_PACKAGE_SHORT "GnuPG" +!define COMPANY "The GnuPG Project" +!define COPYRIGHT "Copyright (C) 2015 The GnuPG Project" +!define DESCRIPTION "GnuPG: The GNU Privacy Guard for Windows" + +!define INSTALL_DIR "GnuPG" + +!define WELCOME_TITLE_ENGLISH \ + "Welcome to the installation of GnuPG" + +!define WELCOME_TITLE_GERMAN \ + "Willkommen bei der Installation von GnuPG" + +!define ABOUT_ENGLISH \ + "GnuPG is the mostly used software for mail and data encryption. \ + GnuPG can be used to encrypt data and to create digital signatures. \ + GnuPG includes an advanced key management facility and is compliant \ + with the OpenPGP Internet standard as described in RFC-4880. \ + \r\n\r\n$_CLICK \ + \r\n\r\n\r\n\r\n\r\nThis is GnuPG version ${VERSION}.\r\n\ + File version: ${PROD_VERSION}\r\n\ + Release date: ${BUILD_ISODATE}" +!define ABOUT_GERMAN \ + "GnuPG is die häufigst verwendete Software zur Mail- und Datenverschlüsselung.\ + \r\n\r\n$_CLICK \ + \r\n\r\n\r\n\r\n\r\nDies ist GnuPG Version ${VERSION}.\r\n\ + Dateiversion: ${PROD_VERSION}\r\n\ + Releasedatum: ${BUILD_ISODATE}" + + +# The copyright license of the package. Define only one of these. +!define LICENSE_GPL + +# Select the best compression algorithm available. The dictionary +# size is the default (8 MB). +!ifndef SOURCES +SetCompressor lzma +# SetCompressorDictSize 8 +!endif + +# Include the generic parts. +!define HAVE_STARTMENU + +# We use the modern UI. +!include "MUI.nsh" + +# Some helper some +!include "LogicLib.nsh" +!include "x64.nsh" + +# Set the package name. Note that this name should not be suffixed +# with the version because this would get displayed in the start menu. +# Given that a slash in the name troubles Windows startmenu creation +# we set the Startmenu explicit below. +Name "${PRETTY_PACKAGE}" + +# Set the output filename. +OutFile "${NAME}-${VERSION}_${BUILD_DATESTR}.exe" + +#Fixme: Do we need a logo? +#Icon "${TOP_SRCDIR}/doc/logo/gnupg-logo-icon.ico" +#UninstallIcon "${TOP_SRCDIR}/doc/logo/gnupg-logo-icon.ico" + +# Set the installation directory. +!ifndef INSTALL_DIR +!define INSTALL_DIR "GnuPG" +!endif +InstallDir "$PROGRAMFILES\${INSTALL_DIR}" + +InstallDirRegKey HKLM "Software\${PACKAGE_SHORT}" "Install Directory" + + +# Add version information to the file properties. +VIProductVersion "${PROD_VERSION}" +VIAddVersionKey "ProductName" "${PRETTY_PACKAGE_SHORT} (${VERSION})" +VIAddVersionKey "Comments" \ + "GnuPG is Free Software; you can redistribute it \ + and/or modify it under the terms of the GNU General Public License. \ + You should have received a copy of the GNU General Public License \ + along with this software; if not, write to the Free Software \ + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, \ + MA 02110-1301, USA" +VIAddVersionKey "CompanyName" "${COMPANY}" +VIAddVersionKey "LegalTrademarks" "" +VIAddVersionKey "LegalCopyright" "${COPYRIGHT}" +VIAddVersionKey "FileDescription" "${DESCRIPTION}" +VIAddVersionKey "FileVersion" "${PROD_VERSION}" + +# Interface Settings + +# !define MUI_ABORTWARNING +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_UNFINISHPAGE_NOAUTOCLOSE + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${W32_SRCDIR}\gnupg-logo-150x57.bmp" +!define MUI_WELCOMEFINISHPAGE_BITMAP "${W32_SRCDIR}\gnupg-logo-164x314.bmp" + +# Remember the installer language +!define MUI_LANGDLL_REGISTRY_ROOT "HKCU" +!define MUI_LANGDLL_REGISTRY_KEY "Software\GnuPG" +!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" + +# +# The list of wizard pages. +# +!define MUI_WELCOMEPAGE_TITLE "$(T_WelcomeTitle)" +!define MUI_WELCOMEPAGE_TEXT "$(T_About)" +!insertmacro MUI_PAGE_WELCOME + +!define MUI_LICENSEPAGE_BUTTON "$(^NextBtn)" +!define MUI_PAGE_HEADER_SUBTEXT "$(T_GPLHeader)" +!define MUI_LICENSEPAGE_TEXT_BOTTOM "$(T_GPLShort)" +!insertmacro MUI_PAGE_LICENSE "${TOP_SRCDIR}/COPYING" + +!define MUI_PAGE_CUSTOMFUNCTION_SHOW PrintNonAdminWarning +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE CheckExistingVersion +!insertmacro MUI_PAGE_COMPONENTS + +# We don't have MUI_PAGE_DIRECTORY + +!ifdef HAVE_STARTMENU + +Page custom CustomPageOptions + +Var STARTMENU_FOLDER + +!define MUI_PAGE_CUSTOMFUNCTION_PRE CheckIfStartMenuWanted +!define MUI_STARTMENUPAGE_NODISABLE +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" +!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\GnuPG" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" +# We need to set the Startmenu name explicitly because a slash in the +# name is not possible. +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "GnuPG" + +!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER + +!endif + +!define MUI_PAGE_CUSTOMFUNCTION_PRE PrintCloseOtherApps +!insertmacro MUI_PAGE_INSTFILES + +#!define MUI_PAGE_CUSTOMFUNCTION_PRE ShowFinalWarnings +!define MUI_FINISHPAGE_SHOWREADME "README.txt" +!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(T_ShowReadme)" +#!define MUI_FINISHPAGE_RUN +#!define MUI_FINISHPAGE_RUN_FUNCTION RunOnFinish +#!define MUI_FINISHPAGE_RUN_TEXT "$(T_RunKeyManager)" +#!define MUI_FINISHPAGE_RUN_NOTCHECKED +!define MUI_FINISHPAGE_LINK "$(T_MoreInfo)" +!define MUI_FINISHPAGE_LINK_LOCATION "$(T_MoreInfoURL)" +!insertmacro MUI_PAGE_FINISH + + +# Uninstaller pages. + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + + +#Page license +#Page components +#Page directory +#Page instfiles +#UninstPage uninstConfirm +#UninstPage instfiles + + +# Language support. This has to be done after defining the pages, but +# before defining the translation strings. Confusing. + +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "German" + +!insertmacro MUI_RESERVEFILE_LANGDLL +!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS +ReserveFile "${BUILD_DIR}\g4wihelp.dll" +ReserveFile "${W32_SRCDIR}\gnupg-logo-150x57.bmp" +ReserveFile "${W32_SRCDIR}\gnupg-logo-164x314.bmp" +ReserveFile "${TOP_SRCDIR}\COPYING" +ReserveFile "${W32_SRCDIR}\inst-options.ini" + +# Language support + +LangString T_LangCode ${LANG_ENGLISH} "en" +LangString T_LangCode ${LANG_GERMAN} "de" + + +# The WelcomeTitle is displayed on the first page. +LangString T_WelcomeTitle ${LANG_ENGLISH} "${WELCOME_TITLE_ENGLISH}" +LangString T_WelcomeTitle ${LANG_GERMAN} "${WELCOME_TITLE_GERMAN}" + +# The About string as displayed on the first page. +LangString T_About ${LANG_ENGLISH} "${ABOUT_ENGLISH}" +LangString T_About ${LANG_GERMAN} "${ABOUT_GERMAN}" + +# Startup page +LangString T_GPLHeader ${LANG_ENGLISH} \ + "This software is licensed under the terms of the GNU General Public \ + License (GNU GPL)." +LangString T_GPLHeader ${LANG_GERMAN}} \ + "Diese Software ist unter der GNU General Public License \ + (GNU GPL) lizensiert." + +LangString T_GPLShort ${LANG_ENGLISH} \ + "In short: You are allowed to run this software for any purpose. \ + You may distribute it as long as you give the recipients the same \ + rights you have received." +LangString T_GPLShort ${LANG_GERMAN} \ + "In aller Kürze: Sie haben das Recht, die Software zu jedem Zweck \ + einzusetzen. Sie können die Software weitergeben, sofern Sie dem \ + Empfänger dieselben Rechte einräumen, die auch Sie erhalten haben." + +LangString T_RunKeyManager ${LANG_ENGLISH} \ + "Run the key manager" +LangString T_RunKeyManager ${LANG_GERMAN} \ + "Die Schlüsselverwaltung aufrufen" + +LangString T_MoreInfo ${LANG_ENGLISH} \ + "Click here to see how to help the GnuPG Project" +LangString T_MoreInfo ${LANG_GERMAN} \ + "Hier klicken um dem GnuPG Projekt zu zu helfen" +LangString T_MoreInfoURL ${LANG_ENGLISH} "https://gnupg.org/donate" +LangString T_MoreInfoURL ${LANG_GERMAN} "https://gnupg.org/donate" + +LangString T_ShowReadme ${LANG_ENGLISH} \ + "Show the README file" +LangString T_ShowReadme ${LANG_GERMAN} \ + "Die README Datei anzeigen" + +LangString T_NoKeyManager ${LANG_ENGLISH} \ + "No key manager has been installed, thus we can't run one now." +LangString T_NoKeyManager ${LANG_GERMAN} \ + "Es wurde keine Schlüsselverwaltung installiert. \ + Deswegen kann sie jetzt auch nicht ausgeführt werden." + +# Functions + +# Custom functions and macros for this installer. +LangString T_AlreadyRunning ${LANG_ENGLISH} \ + "An instance of this installer is already running." +LangString T_AlreadyRunning ${LANG_GERMAN} \ + "Ein Exemplar dieses Installers läuft bereits." + +Function G4wRunOnce + Push $R0 + StrCpy $R0 "gnupg" + g4wihelp::runonce + StrCmp $R0 0 +3 + MessageBox MB_OK $(T_AlreadyRunning) + Abort + Pop $R0 +FunctionEnd + +# +# Control function for the Custom page to select special +# install options. +# +Function CustomPageOptions + !insertmacro MUI_HEADER_TEXT "$(T_InstallOptions)" "$(T_InstallOptLinks)" + + # Note, that the default selection is done in the ini file + !insertmacro MUI_INSTALLOPTIONS_WRITE "${W32_SRCDIR}/inst-options.ini" \ + "Field 1" "Text" "$(T_InstOptLabelA)" + !insertmacro MUI_INSTALLOPTIONS_WRITE "${W32_SRCDIR}/inst-options.ini" \ + "Field 2" "Text" "$(T_InstOptFieldA)" + !insertmacro MUI_INSTALLOPTIONS_WRITE "${W32_SRCDIR}/inst-options.ini" \ + "Field 3" "Text" "$(T_InstOptFieldB)" + !insertmacro MUI_INSTALLOPTIONS_WRITE "${W32_SRCDIR}/inst-options.ini" \ + "Field 4" "Text" "$(T_InstOptFieldC)" + !insertmacro MUI_INSTALLOPTIONS_WRITE "${W32_SRCDIR}/inst-options.ini" \ + "Field 5" "Text" "$(T_InstOptLabelB)" + + !insertmacro MUI_INSTALLOPTIONS_DISPLAY "${W32_SRCDIR}/inst-options.ini" +FunctionEnd + + +# Check whether GnuPG has already been installed. This is called as +# a leave function from the components page. A call to abort will get +# back to the components selection. +Function CheckExistingVersion + ClearErrors + FileOpen $0 "$INSTDIR\VERSION" r + IfErrors nexttest + FileRead $0 $R0 + FileRead $0 $R1 + FileClose $0 + + Push $R1 + Call TrimNewLines + Pop $R1 + + MessageBox MB_YESNO "$(T_FoundExistingVersion)" IDYES leave + Abort + + nexttest: + ClearErrors + ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GnuPG" "DisplayVersion" + IfErrors leave 0 + MessageBox MB_YESNO "$(T_FoundExistingVersionB)" IDYES leave + Abort + + leave: +FunctionEnd + + + +# PrintNonAdminWarning + +# Check whether the current user is in the Administrator group or an +# OS version without the need for an Administrator is in use. Print a +# diagnostic if this is not the case and abort installation. +Function PrintNonAdminWarning + ClearErrors + UserInfo::GetName + IfErrors leave + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" leave +1 + MessageBox MB_OK "$(T_AdminNeeded)" + Quit + + leave: +FunctionEnd + + +# Check whether the start menu is actually wanted. + +Function CheckIfStartMenuWanted + !insertmacro MUI_INSTALLOPTIONS_READ $R0 "${W32_SRCDIR}/inst-options.ini" \ + "Field 2" "State" + IntCmp $R0 1 +2 + Abort +FunctionEnd + + +# Check whether this is a reinstall and popup a message box to explain +# that it is better to close other apps before continuing +Function PrintCloseOtherApps + IfFileExists $INSTDIR\bin\gpg.exe print_warning + IfFileExists $INSTDIR\bin\gpa.exe print_warning + Return + print_warning: + MessageBox MB_OK|MB_ICONEXCLAMATION "$(T_CloseOtherApps)" + +FunctionEnd + +# Called right before the final page to show more warnings. +#Function ShowFinalWarnings +# leave: +#FunctionEnd + +#----------------------------------------------- +# Strings pertaining to the install options page +#----------------------------------------------- + +# Installation options title +LangString T_InstallOptions ${LANG_ENGLISH} "Install Options" +LangString T_InstallOptions ${LANG_GERMAN} "Installationsoptionen" + +# Installation options subtitle 1 +LangString T_InstallOptLinks ${LANG_ENGLISH} "Start links" +LangString T_InstallOptLinks ${LANG_GERMAN} "Startlinks" + +LangString T_InstOptLabelA ${LANG_ENGLISH} \ + "Please select where GnuPG shall install links:" +LangString T_InstOptLabelA ${LANG_GERMAN} \ + "Bitte wählen Sie, welche Verknüpfungen angelegt werden sollen:" + +LangString T_InstOptLabelB ${LANG_ENGLISH} \ + "(Only programs will be linked into the quick launch bar.)" +LangString T_InstOptLabelB ${LANG_GERMAN} \ + "(In die Schnellstartleiste werden nur Verknüpfungen für \ + Programme angelegt.) " + +LangString T_InstOptFieldA ${LANG_ENGLISH} \ + "Start Menu" +LangString T_InstOptFieldA ${LANG_GERMAN} \ + "Startmenü" + +LangString T_InstOptFieldB ${LANG_ENGLISH} \ + "Desktop" +LangString T_InstOptFieldB ${LANG_GERMAN} \ + "Arbeitsfläche" + +LangString T_InstOptFieldC ${LANG_ENGLISH} \ + "Quick Launch Bar" +LangString T_InstOptFieldC ${LANG_GERMAN} \ + "Schnellstartleiste" + +#------------------------------------------------ +# String pertaining to the existing version check +#------------------------------------------------ +LangString T_FoundExistingVersion ${LANG_ENGLISH} \ + "Version $R1 has already been installed. $\r$\n\ + Do you want to overwrite it with version ${VERSION}?" +LangString T_FoundExistingVersion ${LANG_GERMAN} \ + "Version $R1 ist hier bereits installiert. $\r$\n\ + Möchten Sie diese mit Version ${VERSION} überschreiben? $\r$\n\ + $\r$\n\ + (Sie können in jedem Fall mit JA antworten, falls es sich um \ + eine neuere oder dieselbe Version handelt.)" +LangString T_FoundExistingVersionB ${LANG_ENGLISH} \ + "A version of GnuPG has already been installed on the system. \ + There will be no problem installing and thus overwriting this \ + Version. $\r$\n\ + $\r$\n\ + Do you want to continue installing GnuPG?" +LangString T_FoundExistingVersionB ${LANG_GERMAN} \ + "Eine Version von GnuPG ist hier bereits installiert. \ + Es ist problemlos möglich, die Installation fortzuführen. $\r$\n\ + $\r$\n\ + Möchten die die Installation von GnuPG fortführen?" + + + +# From Function PrintNonAdminWarning +LangString T_AdminNeeded ${LANG_ENGLISH} \ + "Warning: Administrator permissions required for a successful installation" +LangString T_AdminNeeded ${LANG_GERMAN} \ + "Achtung: Für eine erfolgreiche Installation werden \ + Administratorrechte benötigt." + +# From Function PrintCloseOtherApps +LangString T_CloseOtherApps ${LANG_ENGLISH} \ + "Please make sure that other applications are not running. \ + GnuPG will try to install anyway but a reboot may be required." +LangString T_CloseOtherApps ${LANG_GERMAN} \ + "Bitte stellen Sie sicher, daß alle anderen Anwendugen geschlossen \ + sind. GnuPG wird auf jeden Fall versuchen, eine Installation \ + durchzuführen; es ist dann aber u.U. notwendig, das System neu zu starten." + + +# TrimNewlines - taken from the NSIS reference +# input, top of stack (e.g. whatever$\r$\n) +# output, top of stack (replaces, with e.g. whatever) +# modifies no other variables. +Function TrimNewlines + Exch $R0 + Push $R1 + Push $R2 + StrCpy $R1 0 + + loop: + IntOp $R1 $R1 - 1 + StrCpy $R2 $R0 1 $R1 + StrCmp $R2 "$\r" loop + StrCmp $R2 "$\n" loop + IntOp $R1 $R1 + 1 + IntCmp $R1 0 no_trim_needed + StrCpy $R0 $R0 $R1 + + no_trim_needed: + Pop $R2 + Pop $R1 + Exch $R0 +FunctionEnd + + +# AddToPath - Adds the given dir to the search path. +# Input - head of the stack +Function AddToPath + Exch $0 + g4wihelp::path_add "$0" + StrCmp $R5 "0" add_to_path_done + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + add_to_path_done: + Pop $0 +FunctionEnd + + +# RemoveFromPath - Remove a given dir from the path +# Input: head of the stack +Function un.RemoveFromPath + Exch $0 + g4wihelp::path_remove "$0" + StrCmp $R5 "0" remove_from_path_done + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + remove_from_path_done: + Pop $0 +FunctionEnd + + +# +# Define the installer sections. +# + +Section "-gnupginst" + SetOutPath "$INSTDIR" + + File "${BUILD_DIR}/README.txt" + + # Write a version file. + FileOpen $0 "$INSTDIR\VERSION" w + FileWrite $0 "${PACKAGE}$\r$\n" + FileWrite $0 "${VERSION}$\r$\n" + FileClose $0 + + WriteRegStr HKLM "Software\GnuPG" "Install Directory" $INSTDIR + + # If we are reinstalling, try to kill a possible running gpa using + # an already installed gpa. + ifFileExists "$INSTDIR\bin\launch-gpa.exe" 0 no_uiserver + nsExec::ExecToLog '"$INSTDIR\bin\launch-gpa" "--stop-server"' + + no_uiserver: + + # If we are reinstalling, try to kill a possible running agent using + # an already installed gpgconf. + + ifFileExists "$INSTDIR\bin\gpgconf.exe" 0 no_gpgconf + nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "dirmngr"' + nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "gpg-agent"' + + no_gpgconf: + + # Add the bin directory to the PATH + Push "$INSTDIR\bin" + Call AddToPath + DetailPrint "Added $INSTDIR\bin to PATH" +SectionEnd + +LangString DESC_Menu_gnupg_readme ${LANG_ENGLISH} \ + "General information on GnuPG" +LangString DESC_Menu_gnupg_readme ${LANG_GERMAN} \ + "Allgemeine Informationen zu GnuPG" + + +Section "GnuPG" SEC_gnupg + SectionIn RO + + SetOutPath "$INSTDIR\bin" + File "bin/gpg.exe" + File "bin/gpgv.exe" + File "bin/gpgsm.exe" + File "bin/gpgconf.exe" + File "bin/gpg-connect-agent.exe" + File "bin/gpgtar.exe" + File "libexec/gpg-preset-passphrase.exe" + + ClearErrors + SetOverwrite try + File "bin/gpg-agent.exe" + SetOverwrite lastused + ifErrors 0 +3 + File /oname=gpg-agent.exe.tmp "bin/gpg-agent.exe" + Rename /REBOOTOK gpg-agent.exe.tmp gpg-agent.exe + + ClearErrors + SetOverwrite try + File "bin/dirmngr.exe" + SetOverwrite lastused + ifErrors 0 +3 + File /oname=dirmngr.exe.tmp "bin/dirmngr.exe" + Rename /REBOOTOK dirmngr.exe.tmp dirmngr.exe + + ClearErrors + SetOverwrite try + File "libexec/scdaemon.exe" + SetOverwrite lastused + ifErrors 0 +3 + File /oname=scdaemon.exe.tmp "libexec/scdaemon.exe" + Rename /REBOOTOK scdaemon.exe.tmp scdaemon.exe + + SetOutPath "$INSTDIR\share\gnupg" + File "share/gnupg/gpg-conf.skel" + File "share/gnupg/dirmngr-conf.skel" + File "share/gnupg/distsigkey.gpg" + + SetOutPath "$INSTDIR\share\locale\ca\LC_MESSAGES" + File share/locale/ca/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\cs\LC_MESSAGES" + File share/locale/cs/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\da\LC_MESSAGES" + File share/locale/da/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\de\LC_MESSAGES" + File share/locale/de/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\el\LC_MESSAGES" + File share/locale/el/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\en@boldquot\LC_MESSAGES" + File share/locale/en@boldquot/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\en@quot\LC_MESSAGES" + File share/locale/en@quot/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\eo\LC_MESSAGES" + File share/locale/eo/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\es\LC_MESSAGES" + File share/locale/es/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\et\LC_MESSAGES" + File share/locale/et/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\fi\LC_MESSAGES" + File share/locale/fi/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\fr\LC_MESSAGES" + File share/locale/fr/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\gl\LC_MESSAGES" + File share/locale/gl/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\hu\LC_MESSAGES" + File share/locale/hu/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\id\LC_MESSAGES" + File share/locale/id/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\it\LC_MESSAGES" + File share/locale/it/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\ja\LC_MESSAGES" + File share/locale/ja/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\nb\LC_MESSAGES" + File share/locale/nb/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\pl\LC_MESSAGES" + File share/locale/pl/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\pt\LC_MESSAGES" + File share/locale/pt/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\ro\LC_MESSAGES" + File share/locale/ro/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\ru\LC_MESSAGES" + File share/locale/ru/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\sk\LC_MESSAGES" + File share/locale/sk/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\sv\LC_MESSAGES" + File share/locale/sv/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\tr\LC_MESSAGES" + File share/locale/tr/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\uk\LC_MESSAGES" + File share/locale/uk/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\zh_CN\LC_MESSAGES" + File share/locale/zh_CN/LC_MESSAGES/gnupg2.mo + SetOutPath "$INSTDIR\share\locale\zh_TW\LC_MESSAGES" + File share/locale/zh_TW/LC_MESSAGES/gnupg2.mo +SectionEnd + + +LangString DESC_SEC_gnupg ${LANG_ENGLISH} \ + "The GnuPG Core is the actual encrypt core and a set of command \ + line utilities." +LangString DESC_SEC_gnupg ${LANG_GERMAN} \ + "Der GnuPG Core ist, wie der Name schon sagt, der Kernbestandteil \ + dieser Software. Der GnuPG Core stellt die eigentliche \ + Verschlüsselung sowie die Verwaltung der Schlüssel bereit." + +LangString DESC_Menu_gnupg_manual ${LANG_ENGLISH} \ + "Show the manual for the GnuPG Core" +LangString DESC_Menu_gnupg_manual ${LANG_GERMAN} \ + "Das Handbuch zum GnuPG Kern anzeigen" + +Section "-libgpg-error" SEC_libgpg_error + SetOutPath "$INSTDIR\bin" + File bin/libgpg-error-0.dll + SetOutPath "$INSTDIR\lib" + File /oname=libgpg-error.imp lib/libgpg-error.dll.a + SetOutPath "$INSTDIR\include" + File include/gpg-error.h + SetOutPath "$INSTDIR\share\locale\cs\LC_MESSAGES" + File share/locale/cs/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\da\LC_MESSAGES" + File share/locale/da/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\de\LC_MESSAGES" + File share/locale/de/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\eo\LC_MESSAGES" + File share/locale/eo/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\fr\LC_MESSAGES" + File share/locale/fr/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\hu\LC_MESSAGES" + File share/locale/hu/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\it\LC_MESSAGES" + File share/locale/it/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\ja\LC_MESSAGES" + File share/locale/ja/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\nl\LC_MESSAGES" + File share/locale/nl/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\pl\LC_MESSAGES" + File share/locale/pl/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\pt\LC_MESSAGES" + File share/locale/pt/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\ro\LC_MESSAGES" + File share/locale/ro/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\ru\LC_MESSAGES" + File share/locale/ru/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\sr\LC_MESSAGES" + File share/locale/sr/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\sv\LC_MESSAGES" + File share/locale/sv/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\uk\LC_MESSAGES" + File share/locale/uk/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\vi\LC_MESSAGES" + File share/locale/vi/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\zh_CN\LC_MESSAGES" + File share/locale/zh_CN/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\zh_TW\LC_MESSAGES" + File share/locale/zh_TW/LC_MESSAGES/libgpg-error.mo +SectionEnd + +Section "-zlib" SEC_zlib + SetOutPath "$INSTDIR\bin" + File bin/zlib1.dll +SectionEnd + +Section "-adns" SEC_adns + SetOutPath "$INSTDIR\bin" + File bin/libadns-1.dll + SetOutPath "$INSTDIR\lib" + File /oname=libadns.imp lib/libadns.dll.a + SetOutPath "$INSTDIR\include" + File include/adns.h +SectionEnd + +Section "-npth" SEC_npth + SetOutPath "$INSTDIR\bin" + File bin/libnpth-0.dll + SetOutPath "$INSTDIR\lib" + File /oname=libnpth.imp lib/libnpth.dll.a + SetOutPath "$INSTDIR\include" + File include/npth.h +SectionEnd + +Section "-gcrypt" SEC_gcrypt + SetOutPath "$INSTDIR\bin" + File bin/libgcrypt-20.dll + SetOutPath "$INSTDIR\lib" + File /oname=libgcrypt.imp lib/libgcrypt.dll.a + SetOutPath "$INSTDIR\include" + File include/gcrypt.h +SectionEnd + +Section "-assuan" SEC_assuan + SetOutPath "$INSTDIR\bin" + File bin/libassuan-0.dll + SetOutPath "$INSTDIR\lib" + File /oname=libassuan.imp lib/libassuan.dll.a + SetOutPath "$INSTDIR\include" + File include/assuan.h +SectionEnd + +Section "-ksba" SEC_ksba + SetOutPath "$INSTDIR\bin" + File bin/libksba-8.dll + SetOutPath "$INSTDIR\lib" + File /oname=libksba.imp lib/libksba.dll.a + SetOutPath "$INSTDIR\include" + File include/ksba.h +SectionEnd + +Section "-gpgme" SEC_gpgme + SetOutPath "$INSTDIR\bin" + File bin/libgpgme-11.dll + File /nonfatal bin/libgpgme-glib-11.dll + File libexec/gpgme-w32spawn.exe + SetOutPath "$INSTDIR\lib" + File /oname=libgpgme.imp lib/libgpgme.dll.a + File /nonfatal /oname=libgpgme-glib.imp lib/libgpgme-glib.dll.a + SetOutPath "$INSTDIR\include" + File include/gpgme.h +SectionEnd + +Section "-sqlite" SEC_sqlite + SetOutPath "$INSTDIR\bin" + File bin/libsqlite3-0.dll +SectionEnd + +!ifdef WITH_GUI +Section "-libiconv" SEC_libiconv + SetOutPath "$INSTDIR\bin" + File bin/libiconv-2.dll +SectionEnd + +Section "-gettext" SEC_gettext + SetOutPath "$INSTDIR\bin" + File bin/libintl-8.dll +SectionEnd + +Section "-glib" SEC_glib + SetOutPath "$INSTDIR\bin" + File bin/libgio-2.0-0.dll + File bin/libglib-2.0-0.dll + File bin/libgmodule-2.0-0.dll + File bin/libgobject-2.0-0.dll + File bin/libgthread-2.0-0.dll + File bin/gspawn-win32-helper.exe + File bin/gspawn-win32-helper-console.exe + + File bin/libffi-6.dll +SectionEnd + +Section "-libpng" SEC_libpng + SetOutPath "$INSTDIR\bin" + File bin/libpng14-14.dll +SectionEnd + +#Section "-jpeg" SEC_jpeg +# SetOutPath "$INSTDIR" +# File bin/jpeg62.dll +#SectionEnd + +Section "-cairo" SEC_cairo + SetOutPath "$INSTDIR\bin" + File bin/libcairo-gobject-2.dll + File bin/libpangocairo-1.0-0.dll + File bin/libcairo-2.dll + File bin/libcairo-script-interpreter-2.dll +SectionEnd + +Section "-pixman" SEC_pixman + SetOutPath "$INSTDIR\bin" + File bin/libpixman-1-0.dll +SectionEnd + +Section "-pango" SEC_pango + SetOutPath "$INSTDIR\bin" + File bin/pango-querymodules.exe + File bin/libpango-1.0-0.dll + File bin/libpangowin32-1.0-0.dll + + SetOutPath "$INSTDIR\lib\pango\1.6.0\modules" + File lib/pango/1.6.0/modules/pango-basic-win32.dll + File lib/pango/1.6.0/modules/pango-arabic-lang.dll + File lib/pango/1.6.0/modules/pango-indic-lang.dll + + SetOutPath "$INSTDIR\etc\pango" + File ${W32_SRCDIR}/pango.modules +SectionEnd + +Section "-atk" SEC_atk + SetOutPath "$INSTDIR\bin" + File bin/libatk-1.0-0.dll +SectionEnd + +Section "-gtk+" SEC_gtk_ + SetOutPath "$INSTDIR\bin" + File bin/libgdk_pixbuf-2.0-0.dll + File bin/libgdk-win32-2.0-0.dll + File bin/libgtk-win32-2.0-0.dll + + SetOutPath "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0" + File /oname=loaders.cache ${W32_SRCDIR}/gdk-pixbuf-loaders.cache + SetOutPath "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders" + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-ani.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-bmp.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-emf.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-gif.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-ico.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-jpeg.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-tiff.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gdip-wmf.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-icns.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-pcx.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-png.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-pnm.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-qtif.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-ras.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-tga.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-wbmp.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-xbm.dll + File lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-xpm.dll + + SetOutPath "$INSTDIR\lib\gtk-2.0\2.10.0\engines" + File lib/gtk-2.0/2.10.0/engines/libwimp.dll + File lib/gtk-2.0/2.10.0/engines/libpixmap.dll + + SetOutPath "$INSTDIR\lib\gtk-2.0\2.10.0\immodules" + File lib/gtk-2.0/2.10.0/immodules/im-thai.dll + File lib/gtk-2.0/2.10.0/immodules/im-cyrillic-translit.dll + File lib/gtk-2.0/2.10.0/immodules/im-multipress.dll + File lib/gtk-2.0/2.10.0/immodules/im-ti-er.dll + File lib/gtk-2.0/2.10.0/immodules/im-am-et.dll + File lib/gtk-2.0/2.10.0/immodules/im-cedilla.dll + File lib/gtk-2.0/2.10.0/immodules/im-inuktitut.dll + File lib/gtk-2.0/2.10.0/immodules/im-viqr.dll + File lib/gtk-2.0/2.10.0/immodules/im-ti-et.dll + File lib/gtk-2.0/2.10.0/immodules/im-ipa.dll + File lib/gtk-2.0/2.10.0/immodules/im-ime.dll + + SetOutPath "$INSTDIR\share\themes\Default\gtk-2.0-key" + File share/themes/Default/gtk-2.0-key/gtkrc + + SetOutPath "$INSTDIR\share\themes\MS-Windows\gtk-2.0" + File share/themes/MS-Windows/gtk-2.0/gtkrc + + SetOutPath "$INSTDIR\etc\gtk-2.0" + File etc/gtk-2.0/im-multipress.conf +SectionEnd +!endif + +Section "-pinentry" SEC_pinentry + SetOutPath "$INSTDIR\bin" + File /oname=pinentry-basic.exe "bin/pinentry-w32.exe" +SectionEnd + +!ifdef WITH_GUI +Section "gpa" SEC_gpa + SectionIn RO + SetOutPath "$INSTDIR\bin" + File bin/gpa.exe + File bin/launch-gpa.exe +SectionEnd + +LangString DESC_SEC_gpa ${LANG_ENGLISH} \ + "The GnuPG Assistant is the graphical interface of GnuPG" +LangString DESC_SEC_gpa ${LANG_GERMAN} \ + "Der GnuPG Assistent ist die graphische Oberfläche von GnuPG." + +LangString DESC_Menu_gpa ${LANG_ENGLISH} \ + "Run the GnuGP Assistant." +LangString DESC_Menu_gpa ${LANG_GERMAN} \ + "Den GnuPG Assistenten starten." + +Section "gpgex" SEC_gpgex + SetOutPath "$INSTDIR\bin" + + ClearErrors + SetOverwrite try + File bin/gpgex.dll + SetOverwrite lastused + ifErrors 0 do_reg + File /oname=gpgex.dll.tmp bin/gpgex.dll + Rename /REBOOTOK gpgex.dll.tmp gpgex.dll + + do_reg: + ClearErrors + RegDLL "$INSTDIR\bin\gpgex.dll" + ifErrors 0 +2 + MessageBox MB_OK "$(T_GPGEX_RegFailed)" + +${If} ${RunningX64} + # Install the 64 bit version of the plugin. + # Note that we install this in addition to the 32 bit version so that + # the 32 bit version can be used by file dialogs of 32 bit programs. + ClearErrors + SetOverwrite try + File /oname=gpgex6.dll "${INST6_DIR}/bin/gpgex.dll" + SetOverwrite lastused + ifErrors 0 do_reg64 + File /oname=gpgex6.dll.tmp "${INST6_DIR}/bin/gpgex.dll" + Rename /REBOOTOK gpgex6.dll.tmp gpgex6.dll + + do_reg64: + # Register the DLL. We need to register both versions. However + # RegDLL can't be used for 64 bit and InstallLib seems to be a + # registry hack. + ClearErrors + nsExec::ExecToLog '"$SYSDIR\regsvr32" "/s" "$INSTDIR\bin\gpgex6.dll"' + ifErrors 0 +2 + MessageBox MB_OK "$(T_GPGEX_RegFailed) (64 bit)" + + # Note: There is no need to install the help an mo files because + # they are identical to those installed by the 32 bit version. +${EndIf} +SectionEnd + +LangString T_GPGEX_RegFailed ${LANG_ENGLISH} \ + "Warning: Registration of the Explorer plugin failed." + +LangString DESC_SEC_gpgex ${LANG_ENGLISH} \ + "GnuPG Explorer Extension" + +!endif + + +Section "-gnupglast" SEC_gnupglast + SetOutPath "$INSTDIR" +SectionEnd + + +# +# Define the uninstaller sections. +# +# (reverse order of the installer sections!) +# + +Section "-un.gnupglast" + ifFileExists "$INSTDIR\bin\launch-gpa.exe" 0 no_uiserver + nsExec::ExecToLog '"$INSTDIR\bin\launch-gpa" "--stop-server"' + no_uiserver: + ifFileExists "$INSTDIR\bin\gpgconf.exe" 0 no_gpgconf + nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "gpg-agent"' + nsExec::ExecToLog '"$INSTDIR\bin\gpgconf" "--kill" "dirmngr"' + no_gpgconf: +SectionEnd + +Section "-un.gpgex" + UnRegDLL "$INSTDIR\bin\gpgex.dll" + + Delete /REBOOTOK "$INSTDIR\bin\gpgex.dll" + +${If} ${RunningX64} + nsExec::ExecToLog '"$SYSDIR\regsvr32" "/u" "/s" "$INSTDIR\bin\gpgex6.dll"' + Delete /REBOOTOK "$INSTDIR\bin\gpgex6.dll" +${EndIf} +SectionEnd + +!ifdef WITH_GUI +Section "-un.gpa" + Delete "$INSTDIR\bin\gpa.exe" + Delete "$INSTDIR\bin\launch-gpa.exe" + + RMDir "$INSTDIR\share\gpa" +SectionEnd +!endif + +Section "-un.pinentry" + Delete "$INSTDIR\bin\pinentry-basic.exe" +SectionEnd + +!ifdef WITH_GUI +Section "-un.gtk+" + Delete "$INSTDIR\bin\libgdk_pixbuf-2.0-0.dll" + Delete "$INSTDIR\bin\libgdk-win32-2.0-0.dll" + Delete "$INSTDIR\bin\libgtk-win32-2.0-0.dll" + + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache" + + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-ani.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-bmp.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-emf.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-gif.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-ico.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-jpeg.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-tiff.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-gdip-wmf.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-icns.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-pcx.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-png.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-pnm.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-qtif.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-ras.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-tga.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-wbmp.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-xbm.dll" + Delete "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-xpm.dll" + RMDir "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0\loaders" + RMDir "$INSTDIR\lib\gdk-pixbuf-2.0\2.10.0" + RMDir "$INSTDIR\lib\gdk-pixbuf-2.0" + + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\engines\libwimp.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\engines\libpixmap.dll" + RMDir "$INSTDIR\lib\gtk-2.0\2.10.0\engines" + + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-thai.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-cyrillic-translit.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-multipress.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-ti-er.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-am-et.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-cedilla.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-inuktitut.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-viqr.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-ti-et.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-ipa.dll" + Delete "$INSTDIR\lib\gtk-2.0\2.10.0\immodules\im-ime.dll" + RMDir "$INSTDIR\lib\gtk-2.0\2.10.0\immodules" + + RMDir "$INSTDIR\lib\gtk-2.0\2.10.0" + RMDir "$INSTDIR\lib\gtk-2.0" + + Delete "$INSTDIR\share\themes\Default\gtk-2.0-key\gtkrc" + RMDir "$INSTDIR\share\themes\Default\gtk-2.0-key" + RMDir "$INSTDIR\share\themes\Default" + + Delete "$INSTDIR\share\themes\MS-Windows\gtk-2.0\gtkrc" + RMDir "$INSTDIR\share\themes\MS-Windows\gtk-2.0" + RMDir "$INSTDIR\share\themes\MS-Windows" + + RMDir "$INSTDIR\share\themes" + + Delete "$INSTDIR\etc\gtk-2.0\im-multipress.conf" + RMDir "$INSTDIR\etc\gtk-2.0" +SectionEnd + +Section "-un.atk" + Delete "$INSTDIR\bin\libatk-1.0-0.dll" +SectionEnd + +Section "-un.pango" + Delete "$INSTDIR\bin\pango-querymodules.exe" + Delete "$INSTDIR\bin\libpango-1.0-0.dll" + Delete "$INSTDIR\bin\libpangowin32-1.0-0.dll" + + Delete "$INSTDIR\lib\pango\1.6.0\modules\pango-basic-win32.dll" + Delete "$INSTDIR\lib\pango\1.6.0\modules\pango-arabic-lang.dll" + Delete "$INSTDIR\lib\pango\1.6.0\modules\pango-indic-lang.dll" + RMDir "$INSTDIR\lib\pango\1.6.0\modules" + RMDir "$INSTDIR\lib\pango\1.6.0" + RMDir "$INSTDIR\lib\pango" + + Delete "$INSTDIR\etc\pango\pango.modules" + RMDir "$INSTDIR\etc\pango" +SectionEnd + +Section "-un.pixman" + Delete "$INSTDIR\bin\libpixman-1-0.dll" +SectionEnd + +Section "-un.cairo" + Delete "$INSTDIR\bin\libcairo-gobject-2.dll" + Delete "$INSTDIR\bin\libpangocairo-1.0-0.dll" + Delete "$INSTDIR\bin\libcairo-2.dll" + Delete "$INSTDIR\bin\libcairo-script-interpreter-2.dll" +SectionEnd + +Section "-un.libpng" + Delete "$INSTDIR\bin\libpng14-14.dll" +SectionEnd + +Section "-un.glib" + Delete "$INSTDIR\bin\libgio-2.0-0.dll" + Delete "$INSTDIR\bin\libglib-2.0-0.dll" + Delete "$INSTDIR\bin\libgmodule-2.0-0.dll" + Delete "$INSTDIR\bin\libgobject-2.0-0.dll" + Delete "$INSTDIR\bin\libgthread-2.0-0.dll" + Delete "$INSTDIR\bin\gspawn-win32-helper.exe" + Delete "$INSTDIR\bin\gspawn-win32-helper-console.exe" + Delete "$INSTDIR\bin\libffi-6.dll" +SectionEnd +!endif + + +Section "-un.gettext" + Delete "$INSTDIR\bin\libintl-8.dll" +SectionEnd + +Section "-un.libiconv" + Delete "$INSTDIR\bin\libiconv-2.dll" +SectionEnd + +Section "-un.gpgme" + Delete "$INSTDIR\bin\libgpgme-11.dll" + Delete "$INSTDIR\bin\libgpgme-glib-11.dll" + Delete "$INSTDIR\bin\gpgme-w32spawn.exe" + Delete "$INSTDIR\lib\libgpgme.imp" + Delete "$INSTDIR\lib\libgpgme-glib.imp" + Delete "$INSTDIR\include\gpgme.h" +SectionEnd + +Section "-un.ksba" + Delete "$INSTDIR\bin\libksba-8.dll" + Delete "$INSTDIR\lib\libksba.imp" + Delete "$INSTDIR\include\ksba.h" +SectionEnd + +Section "-un.assuan" + Delete "$INSTDIR\bin\libassuan-0.dll" + Delete "$INSTDIR\lib\libassuan.imp" + Delete "$INSTDIR\include\assuan.h" +SectionEnd + +Section "-un.gcrypt" + Delete "$INSTDIR\bin\libgcrypt-20.dll" + Delete "$INSTDIR\lib\libgcrypt.imp" + Delete "$INSTDIR\include\gcrypt.h" +SectionEnd + +Section "-un.npth" + Delete "$INSTDIR\bin\libnpth-0.dll" + Delete "$INSTDIR\lib\libnpth.imp" + Delete "$INSTDIR\include\npth.h" +SectionEnd + +Section "-un.adns" + Delete "$INSTDIR\bin\libadns-1.dll" + Delete "$INSTDIR\lib\libadns.imp" + Delete "$INSTDIR\include\adns.h" +SectionEnd + +Section "-un.zlib" + Delete "$INSTDIR\bin\zlib1.dll" +SectionEnd + +Section "-un.libgpg-error" + Delete "$INSTDIR\bin\libgpg-error-0.dll" + Delete "$INSTDIR\lib\libgpg-error.imp" + Delete "$INSTDIR\include\gpg-error.h" + Delete "$INSTDIR\share\locale\cs\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\cs\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\cs" + Delete "$INSTDIR\share\locale\da\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\da\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\da" + Delete "$INSTDIR\share\locale\de\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\de\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\de" + Delete "$INSTDIR\share\locale\eo\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\eo\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\eo" + Delete "$INSTDIR\share\locale\fr\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\fr\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\fr" + Delete "$INSTDIR\share\locale\hu\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\hu\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\hu" + Delete "$INSTDIR\share\locale\it\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\it\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\it" + Delete "$INSTDIR\share\locale\ja\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\ja\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ja" + Delete "$INSTDIR\share\locale\nl\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\nl\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\nl" + Delete "$INSTDIR\share\locale\pl\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\pl\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\pl" + Delete "$INSTDIR\share\locale\pt\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\pt\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\pt" + Delete "$INSTDIR\share\locale\ro\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\ro\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ro" + Delete "$INSTDIR\share\locale\ru\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\ru\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ru" + Delete "$INSTDIR\share\locale\sr\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\sr\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\sr" + Delete "$INSTDIR\share\locale\sv\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\sv\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\sv" + Delete "$INSTDIR\share\locale\uk\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\uk\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\uk" + Delete "$INSTDIR\share\locale\vi\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\vi\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\vi" + Delete "$INSTDIR\share\locale\zh_CN\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\zh_CN\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\zh_CN" + Delete "$INSTDIR\share\locale\zh_TW\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\zh_TW\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\zh_TW" + RMDir "$INSTDIR\share\locale" +SectionEnd + +Section "-un.gnupg" + Delete "$INSTDIR\bin\gpg.exe" + Delete "$INSTDIR\bin\gpgv.exe" + Delete "$INSTDIR\bin\gpgsm.exe" + Delete "$INSTDIR\bin\gpg-agent.exe" + Delete "$INSTDIR\bin\scdaemon.exe" + Delete "$INSTDIR\bin\dirmngr.exe" + Delete "$INSTDIR\bin\gpgconf.exe" + Delete "$INSTDIR\bin\gpg-connect-agent.exe" + Delete "$INSTDIR\bin\gpgtar.exe" + Delete "$INSTDIR\bin\gpg-preset-passphrase.exe" + + Delete "$INSTDIR\share\gnupg\dirmngr-conf.skel" + Delete "$INSTDIR\share\gnupg\distsigkey.gpg" + Delete "$INSTDIR\share\gnupg\gpg-conf.skel" + RMDir "$INSTDIR\share\gnupg" + + Delete "$INSTDIR\share\locale\ca\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\ca\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ca" + Delete "$INSTDIR\share\locale\cs\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\cs\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\cs" + Delete "$INSTDIR\share\locale\da\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\da\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\da" + Delete "$INSTDIR\share\locale\de\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\de\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\de" + Delete "$INSTDIR\share\locale\el\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\el\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\el" + Delete "$INSTDIR\share\locale\en@boldquot\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\en@boldquot\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\en@boldquot" + Delete "$INSTDIR\share\locale\en@quot\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\en@quot\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\en@quot" + Delete "$INSTDIR\share\locale\eo\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\eo\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\eo" + Delete "$INSTDIR\share\locale\es\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\es\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\es" + Delete "$INSTDIR\share\locale\et\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\et\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\et" + Delete "$INSTDIR\share\locale\fi\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\fi\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\fi" + Delete "$INSTDIR\share\locale\fr\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\fr\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\fr" + Delete "$INSTDIR\share\locale\gl\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\gl\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\gl" + Delete "$INSTDIR\share\locale\hu\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\hu\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\hu" + Delete "$INSTDIR\share\locale\id\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\id\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\id" + Delete "$INSTDIR\share\locale\it\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\it\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\it" + Delete "$INSTDIR\share\locale\ja\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\ja\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ja" + Delete "$INSTDIR\share\locale\nb\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\nb\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\nb" + Delete "$INSTDIR\share\locale\pl\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\pl\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\pl" + Delete "$INSTDIR\share\locale\pt\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\pt\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\pt" + Delete "$INSTDIR\share\locale\ro\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\ro\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ro" + Delete "$INSTDIR\share\locale\ru\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\ru\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\ru" + Delete "$INSTDIR\share\locale\sk\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\sk\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\sk" + Delete "$INSTDIR\share\locale\sv\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\sv\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\sv" + Delete "$INSTDIR\share\locale\tr\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\tr\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\tr" + Delete "$INSTDIR\share\locale\uk\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\uk\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\uk" + Delete "$INSTDIR\share\locale\zh_CN\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\zh_CN\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\zh_CN" + Delete "$INSTDIR\share\locale\zh_TW\LC_MESSAGES\gnupg2.mo" + RMDir "$INSTDIR\share\locale\zh_TW\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\zh_TW" + RMDir "$INSTDIR\share\locale" +SectionEnd + +Section "-un.sqlite" + Delete "$INSTDIR\bin\libsqlite3-0.dll" +SectionEnd + +Section "-un.gnupginst" + # Delete standard stuff. + Delete "$INSTDIR\README.txt" + + Delete "$INSTDIR\VERSION" + + # Remove the bin directory from the PATH + Push "$INSTDIR\bin" + Call un.RemoveFromPath + + # Try to remove the top level directories. + RMDir "$INSTDIR\bin" + RMDir "$INSTDIR\lib" + RMDir "$INSTDIR\include" + RMDir "$INSTDIR\share" + RMDir "$INSTDIR\etc" + RMDir "$INSTDIR" + + # Clean the registry. + DeleteRegValue HKLM "Software\GNU\GnuPG" "Install Directory" +SectionEnd + + +Function .onInit + ;;!define MUI_LANGDLL_ALWAYSSHOW + !insertmacro MUI_LANGDLL_DISPLAY + + Call G4wRunOnce + + SetOutPath $TEMP +#!ifdef SOURCES +# File /oname=gpgspltmp.bmp "${TOP_SRCDIR}/doc/logo/gnupg-logo-400px.bmp" +# # We play the tune only for the soruce installer +# File /oname=gpgspltmp.wav "${TOP_SRCDIR}/src/gnupg-splash.wav" +# g4wihelp::playsound $TEMP\gpgspltmp.wav +# g4wihelp::showsplash 2500 $TEMP\gpgspltmp.bmp + +# Delete $TEMP\gpgspltmp.bmp +# # Note that we delete gpgspltmp.wav in .onInst{Failed,Success} +#!endif + + # We can't use TOP_SRCDIR dir as the name of the file needs to be + # the same while building and running the installer. Thus we + # generate the file from a template. + !insertmacro MUI_INSTALLOPTIONS_EXTRACT "${W32_SRCDIR}/inst-options.ini" + + #Call CalcDepends +FunctionEnd + + +#Function .onInstFailed +# Delete $TEMP\gpgspltmp.wav +#FunctionEnd + +#Function .onInstSuccess +# Delete $TEMP\gpgspltmp.wav +#FunctionEnd + +#Function .onSelChange +# Call CalcDepends +#FunctionEnd + + +# This must be in a central place. Urgs. + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gnupg} $(DESC_SEC_gnupg) +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gpa} $(DESC_SEC_gpa) +!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gpgex} $(DESC_SEC_gpgex) +!insertmacro MUI_FUNCTION_DESCRIPTION_END + + +# This also must be in a central place. Also Urgs. + +!ifdef WITH_GUI +Section "-startmenu" + +!ifdef HAVE_STARTMENU + # Make sure that the context of the automatic variables has been set to + # the "all users" shell folder. This guarantees that the menu gets written + # for all users. We have already checked that we are running as Admin; or + # we printed a warning that installation will not succeed. + SetShellVarContext all + + # Check if the start menu entries where requested. + !insertmacro MUI_INSTALLOPTIONS_READ $R0 "${W32_SRCDIR}/inst-options.ini" \ + "Field 2" "State" + IntCmp $R0 0 no_start_menu + +!insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" + + SectionGetFlags ${SEC_gpa} $R0 + IntOp $R0 $R0 & ${SF_SELECTED} + IntCmp $R0 ${SF_SELECTED} 0 no_gpa_menu + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\GPA.lnk" \ + "$INSTDIR\bin\launch-gpa.exe" \ + "" "" "" SW_SHOWNORMAL "" $(DESC_Menu_gpa) + no_gpa_menu: + + + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\GnuPG Manual.lnk" \ + "$INSTDIR\share\gnupg\gnupg.html" \ + "" "" "" SW_SHOWNORMAL "" $(DESC_Menu_gnupg_manual) + + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\GnuPG README.lnk" \ + "$INSTDIR\README.txt" \ + "" "" "" SW_SHOWNORMAL "" $(DESC_Menu_gnupg_readme) + +!insertmacro MUI_STARTMENU_WRITE_END + + + +no_start_menu: + + + # Check if the desktop entries where requested. + !insertmacro MUI_INSTALLOPTIONS_READ $R0 "${W32_SRCDIR}/inst-options.ini" \ + "Field 3" "State" + IntCmp $R0 0 no_desktop + + SectionGetFlags ${SEC_gpa} $R0 + IntOp $R0 $R0 & ${SF_SELECTED} + IntCmp $R0 ${SF_SELECTED} 0 no_gpa_desktop + CreateShortCut "$DESKTOP\GPA.lnk" \ + "$INSTDIR\bin\launch-gpa.exe" \ + "" "" "" SW_SHOWNORMAL "" $(DESC_Menu_gpa) + no_gpa_desktop: + + + CreateShortCut "$DESKTOP\GPA Manual.lnk" \ + "$INSTDIR\share\gpa\gpa.html" \ + "" "" "" SW_SHOWNORMAL "" $(DESC_Menu_gpa_manual) + +no_desktop: + + + # Check if the quick launch bar entries where requested. + !insertmacro MUI_INSTALLOPTIONS_READ $R0 "${W32_SRCDIR}/inst-options.ini" \ + "Field 4" "State" + IntCmp $R0 0 no_quick_launch + StrCmp $QUICKLAUNCH $TEMP no_quick_launch + + SectionGetFlags ${SEC_gpa} $R0 + IntOp $R0 $R0 & ${SF_SELECTED} + IntCmp $R0 ${SF_SELECTED} 0 no_gpa_quicklaunch + CreateShortCut "$QUICKLAUNCH\GPA.lnk" \ + "$INSTDIR\bin\launch-gpa.exe" \ + "" "" "" SW_SHOWNORMAL "" $(DESC_Menu_gpa) +no_gpa_quicklaunch: + + +no_quick_launch: + + +!endif +SectionEnd +!endif + + +# +# Now for the generic parts to end the installation. +# +Var MYTMP + +# Last section is a hidden one. +Section + WriteUninstaller "$INSTDIR\gnupg-uninstall.exe" + + # Windows Add/Remove Programs support + StrCpy $MYTMP "Software\Microsoft\Windows\CurrentVersion\Uninstall\GnuPG" + WriteRegExpandStr HKLM $MYTMP "UninstallString" '"$INSTDIR\gnupg-uninstall.exe"' + WriteRegExpandStr HKLM $MYTMP "InstallLocation" "$INSTDIR" + WriteRegStr HKLM $MYTMP "DisplayName" "${PRETTY_PACKAGE}" +!ifdef WITH_GUI + WriteRegStr HKLM $MYTMP "DisplayIcon" "$INSTDIR\bin\gpa.exe,0" +!endif + WriteRegStr HKLM $MYTMP "DisplayVersion" "${VERSION}" + WriteRegStr HKLM $MYTMP "Publisher" "The GnuPG Project" + WriteRegStr HKLM $MYTMP "URLInfoAbout" "https://gnupg.org" + WriteRegDWORD HKLM $MYTMP "NoModify" "1" + WriteRegDWORD HKLM $MYTMP "NoRepair" "1" +SectionEnd + + +Section Uninstall + +!ifdef WITH_GUI +!ifdef HAVE_STARTMENU + # Make sure that the context of the automatic variables has been set to + # the "all users" shell folder. This guarantees that the menu gets written + # for all users. We have already checked that we are running as Admin; or + # we printed a warning that installation will not succeed. + SetShellVarContext all + + #--------------------------------------------------- + # Delete the menu entries and any empty parent menus + #--------------------------------------------------- + !insertmacro MUI_STARTMENU_GETFOLDER Application $MYTMP + Delete "$SMPROGRAMS\$MYTMP\GPA.lnk" + Delete "$SMPROGRAMS\$MYTMP\GnuPG Manual.lnk" + Delete "$SMPROGRAMS\$MYTMP\GnuPG README.lnk" + Delete "$SMPROGRAMS\$MYTMP\*.lnk" + StrCpy $MYTMP "$SMPROGRAMS\$MYTMP" + startMenuDeleteLoop: + ClearErrors + RMDir $MYTMP + GetFullPathName $MYTMP "$MYTMP\.." + IfErrors startMenuDeleteLoopDone + StrCmp $MYTMP $SMPROGRAMS startMenuDeleteLoopDone startMenuDeleteLoop + startMenuDeleteLoopDone: + + DeleteRegValue HKLM "Software\GNU\GnuPG" "Start Menu Folder" + + # Delete Desktop links. + Delete "$DESKTOP\GPA.lnk" + Delete "$DESKTOP\GnuPG Manual.lnk" + Delete "$DESKTOP\GnuPG README.lnk" + + # Delete Quick Launch Bar links. + StrCmp $QUICKLAUNCH $TEMP no_quick_launch_uninstall + Delete "$QUICKLAUNCH\GPA.lnk" +no_quick_launch_uninstall: + +!endif +!endif + + Delete "$INSTDIR\gnupg-uninstall.exe" + RMDir "$INSTDIR" + + # Clean the registry. + DeleteRegValue HKLM "Software\GnuPG" "Install Directory" + DeleteRegKey /ifempty HKLM "Software\GnuPG" + # Remove Windows Add/Remove Programs support. + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GnuPG" +SectionEnd diff --git a/build-aux/speedo/w32/pango.modules b/build-aux/speedo/w32/pango.modules new file mode 100755 index 0000000..75b2527 --- /dev/null +++ b/build-aux/speedo/w32/pango.modules @@ -0,0 +1,3 @@ +# Pango Modules file +# +"../lib/pango/1.6.0/modules/pango-basic-win32.dll" BasicScriptEngineWin32 PangoEngineShape PangoRenderWin32 common: diff --git a/build-aux/speedo/w32/pkg-copyright.txt b/build-aux/speedo/w32/pkg-copyright.txt new file mode 100644 index 0000000..a630c8e --- /dev/null +++ b/build-aux/speedo/w32/pkg-copyright.txt @@ -0,0 +1,181 @@ +Here is a list with collected copyright notices. For details see the +description of each individual package. [Compiled by wk 2016-06-17] + +GnuPG is + + Copyright (C) 1997-2016 Werner Koch + Copyright (C) 1994-2016 Free Software Foundation, Inc. + Copyright (C) 2003-2016 g10 Code GmbH + Copyright (C) 2002 Klarälvdalens Datakonsult AB + Copyright (C) 1995-1997, 2000-2007 Ulrich Drepper + Copyright (C) 1994 X Consortium + Copyright (C) 1998 by The Internet Society. + Copyright (C) 1998-2004 The OpenLDAP Foundation + Copyright (C) 1998-2004 Kurt D. Zeilenga. + Copyright (C) 1998-2004 Net Boolean Incorporated. + Copyright (C) 2001-2004 IBM Corporation. + Copyright (C) 1999-2003 Howard Y.H. Chu. + Copyright (C) 1999-2003 Symas Corporation. + Copyright (C) 1998-2003 Hallvard B. Furuseth. + Copyright (C) 1992-1996 Regents of the University of Michigan. + Copyright (C) 2000 Dimitrios Souflis + Copyright (C) 2008,2009,2010,2012-2016 William Ahern + + 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 3 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 + +GPGME is + + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 g10 Code GmbH + + GPGME is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + GPGME is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . + +LIBGPG-ERROR is + + Copyright (C) 2003, 2004 g10 Code GmbH + + libgpg-error 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. + + libgpg-error is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . + + +NSIS is + + Copyright (C) 1999-2005 Nullsoft, Inc. + + This license applies to everything in the NSIS package, except where + otherwise noted. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The user interface used with the installer is + + Copyright (C) 2002-2005 Joost Verburg + + [It is distributed along with NSIS and the same conditions as stated + above apply] + + +ADNS is + + Copyright (C) 1997-2000,2003,2006 Ian Jackson + Copyright (C) 1999-2000,2003,2006 Tony Finch + Copyright (C) 1991 Massachusetts Institute of Technology + + +TinySCHEME is part of the GnuPG package and is + + Copyright (c) 2000, Dimitrios Souflis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of Dimitrios Souflis nor the names of the + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +libdns is part of the GnuPG package and is + + Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + +SQLite has + + been put into the public-domain by its author D. Richard Hipp: + The author disclaims copyright to this source code. In place of + a legal notice, here is a blessing: + + May you do good and not evil. + May you find forgiveness for yourself and forgive others. + May you share freely, never taking more than you give. diff --git a/build-aux/speedo/zlib.pc b/build-aux/speedo/zlib.pc new file mode 100644 index 0000000..b758050 --- /dev/null +++ b/build-aux/speedo/zlib.pc @@ -0,0 +1,10 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: zlib +Description: zlib compression library +Version: 1.2.5 +Libs: -L${libdir} -lz +Cflags: -I${includedir} diff --git a/build-aux/texinfo.tex b/build-aux/texinfo.tex new file mode 100644 index 0000000..a181898 --- /dev/null +++ b/build-aux/texinfo.tex @@ -0,0 +1,8638 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2007-05-03.09} +% +% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +% 2007 Free Software Foundation, Inc. +% +% This texinfo.tex file 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 3, or (at +% your option) any later version. +% +% This texinfo.tex file 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 texinfo.tex file; see the file COPYING. If not, +% see . +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. (This has been our intent since Texinfo was invented.) +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or +% ftp://tug.org/tex/texinfo.tex +% (and all CTAN mirrors, see http://www.ctan.org). +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include including a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + + +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexstar=\* +\let\ptext=\t + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Since the category of space is not known, we have to be careful. +\chardef\spacecat = 10 +\def\spaceisspace{\catcode`\ =\spacecat} + +% sometimes characters are active, so we need control sequences. +\chardef\colonChar = `\: +\chardef\commaChar = `\, +\chardef\dashChar = `\- +\chardef\dotChar = `\. +\chardef\exclamChar= `\! +\chardef\lquoteChar= `\` +\chardef\questChar = `\? +\chardef\rquoteChar= `\' +\chardef\semiChar = `\; +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\undefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% For @cropmarks command. +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + % We don't want .vr (or whatever) entries like this: + % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} + % "\acronym" won't work when it's read back in; + % it needs to be + % {\code {{\tt \backslashcurfont }acronym} + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \indexdummies + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurence of `\^^M' or `\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarily, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +% +% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my +% favourite TeX trick. --kasal, 16nov03 + +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as enviroments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At runtime, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Evironment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + out of any environment% + \else + in environment \expandafter\string#1% + \fi +} + +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv +% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03 + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt\char64}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. +\def\mylbrace {{\tt\char123}} +\def\myrbrace {{\tt\char125}} +\let\{=\mylbrace +\let\}=\myrbrace +\begingroup + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux/toc files. + \catcode`\{ = \other \catcode`\} = \other + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \c +\let\dotaccent = \. +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \t +\let\ubaraccent = \b +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ptexi + \else\ifx\temp\jmacro \j + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}% + \kern-.15em + \TeX +} + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on/off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox + \prevdepth = \dimen1 + \checkinserts +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +% Old definition--didn't work. +%\parseargdef\need{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak +%\prevdepth=-1000pt +%}} + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @include file insert text of that file as input. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable + \def\temp{\input #1 }% + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} + +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\next\centerH + \else + \let\next\centerV + \fi + \next{\hfil \ignorespaces#1\unskip \hfil}% +} +\def\centerH#1{% + {% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break + }% +} +\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}} + +% @sp n outputs n lines of vertical space + +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} + +\let\c=\comment + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent{% + \restorefirstparagraphindent + \indent + }% + \gdef\noindent{% + \restorefirstparagraphindent + \noindent + }% + \global\everypar = {% + \kern -\parindent + \restorefirstparagraphindent + }% +} + +\gdef\restorefirstparagraphindent{% + \global \let \indent = \ptexindent + \global \let \noindent = \ptexnoindent + \global \everypar = {}% +} + + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a \ character. +% FYI, plain.tex uses \\ as a temporary control sequence (why?), but +% this is not advertised and we don't care. Texinfo does not +% otherwise define @\. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + $\finishmath +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + } +} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{$\ptexbullet$} +\def\minus{$-$} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @comma{} is so commas can be inserted into text without messing up +% Texinfo's parsing. +% +\let\comma = , + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi % \openindices needs to do some work in any case. + \openindices + \let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + \openin 1 texinfo.cnf + \ifeof 1 \else \input texinfo.cnf \fi + \closein 1 + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as \undefined, +% borrowed from ifpdf.sty. +\ifx\pdfoutput\undefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html +% (and related messages, the final outcome is that it is up to the TeX +% user to double the backslashes and otherwise make the string valid, so +% that's what we do). + +% double active backslashes. +% +{\catcode`\@=0 \catcode`\\=\active + @gdef@activebackslashdouble{% + @catcode`@\=@active + @let\=@doublebackslash} +} + +% To handle parens, we must adopt a different approach, since parens are +% not active characters. hyperref.dtx (which has the same problem as +% us) handles it with this amazing macro to replace tokens, with minor +% changes for Texinfo. It is included here under the GPL by permission +% from the author, Heiko Oberdiek. +% +% #1 is the tokens to replace. +% #2 is the replacement. +% #3 is the control sequence with the string. +% +\def\HyPsdSubst#1#2#3{% + \def\HyPsdReplace##1#1##2\END{% + ##1% + \ifx\\##2\\% + \else + #2% + \HyReturnAfterFi{% + \HyPsdReplace##2\END + }% + \fi + }% + \xdef#3{\expandafter\HyPsdReplace#3#1\END}% +} +\long\def\HyReturnAfterFi#1\fi{\fi#1} + +% #1 is a control sequence in which to do the replacements. +\def\backslashparens#1{% + \xdef#1{#1}% redefine it as its expansion; the definition is simply + % \lastnode when called from \setref -> \pdfmkdest. + \HyPsdSubst{(}{\realbackslash(}{#1}% + \HyPsdSubst{)}{\realbackslash)}{#1}% +} + +\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images +with PDF output, and none of those formats could be found. (.eps cannot +be supported due to the design of the PDF format; use regular TeX (DVI +output) for that.)} + +\ifpdf + \input pdfcolor + \pdfcatalog{/PageMode /UseOutlines} + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\imagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\imageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % pdftex (and the PDF format) support .png, .jpg, .pdf (among + % others). Let's try in that order. + \let\pdfimgext=\empty + \begingroup + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \openin 1 #1.pdf \ifeof 1 + \errhelp = \nopdfimagehelp + \errmessage{Could not find image file #1 for pdf}% + \else \gdef\pdfimgext{pdf}% + \fi + \else \gdef\pdfimgext{JPG}% + \fi + \else \gdef\pdfimgext{jpeg}% + \fi + \else \gdef\pdfimgext{jpg}% + \fi + \else \gdef\pdfimgext{png}% + \fi + \closein 1 + \endgroup + % + % without \immediate, pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \imagewidth \fi + \ifdim \wd2 >0pt height \imageheight \fi + \ifnum\pdftexversion<13 + #1.\pdfimgext + \else + {#1.\pdfimgext}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + % + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \turnoffactive + \activebackslashdouble + \makevalueexpandable + \def\pdfdestname{#1}% + \backslashparens\pdfdestname + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + }} + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1} + % + % by default, use a color that is dark enough to print on paper as + % nearly black, but still distinguishable for online viewing. + % (Defined in pdfcolor.tex.) + \let\urlcolor = \BrickRed + \let\linkcolor = \BrickRed + \def\endlink{\Black\pdfendlink} + % + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \def\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty + \def\pdfoutlinedest{#4}% + \else + % Doubled backslashes in the name. + {\activebackslashdouble \xdef\pdfoutlinedest{#3}% + \backslashparens\pdfoutlinedest}% + \fi + % + % Also double the backslashes in the display string. + {\activebackslashdouble \xdef\pdfoutlinetext{#1}% + \backslashparens\pdfoutlinetext}% + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Thanh's hack / proper braces in bookmarks + \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace + \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace + % + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % xx to do this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Right + % now, I guess we'll just let the pdf reader have its way. + \indexnofonts + \setupdatafile + \catcode`\\=\active \otherbackslash + \input \jobname.toc + \endgroup + } + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \ifx\p\space\else\addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \fi + \nextsp} + \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax} + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + \leavevmode\urlcolor + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \linkcolor #1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\linkcolor = \relax + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + + +\message{fonts,} + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% Default leading. +\newdimen\textleading \textleading = 13.2pt + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% +% PDF CMaps. See also LaTeX's t1.cmap. +% +% \cmapOT1 +\ifpdf + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1-0) +%%Title: (TeX-OT1-0 TeX OT1 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1) +/Supplement 0 +>> def +/CMapName /TeX-OT1-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<23> <26> <0023> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +40 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1IT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1IT-0) +%%Title: (TeX-OT1IT-0 TeX OT1IT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1IT) +/Supplement 0 +>> def +/CMapName /TeX-OT1IT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<25> <26> <0025> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +42 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<23> <0023> +<24> <00A3> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1IT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1TT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1TT-0) +%%Title: (TeX-OT1TT-0 TeX OT1TT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1TT) +/Supplement 0 +>> def +/CMapName /TeX-OT1TT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +5 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<21> <26> <0021> +<28> <5F> <0028> +<61> <7E> <0061> +endbfrange +32 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <2191> +<0C> <2193> +<0D> <0027> +<0E> <00A1> +<0F> <00BF> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<20> <2423> +<27> <2019> +<60> <2018> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1TT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +\else + \expandafter\let\csname cmapOT1\endcsname\gobble + \expandafter\let\csname cmapOT1IT\endcsname\gobble + \expandafter\let\csname cmapOT1TT\endcsname\gobble +\fi + + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor, #5 is the CMap +% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass +% empty to omit). +\def\setfont#1#2#3#4#5{% + \font#1=\fontprefix#2#3 scaled #4 + \csname cmap#5\endcsname#1% +} +% This is what gets called when #5 of \setfont is empty. +\let\cmap\gobble + + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\undefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. This is the default in +% Texinfo. +% +\def\definetextfontsizexi{% +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1}{OT1} +\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\authorrm{\secrm} +\def\authortt{\sectt} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} +\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep3}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} +\setfont\chapsf\sfbshape{17}{1000}{OT1} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3}{OT1} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1}{OT1} +\setfont\secit\itbshape{10}{\magstep2}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep2}{OT1} +\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\secsf\sfbshape{12}{\magstep1}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2}{OT1} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} +\setfont\ssecit\itbshape{10}{1315}{OT1IT} +\setfont\ssecsl\slbshape{10}{1315}{OT1} +\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} +\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315}{OT1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 + +% Reduced fonts for @acro in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000}{OT1} +\setfont\reducedtt\ttshape{10}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{1000}{OT1} +\setfont\reducedit\itshape{10}{1000}{OT1IT} +\setfont\reducedsl\slshape{10}{1000}{OT1} +\setfont\reducedsf\sfshape{10}{1000}{OT1} +\setfont\reducedsc\scshape{10}{1000}{OT1} +\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 + +% reset the current fonts +\textfonts +\rm +} % end of 11pt text font size definitions + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf}{OT1} +\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\authorrm{\secrm} +\def\authortt{\sectt} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} +\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep2}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2}{OT1} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000}{OT1} +\setfont\secit\itbshape{10}{\magstep1}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep1}{OT1} +\setfont\sectt\ttbshape{12}{1000}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} +\setfont\secsf\sfbshape{12}{1000}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1}{OT1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000}{OT1} +\setfont\ssecit\itbshape{10}{1000}{OT1IT} +\setfont\ssecsl\slbshape{10}{1000}{OT1} +\setfont\ssectt\ttbshape{10}{1000}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} +\setfont\ssecsf\sfbshape{10}{1000}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000}{OT1} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 + +% Reduced fonts for @acro in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000}{OT1} +\setfont\reducedtt\ttshape{9}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{900}{OT1} +\setfont\reducedit\itshape{9}{1000}{OT1IT} +\setfont\reducedsl\slshape{9}{1000}{OT1} +\setfont\reducedsf\sfshape{9}{1000}{OT1} +\setfont\reducedsc\scshape{10}{900}{OT1} +\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 + +% reduce space between paragraphs +\divide\parskip by 2 + +% reset the current fonts +\textfonts +\rm +} % end of 10pt text font size definitions + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xword{10} +\def\xiword{11} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + \wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts except +% in the main text, we don't bother to reset \scriptfont and +% \scriptscriptfont (which would also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf +} + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used in +% the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\curfontsize{text}% + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \def\curfontsize{title}% + \def\lsize{chap}\def\lllsize{subsec}% + \resetmathfonts \setleading{25pt}} +\def\titlefont#1{{\titlefonts\rm #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \let\tenttsl=\chapttsl + \def\curfontsize{chap}% + \def\lsize{sec}\def\lllsize{text}% + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\curfontsize{sec}% + \def\lsize{subsec}\def\lllsize{reduced}% + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\curfontsize{ssec}% + \def\lsize{text}\def\lllsize{small}% + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\curfontsize{reduced}% + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \def\curfontsize{small}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\curfontsize{smaller}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% +% I wish the USA used A4 paper. +% --karl, 24jan03. + + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + +% Define these so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000}{OT1} +\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000}{OT1} +\setfont\shortconttt\ttshape{12}{1000}{OT1TT} + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else + \ptexslash\fi\fi\fi} +\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx} +\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally uses \ttsl. +% @var is set to this for defun arguments. +\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\let\var=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% @b, explicit bold. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m + \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +\def\t#1{% + {\tt \rawbackslash \plainfrenchspacing #1}% + \null +} +\def\samp#1{`\tclose{#1}'\null} +\setfont\keyrm\rmshape{8}{1000}{OT1} +\font\keysy=cmsy9 +\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% + \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% + \vbox{\hrule\kern-0.4pt + \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% + \kern-0.4pt\hrule}% + \kern-.06em\raise0.4pt\hbox{\angleright}}}} +\def\key #1{{\nohyphenation \uppercase{#1}}\null} +% The old definition, with no lozenge: +%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \plainfrenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + % + \global\def\code{\begingroup + \catcode\rquoteChar=\active \catcode\lquoteChar=\active + \let'\codequoteright \let`\codequoteleft + % + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\realdash + \let_\realunder + \fi + \codex + } +} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} +\def\codex #1{\tclose{#1}\endgroup} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is undesirable in +% some manuals, especially if they don't have long identifiers in +% general. @allowcodebreaks provides a way to control this. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg'}% + \fi\fi +} + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle option `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct.' +\kbdinputstyle distinct + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\kbdfont\look}}\fi +\else{\tclose{\kbdfont\look}}\fi} + +% For @indicateurl, @env, @command quotes seem unnecessary, so use \code. +\let\indicateurl=\code +\let\env=\code +\let\command=\code + +% @uref (abbreviation for `urlref') takes an optional (comma-separated) +% second argument specifying the text to display and an optional third +% arg as text to display instead of (rather than in addition to) the url +% itself. First (mandatory) arg is the url. Perhaps eventually put in +% a hypertex \special here. +% +\def\uref#1{\douref #1,,,\finish} +\def\douref#1,#2,#3,#4\finish{\begingroup + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \code{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{{\it\$}} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\undefined +\def\Orb{\mathhexbox20D} +\fi + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines + \let\tt=\authortt} + +\parseargdef\title{% + \checkenv\titlepage + \leftline{\titlefonts\rm #1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\authorfont \leftline{#1}}% + \fi +} + + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make TeX use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{% +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\undefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + \def\itemcontents{#1}% + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + \vadjust{\penalty 1200}}% not good to break after first line of item. + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. +% Assignments have to be global since we are inside the implicit group +% of an alignment entry. Note that \everycr resets \everytab. +\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}% +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we encounter the problem it was intended to solve again. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% + \global\colcount=0 % Reset the column counter. + % Check for saved footnotes, etc. + \checkinserts + % Keeps underfull box messages off when table breaks over pages. + %\filbreak + % Maybe so, but it also creates really weird page breaks when the + % table breaks over pages. Wouldn't \vfil be better? Wait until the + % problem manifests itself, so it can be fixed for real --karl. + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +%% Test to see if parskip is larger than space between lines of +%% table. If not, do nothing. +%% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\- = \active \catcode`\_ = \active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\realdash \let_\normalunderscore + } +} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get special treatment of `@end ifset,' call \makeond and the redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \undefined + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname\donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +% Take care of Texinfo commands that can appear in an index entry. +% Since there are some commands we want to expand, and others we don't, +% we have to laboriously prevent expansion for those that we don't. +% +\def\indexdummies{% + \escapechar = `\\ % use backslash in output files. + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % + % Need these in case \tex is in effect and \{ is a \delimiter again. + % But can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. + \let\{ = \mylbrace + \let\} = \myrbrace + % + % I don't entirely understand this, but when an index entry is + % generated from a macro call, the \endinput which \scanmacro inserts + % causes processing to be prematurely terminated. This is, + % apparently, because \indexsorttmp is fully expanded, and \endinput + % is an expandable command. The redefinition below makes \endinput + % disappear altogether for that purpose -- although logging shows that + % processing continues to some further point. On the other hand, it + % seems \endinput does not hurt in the printed index arg, since that + % is still getting written without apparent harm. + % + % Sample source (mac-idx3.tex, reported by Graham Percival to + % help-texinfo, 22may06): + % @macro funindex {WORD} + % @findex xyz + % @end macro + % ... + % @funindex commtest + % + % The above is not enough to reproduce the bug, but it gives the flavor. + % + % Sample whatsit resulting: + % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} + % + % So: + \let\endinput = \empty + % + % Do the redefinitions. + \commondummies +} + +% For the aux and toc files, @ is the escape character. So we want to +% redefine everything using @ as the escape character (instead of +% \realbackslash, still used for index files). When everything uses @, +% this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % Do the redefinitions. + \commondummies + \otherbackslash +} + +% Called from \indexdummies and \atdummies. +% +\def\commondummies{% + % + % \definedummyword defines \#1 as \string\#1\space, thus effectively + % preventing its expansion. This is used only for control% words, + % not control letters, because the \space would be incorrect for + % control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword ##1{\def##1{\string##1\space}}% + \def\definedummyletter##1{\def##1{\string##1}}% + \let\definedummyaccent\definedummyletter + % + \commondummiesnofonts + % + \definedummyletter\_% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\L + \definedummyword\OE + \definedummyword\O + \definedummyword\aa + \definedummyword\ae + \definedummyword\l + \definedummyword\oe + \definedummyword\o + \definedummyword\ss + \definedummyword\exclamdown + \definedummyword\questiondown + \definedummyword\ordf + \definedummyword\ordm + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\expansion + \definedummyword\minus + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\result + \definedummyword\textdegree + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + % + \normalturnoffactive + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable +} + +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter\!% + \definedummyaccent\"% + \definedummyaccent\'% + \definedummyletter\*% + \definedummyaccent\,% + \definedummyletter\.% + \definedummyletter\/% + \definedummyletter\:% + \definedummyaccent\=% + \definedummyletter\?% + \definedummyaccent\^% + \definedummyaccent\`% + \definedummyaccent\~% + \definedummyword\u + \definedummyword\v + \definedummyword\H + \definedummyword\dotaccent + \definedummyword\ringaccent + \definedummyword\tieaccent + \definedummyword\ubaraccent + \definedummyword\udotaccent + \definedummyword\dotless + % + % Texinfo font commands. + \definedummyword\b + \definedummyword\i + \definedummyword\r + \definedummyword\sc + \definedummyword\t + % + % Commands that take arguments. + \definedummyword\acronym + \definedummyword\cite + \definedummyword\code + \definedummyword\command + \definedummyword\dfn + \definedummyword\emph + \definedummyword\env + \definedummyword\file + \definedummyword\kbd + \definedummyword\key + \definedummyword\math + \definedummyword\option + \definedummyword\pxref + \definedummyword\ref + \definedummyword\samp + \definedummyword\strong + \definedummyword\tie + \definedummyword\uref + \definedummyword\url + \definedummyword\var + \definedummyword\verb + \definedummyword\w + \definedummyword\xref +} + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\definedummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\definedummyletter##1{\let##1\empty}% + % Hopefully, all control words can become @asis. + \let\definedummyword\definedummyaccent + % + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + % how to handle braces? + \def\_{\normalunderscore}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\aa{aa}% + \def\ae{ae}% + \def\l{l}% + \def\oe{oe}% + \def\o{o}% + \def\ss{ss}% + \def\exclamdown{!}% + \def\questiondown{?}% + \def\ordf{a}% + \def\ordm{o}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\bullet{bullet}% + \def\comma{,}% + \def\copyright{copyright}% + \def\registeredsymbol{R}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\euro{euro}% + \def\expansion{==>}% + \def\minus{-}% + \def\pounds{pounds}% + \def\point{.}% + \def\print{-|}% + \def\result{=>}% + \def\textdegree{degrees}% + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist +} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% Most index entries go through here, but \dosubind is the general case. +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). +% +\def\dosubind#1#2#3{% + \iflinks + {% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \safewhatsit\dosubindwrite + }% + \fi +} + +% Write the entry in \toks0 to the index file: +% +\def\dosubindwrite{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% + \fi + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % Process the index entry with all font commands turned off, to + % get the string to sort by. + {\indexnofonts + \edef\temp{\the\toks0}% need full expansion + \xdef\indexsorttmp{\temp}% + }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} + +% Take care of unwanted page breaks/skips around a whatsit: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write or \pdfdest will make \lastskip zero. The result is that +% sequences like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +\newskip\whatsitskip +\newcount\whatsitpenalty +% +% ..., ready, GO: +% +\def\safewhatsit#1{% +\ifhmode + #1% +\else + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \whatsitskip = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \whatsitpenalty = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\skip0 glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\whatsitskip + \fi + % + #1% + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\whatsitskip + \fi +\fi +} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \plainfrenchspacing + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\backslashcurfont}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \nobreak + \vskip 0pt plus 3\baselineskip + \penalty 0 + \vskip 0pt plus -3\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip +}} + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +% A straightforward implementation would start like this: +% \def\entry#1#2{... +% But this frozes the catcodes in the argument, and can cause problems to +% @code, which sets - active. This problem was fixed by a kludge--- +% ``-'' was active throughout whole index, but this isn't really right. +% +% The right solution is to prevent \entry from swallowing the whole text. +% --kasal, 21nov03 +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus1pt + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\doentry{% + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. +} +\def\finishentry#1{% + % #1 is the page number. + % + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#1}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd + \ % + \else + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#1.% + \ \the\toksA + \else + \ #1% + \fi + \fi + \par + \endgroup +} + +% Like plain.tex's \dotfill, except uses up at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +% +% All done with double columns. +\def\enddoublecolumns{% + % The following penalty ensures that the page builder is exercised + % _before_ we change the output routine. This is necessary in the + % following situation: + % + % The last section of the index consists only of a single entry. + % Before this section, \pagetotal is less than \pagegoal, so no + % break occurs before the last section starts. However, the last + % section, consisting of \initial and the single \entry, does not + % fit on the page and has to be broken off. Without the following + % penalty the page builder will not be exercised until \eject + % below, and by that time we'll already have changed the output + % routine to the \balancecolumns version, so the next-to-last + % double-column page will be processed with \balancecolumns, which + % is wrong: The two columns will go to the main vertical list, with + % the broken-off section in the recent contributions. As soon as + % the output routine finishes, TeX starts reconsidering the page + % break. The two columns and the broken-off section both fit on the + % page, because the two columns now take up only half of the page + % goal. When TeX sees \eject from below which follows the final + % section, it invokes the new output routine that we've set after + % \balancecolumns below; \onepageout will try to fit the two columns + % and the final section into the vbox of \pageheight (see + % \pagebody), causing an overfull box. + % + % Note that glue won't work here, because glue does not exercise the + % page builder, unlike penalties (see The TeXbook, pp. 280-281). + \penalty0 + % + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +% +% Called at the end of the double column material. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% \unnumberedno is an oxymoron, of course. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise. +% However, they are not reliable, because we don't use marks. +\def\thischapter{} +\def\thissection{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achive this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unmlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unmlevel + \chardef\unmlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unmlevel + \def\headtype{U}% + \else + \chardef\unmlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + \message{\putwordChapter\space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + \def\appendixnum{\putwordAppendix\space \appendixletter}% + \message{\appendixnum}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the to achieve this: TeX expands \the only once, + % simply yielding the contents of . (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + % Well, we could do the following in a group, but that would break + % an assumption that \chapmacro is called at the outermost level. + % Thus we are safer this way: --kasal, 24feb04 + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} + +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% + \bigskip \par\penalty 200\relax + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip\chapheadingskip + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yomitfromtockeyword{Yomitfromtoc} +\def\Yappendixkeyword{Yappendix} +% +\def\chapmacro#1#2#3{% + \pchapsepmacro + {% + \chapfonts \rm + % + % Have to define \thissection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\thissection{#1}% + \gdef\thischaptername{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \gdef\thischapternum{}% + \gdef\thischapter{#1}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \gdef\thischapternum{}% + \gdef\thischapter{}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \xdef\thischapternum{\appendixletter}% + % We don't substitute the actual chapter name into \thischapter + % because we don't want its macros evaluated now. And we don't + % use \thissection because that changes with each section. + % + \xdef\thischapter{\putwordAppendix{} \appendixletter: + \noexpand\thischaptername}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \xdef\thischapternum{\the\chapno}% + \xdef\thischapter{\putwordChapter{} \the\chapno: + \noexpand\thischaptername}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} +% +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\nobreak +} +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rm #1}\hfill}}\bigskip \par\nobreak +} +\def\CHAPFopen{% + \global\let\chapmacro=\chfopen + \global\let\centerchapmacro=\centerchfopen} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is +% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the +% section number. +% +\def\sectionheading#1#2#3#4{% + {% + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rm + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Only insert the space after the number if we have a section number. + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\thissection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \thissection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\thissection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\thissection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) + \vskip-\parskip + % + % This is purely so the last item on the list is a known \penalty > + % 10000. This is so \startdefun can avoid allowing breakpoints after + % section headings. Otherwise, it would insert a valid breakpoint between: + % + % @section sec-whatever + % @deffn def-whatever + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \jobname.toc +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \def\thischapter{}% + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi +} + + +% Normal (long) toc. +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \jobname.toc + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \jobname.toc + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +% +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf error\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\envdef\tex{% + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \escapechar=`\\ + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\*=\ptexstar + \let\t=\ptext + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing = t% + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of \def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + % end paragraph for sake of leading, in case document has no blank + % line. This is redundant with what happens in \aboveenvbreak, but + % we need to do it before changing the fonts, and it's inconvenient + % to change the fonts afterward. + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it by one command: +\def\makedispenv #1#2{ + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2} + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2} + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two synonyms: +\def\maketwodispenvs #1#2#3{ + \makedispenv{#1}{#3} + \makedispenv{#2}{#3} +} + +% @lisp: indented, narrowed, typewriter font; @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvs {lisp}{example}{% + \nonfillstart + \tt\quoteexpand + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenv {display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenv{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\envdef\quotation{% + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \parindent=0pt + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\undefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + + +% LaTeX-like @verbatim...@end verbatim and @verb{...} +% If we want to allow any as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% [Knuth] pp. 380,381,391 +% Disable Spanish ligatures ?` and !` of \tt font +\begingroup + \catcode`\`=\active\gdef`{\relax\lq} +\endgroup +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \catcode`\`=\active + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +\def\starttabbox{\setbox0=\hbox\bgroup} + +% Allow an option to not replace quotes with a regular directed right +% quote/apostrophe (char 0x27), but instead use the undirected quote +% from cmtt (char 0x0d). The undirected quote is ugly, so don't make it +% the default, but it works for pasting with more pdf viewers (at least +% evince), the lilypond developers report. xpdf does work with the +% regular 0x27. +% +\def\codequoteright{% + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else + \char'15 + \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + `% + \else + \char'22 + \fi +} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen0=\wd0 % the width so far, or since the previous tab + \divide\dimen0 by\tabw + \multiply\dimen0 by\tabw % compute previous multiple of \tabw + \advance\dimen0 by\tabw % advance to next multiple of \tabw + \wd0=\dimen0 \box0 \starttabbox + }% + } + \catcode`\'=\active + \gdef\rquoteexpand{\catcode\rquoteChar=\active \def'{\codequoteright}}% + % + \catcode`\`=\active + \gdef\lquoteexpand{\catcode\lquoteChar=\active \def`{\codequoteleft}}% + % + \gdef\quoteexpand{\rquoteexpand \lquoteexpand}% +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + % Easiest (and conventionally used) font for verbatim + \tt + \def\par{\leavevmode\egroup\box0\endgraf}% + \catcode`\`=\active + \tabexpand + \quoteexpand + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'#1'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. +\endgroup +% +\envdef\verbatim{% + \setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + \input #1 + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is very desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt +\newcount\defunpenalty + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \defunpenalty=10003 % Will keep this @deffn together with the + % following @def command, see below. + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \printdefunline, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + % As a minor refinement, we avoid "club" headers by signalling + % with penalty of 10003 after the very first @deffn in the + % sequence (see above), and penalty of 10002 after any following + % @def command. + \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil + \endgraf + \nobreak\vskip -\parskip + \penalty\defunpenalty % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remainnig is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +%%% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +%%% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +%%% Type: +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % How we'll format the type name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % (plain.tex says that \dimen1 should be used only as global.) + \parshape 2 0in \dimen0 \defargsindent \dimen2 + % + % Put the type name to the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% return value type + \ifx\temp\empty\else \tclose{\temp} \fi + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \df \sl \hyphenchar\font=0 + % + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. Let's try @var for that. + \let\var=\ttslanted + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +\def\badparencount{% + \errmessage{Unbalanced parentheses in @def}% + \global\parencount=0 +} +\def\badbrackcount{% + \errmessage{Unbalanced square braces in @def}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\undefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\def\scanmacro#1{% + \begingroup + \newlinechar`\^^M + \let\xeatspaces\eatspaces + % Undo catcode changes of \startcontents and \doprintindex + % When called from @insertcopying or (short)caption, we need active + % backslash to get it printed correctly. Previously, we had + % \catcode`\\=\other instead. We'll see whether a problem appears + % with macro expansion. --kasal, 19aug04 + \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ + % ... and \example + \spaceisspace + % + % Append \endinput to make sure that TeX does not see the ending newline. + % I've verified that it is necessary both for e-TeX and for ordinary TeX + % --kasal, 29nov03 + \scantokens{#1\endinput}% + \endgroup +} + +\def\scanexp#1{% + \edef\temp{\noexpand\scanmacro{#1}}% + \temp +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \definedummyword\macro1\definedummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\definedummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \. + +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. + +\def\scanctxt{% + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\@=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other +} + +\def\scanargctxt{% + \scanctxt + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% + \scanctxt + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +\def\macroargctxt{% + \scanctxt + \catcode`\\=\other +} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. + +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0% + \else + \expandafter\parsemargdef \argl;% + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\definedummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\definedummyword \noexpand#1% + \fi +} + +% This makes use of the obscure feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname #1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.blah for each blah +% in the params list, to be ##N where N is the position in that list. +% That gets used by \mbodybackslash (above). + +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. + +\def\parsemargdef#1;{\paramno=0\def\paramlist{}% + \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1% + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) + +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% + +% This defines the macro itself. There are six cases: recursive and +% nonrecursive macros of zero, one, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \expandafter\noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \fi + \fi} + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg) +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Just make them active and then expand them all to nothing. +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} + +\let\nwnode=\node +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \thissection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \atdummies % preserve commands, but don't expand them + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\thissection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, during \shipout + }% + \fi +} + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + \def\printedmanual{\ignorespaces #5}% + \def\printedrefname{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual\unskip}% + \setbox0=\hbox{\printedrefname\unskip}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax + % Use the node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1 > 0pt + % It is in another manual, so we don't have it. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + \leavevmode + \getfilename{#4}% + {\indexnofonts + \turnoffactive + % See comments at \activebackslashdouble. + {\activebackslashdouble \xdef\pdfxrefdest{#1}% + \backslashparens\pdfxrefdest}% + % + \ifnum\filenamelength>0 + \startlink attr{/Border [0 0 0]}% + goto file{\the\filename.pdf} name{\pdfxrefdest}% + \else + \startlink attr{/Border [0 0 0]}% + goto name{\pdfmkpgn{\pdfxrefdest}}% + \fi + }% + \linkcolor + \fi + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". We distinguish them by the + % LABEL-title being set to a magic string. + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd0 = 0pt + \refx{#1-snt}{}% + \else + \printedrefname + \fi + % + % if the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd1 > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordsection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via a macro so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi + \fi + \endlink +\endgroup} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. +% +\def\refx#1#2{% + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. +% +\def\xrdef#1#2{% + {% The node name might contain 8-bit characters, which in our current + % implementation are changed to commands like @'e. Don't let these + % mess up the control sequence name. + \indexnofonts + \turnoffactive + \xdef\safexrefname{#1}% + }% + % + \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR\safexrefname\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 + {\safexrefname}}% + \fi +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. + {% + \count1=128 + \def\loop{% + \catcode\count1=\other + \advance\count1 by 1 + \ifnum \count1<256 \loop \fi + }% + }% + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\pagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarily, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. + +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\undefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing this stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \nobreak\bigskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \line\bgroup + \fi + % + % Output the image. + \ifpdf + \dopdfimage{#1}{#2}{#3}% + \else + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \fi + % + \ifimagevmode \egroup \bigbreak \fi % space after the image +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \thissection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\thissection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \atdummies + % + % since we read the caption text in the macro world, where ^^M + % is turned into a normal character, we have to scan it back, so + % we don't write the literal three characters "^^M" into the aux file. + \scanexp{% + \xdef\noexpand\gtemp{% + \ifx\thisshortcaption\empty + \thiscaption + \else + \thisshortcaption + \fi + }% + }% + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + % place the captured inserts + % + % BEWARE: when the floats start floating, we have to issue warning + % whenever an insert appears inside a float which could possibly + % float. --kasal, 26may04 + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \thissection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + + +\message{localization,} + +% @documentlanguage is usually given very early, just after +% @setfilename. If done too late, it may not override everything +% properly. Single argument is the language abbreviation. +% It would be nice if we could set up a hyphenation file here. +% +\parseargdef\documentlanguage{% + \tex % read txi-??.tex file in plain TeX. + % Read the file if it exists. + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \input txi-#1.tex + \fi + \closein 1 + \endgroup +} +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? In the current directory +should work if nowhere else does.} + +% Set the catcode of characters 128 through 255 to the specified number. +% +\def\setnonasciicharscatcode#1{% + \count255=128 + \loop\ifnum\count255<256 + \global\catcode\count255=#1 + \advance\count255 by 1 + \repeat +} + +% @documentencoding sets the definition of non-ASCII characters +% according to the specified encoding. +% +\parseargdef\documentencoding{% + % Encoding being declared for the document. + \def\declaredencoding{\csname #1.enc\endcsname}% + % + % Supported encodings: names converted to tokens in order to be able + % to compare them with \ifx. + \def\ascii{\csname US-ASCII.enc\endcsname}% + \def\latnine{\csname ISO-8859-15.enc\endcsname}% + \def\latone{\csname ISO-8859-1.enc\endcsname}% + \def\lattwo{\csname ISO-8859-2.enc\endcsname}% + \def\utfeight{\csname UTF-8.enc\endcsname}% + % + \ifx \declaredencoding \ascii + \asciichardefs + % + \else \ifx \declaredencoding \lattwo + \setnonasciicharscatcode\active + \lattwochardefs + % + \else \ifx \declaredencoding \latone + \setnonasciicharscatcode\active + \latonechardefs + % + \else \ifx \declaredencoding \latnine + \setnonasciicharscatcode\active + \latninechardefs + % + \else \ifx \declaredencoding \utfeight + \setnonasciicharscatcode\active + \utfeightchardefs + % + \else + \message{Unknown document encoding #1, ignoring.}% + % + \fi % utfeight + \fi % latnine + \fi % latone + \fi % lattwo + \fi % ascii +} + +% A message to be logged when using a character that isn't available +% the default font encoding (OT1). +% +\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} + +% Take account of \c (plain) vs. \, (Texinfo) difference. +\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} + +% First, make active non-ASCII characters in order for them to be +% correctly categorized when TeX reads the replacement text of +% macros containing the character definitions. +\setnonasciicharscatcode\active +% +% Latin1 (ISO-8859-1) character definitions. +\def\latonechardefs{% + \gdef^^a0{~} + \gdef^^a1{\exclamdown} + \gdef^^a2{\missingcharmsg{CENT SIGN}} + \gdef^^a3{{\pounds}} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\missingcharmsg{YEN SIGN}} + \gdef^^a6{\missingcharmsg{BROKEN BAR}} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\copyright} + \gdef^^aa{\ordf} + \gdef^^ab{\missingcharmsg{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}} + \gdef^^ac{$\lnot$} + \gdef^^ad{\-} + \gdef^^ae{\registeredsymbol} + \gdef^^af{\={}} + % + \gdef^^b0{\textdegree} + \gdef^^b1{$\pm$} + \gdef^^b2{$^2$} + \gdef^^b3{$^3$} + \gdef^^b4{\'{}} + \gdef^^b5{$\mu$} + \gdef^^b6{\P} + % + \gdef^^b7{$^.$} + \gdef^^b8{\cedilla\ } + \gdef^^b9{$^1$} + \gdef^^ba{\ordm} + % + \gdef^^bb{\missingcharmsg{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK}} + \gdef^^bc{$1\over4$} + \gdef^^bd{$1\over2$} + \gdef^^be{$3\over4$} + \gdef^^bf{\questiondown} + % + \gdef^^c0{\`A} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\~A} + \gdef^^c4{\"A} + \gdef^^c5{\ringaccent A} + \gdef^^c6{\AE} + \gdef^^c7{\cedilla C} + \gdef^^c8{\`E} + \gdef^^c9{\'E} + \gdef^^ca{\^E} + \gdef^^cb{\"E} + \gdef^^cc{\`I} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\"I} + % + \gdef^^d0{\missingcharmsg{LATIN CAPITAL LETTER ETH}} + \gdef^^d1{\~N} + \gdef^^d2{\`O} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\~O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\O} + \gdef^^d9{\`U} + \gdef^^da{\'U} + \gdef^^db{\^U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\missingcharmsg{LATIN CAPITAL LETTER THORN}} + \gdef^^df{\ss} + % + \gdef^^e0{\`a} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\~a} + \gdef^^e4{\"a} + \gdef^^e5{\ringaccent a} + \gdef^^e6{\ae} + \gdef^^e7{\cedilla c} + \gdef^^e8{\`e} + \gdef^^e9{\'e} + \gdef^^ea{\^e} + \gdef^^eb{\"e} + \gdef^^ec{\`{\dotless i}} + \gdef^^ed{\'{\dotless i}} + \gdef^^ee{\^{\dotless i}} + \gdef^^ef{\"{\dotless i}} + % + \gdef^^f0{\missingcharmsg{LATIN SMALL LETTER ETH}} + \gdef^^f1{\~n} + \gdef^^f2{\`o} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\~o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\o} + \gdef^^f9{\`u} + \gdef^^fa{\'u} + \gdef^^fb{\^u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\missingcharmsg{LATIN SMALL LETTER THORN}} + \gdef^^ff{\"y} +} + +% Latin9 (ISO-8859-15) encoding character definitions. +\def\latninechardefs{% + % Encoding is almost identical to Latin1. + \latonechardefs + % + \gdef^^a4{\euro} + \gdef^^a6{\v S} + \gdef^^a8{\v s} + \gdef^^b4{\v Z} + \gdef^^b8{\v z} + \gdef^^bc{\OE} + \gdef^^bd{\oe} + \gdef^^be{\"Y} +} + +% Latin2 (ISO-8859-2) character definitions. +\def\lattwochardefs{% + \gdef^^a0{~} + \gdef^^a1{\missingcharmsg{LATIN CAPITAL LETTER A WITH OGONEK}} + \gdef^^a2{\u{}} + \gdef^^a3{\L} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\v L} + \gdef^^a6{\'S} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\v S} + \gdef^^aa{\cedilla S} + \gdef^^ab{\v T} + \gdef^^ac{\'Z} + \gdef^^ad{\-} + \gdef^^ae{\v Z} + \gdef^^af{\dotaccent Z} + % + \gdef^^b0{\textdegree} + \gdef^^b1{\missingcharmsg{LATIN SMALL LETTER A WITH OGONEK}} + \gdef^^b2{\missingcharmsg{OGONEK}} + \gdef^^b3{\l} + \gdef^^b4{\'{}} + \gdef^^b5{\v l} + \gdef^^b6{\'s} + \gdef^^b7{\v{}} + \gdef^^b8{\cedilla\ } + \gdef^^b9{\v s} + \gdef^^ba{\cedilla s} + \gdef^^bb{\v t} + \gdef^^bc{\'z} + \gdef^^bd{\H{}} + \gdef^^be{\v z} + \gdef^^bf{\dotaccent z} + % + \gdef^^c0{\'R} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\u A} + \gdef^^c4{\"A} + \gdef^^c5{\'L} + \gdef^^c6{\'C} + \gdef^^c7{\cedilla C} + \gdef^^c8{\v C} + \gdef^^c9{\'E} + \gdef^^ca{\missingcharmsg{LATIN CAPITAL LETTER E WITH OGONEK}} + \gdef^^cb{\"E} + \gdef^^cc{\v E} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\v D} + % + \gdef^^d0{\missingcharmsg{LATIN CAPITAL LETTER D WITH STROKE}} + \gdef^^d1{\'N} + \gdef^^d2{\v N} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\H O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\v R} + \gdef^^d9{\ringaccent U} + \gdef^^da{\'U} + \gdef^^db{\H U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\cedilla T} + \gdef^^df{\ss} + % + \gdef^^e0{\'r} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\u a} + \gdef^^e4{\"a} + \gdef^^e5{\'l} + \gdef^^e6{\'c} + \gdef^^e7{\cedilla c} + \gdef^^e8{\v c} + \gdef^^e9{\'e} + \gdef^^ea{\missingcharmsg{LATIN SMALL LETTER E WITH OGONEK}} + \gdef^^eb{\"e} + \gdef^^ec{\v e} + \gdef^^ed{\'\i} + \gdef^^ee{\^\i} + \gdef^^ef{\v d} + % + \gdef^^f0{\missingcharmsg{LATIN SMALL LETTER D WITH STROKE}} + \gdef^^f1{\'n} + \gdef^^f2{\v n} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\H o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\v r} + \gdef^^f9{\ringaccent u} + \gdef^^fa{\'u} + \gdef^^fb{\H u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\cedilla t} + \gdef^^ff{\dotaccent{}} +} + +% UTF-8 character definitions. +% +% This code to support UTF-8 is based on LaTeX's utf8.def, with some +% changes for Texinfo conventions. It is included here under the GPL by +% permission from Frank Mittelbach and the LaTeX team. +% +\newcount\countUTFx +\newcount\countUTFy +\newcount\countUTFz + +\gdef\UTFviiiTwoOctets#1#2{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\endcsname} +% +\gdef\UTFviiiThreeOctets#1#2#3{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} +% +\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} + +\gdef\UTFviiiDefined#1{% + \ifx #1\relax + \message{\linenumber Unicode char \string #1 not defined for Texinfo}% + \else + \expandafter #1% + \fi +} + +\begingroup + \catcode`\~13 + \catcode`\"12 + + \def\UTFviiiLoop{% + \global\catcode\countUTFx\active + \uccode`\~\countUTFx + \uppercase\expandafter{\UTFviiiTmp}% + \advance\countUTFx by 1 + \ifnum\countUTFx < \countUTFy + \expandafter\UTFviiiLoop + \fi} + + \countUTFx = "C2 + \countUTFy = "E0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiTwoOctets\string~}} + \UTFviiiLoop + + \countUTFx = "E0 + \countUTFy = "F0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiThreeOctets\string~}} + \UTFviiiLoop + + \countUTFx = "F0 + \countUTFy = "F4 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiFourOctets\string~}} + \UTFviiiLoop +\endgroup + +\begingroup + \catcode`\"=12 + \catcode`\<=12 + \catcode`\.=12 + \catcode`\,=12 + \catcode`\;=12 + \catcode`\!=12 + \catcode`\~=13 + + \gdef\DeclareUnicodeCharacter#1#2{% + \countUTFz = "#1\relax + \wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% + \begingroup + \parseXMLCharref + \def\UTFviiiTwoOctets##1##2{% + \csname u8:##1\string ##2\endcsname}% + \def\UTFviiiThreeOctets##1##2##3{% + \csname u8:##1\string ##2\string ##3\endcsname}% + \def\UTFviiiFourOctets##1##2##3##4{% + \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter + \gdef\UTFviiiTmp{#2}% + \endgroup} + + \gdef\parseXMLCharref{% + \ifnum\countUTFz < "A0\relax + \errhelp = \EMsimple + \errmessage{Cannot define Unicode char value < 00A0}% + \else\ifnum\countUTFz < "800\relax + \parseUTFviiiA,% + \parseUTFviiiB C\UTFviiiTwoOctets.,% + \else\ifnum\countUTFz < "10000\relax + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% + \else + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiA!% + \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% + \fi\fi\fi + } + + \gdef\parseUTFviiiA#1{% + \countUTFx = \countUTFz + \divide\countUTFz by 64 + \countUTFy = \countUTFz + \multiply\countUTFz by 64 + \advance\countUTFx by -\countUTFz + \advance\countUTFx by 128 + \uccode `#1\countUTFx + \countUTFz = \countUTFy} + + \gdef\parseUTFviiiB#1#2#3#4{% + \advance\countUTFz by "#10\relax + \uccode `#3\countUTFz + \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} +\endgroup + +\def\utfeightchardefs{% + \DeclareUnicodeCharacter{00A0}{\tie} + \DeclareUnicodeCharacter{00A1}{\exclamdown} + \DeclareUnicodeCharacter{00A3}{\pounds} + \DeclareUnicodeCharacter{00A8}{\"{ }} + \DeclareUnicodeCharacter{00A9}{\copyright} + \DeclareUnicodeCharacter{00AA}{\ordf} + \DeclareUnicodeCharacter{00AD}{\-} + \DeclareUnicodeCharacter{00AE}{\registeredsymbol} + \DeclareUnicodeCharacter{00AF}{\={ }} + + \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} + \DeclareUnicodeCharacter{00B4}{\'{ }} + \DeclareUnicodeCharacter{00B8}{\cedilla{ }} + \DeclareUnicodeCharacter{00BA}{\ordm} + \DeclareUnicodeCharacter{00BF}{\questiondown} + + \DeclareUnicodeCharacter{00C0}{\`A} + \DeclareUnicodeCharacter{00C1}{\'A} + \DeclareUnicodeCharacter{00C2}{\^A} + \DeclareUnicodeCharacter{00C3}{\~A} + \DeclareUnicodeCharacter{00C4}{\"A} + \DeclareUnicodeCharacter{00C5}{\AA} + \DeclareUnicodeCharacter{00C6}{\AE} + \DeclareUnicodeCharacter{00C7}{\cedilla{C}} + \DeclareUnicodeCharacter{00C8}{\`E} + \DeclareUnicodeCharacter{00C9}{\'E} + \DeclareUnicodeCharacter{00CA}{\^E} + \DeclareUnicodeCharacter{00CB}{\"E} + \DeclareUnicodeCharacter{00CC}{\`I} + \DeclareUnicodeCharacter{00CD}{\'I} + \DeclareUnicodeCharacter{00CE}{\^I} + \DeclareUnicodeCharacter{00CF}{\"I} + + \DeclareUnicodeCharacter{00D1}{\~N} + \DeclareUnicodeCharacter{00D2}{\`O} + \DeclareUnicodeCharacter{00D3}{\'O} + \DeclareUnicodeCharacter{00D4}{\^O} + \DeclareUnicodeCharacter{00D5}{\~O} + \DeclareUnicodeCharacter{00D6}{\"O} + \DeclareUnicodeCharacter{00D8}{\O} + \DeclareUnicodeCharacter{00D9}{\`U} + \DeclareUnicodeCharacter{00DA}{\'U} + \DeclareUnicodeCharacter{00DB}{\^U} + \DeclareUnicodeCharacter{00DC}{\"U} + \DeclareUnicodeCharacter{00DD}{\'Y} + \DeclareUnicodeCharacter{00DF}{\ss} + + \DeclareUnicodeCharacter{00E0}{\`a} + \DeclareUnicodeCharacter{00E1}{\'a} + \DeclareUnicodeCharacter{00E2}{\^a} + \DeclareUnicodeCharacter{00E3}{\~a} + \DeclareUnicodeCharacter{00E4}{\"a} + \DeclareUnicodeCharacter{00E5}{\aa} + \DeclareUnicodeCharacter{00E6}{\ae} + \DeclareUnicodeCharacter{00E7}{\cedilla{c}} + \DeclareUnicodeCharacter{00E8}{\`e} + \DeclareUnicodeCharacter{00E9}{\'e} + \DeclareUnicodeCharacter{00EA}{\^e} + \DeclareUnicodeCharacter{00EB}{\"e} + \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} + \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} + \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} + \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} + + \DeclareUnicodeCharacter{00F1}{\~n} + \DeclareUnicodeCharacter{00F2}{\`o} + \DeclareUnicodeCharacter{00F3}{\'o} + \DeclareUnicodeCharacter{00F4}{\^o} + \DeclareUnicodeCharacter{00F5}{\~o} + \DeclareUnicodeCharacter{00F6}{\"o} + \DeclareUnicodeCharacter{00F8}{\o} + \DeclareUnicodeCharacter{00F9}{\`u} + \DeclareUnicodeCharacter{00FA}{\'u} + \DeclareUnicodeCharacter{00FB}{\^u} + \DeclareUnicodeCharacter{00FC}{\"u} + \DeclareUnicodeCharacter{00FD}{\'y} + \DeclareUnicodeCharacter{00FF}{\"y} + + \DeclareUnicodeCharacter{0100}{\=A} + \DeclareUnicodeCharacter{0101}{\=a} + \DeclareUnicodeCharacter{0102}{\u{A}} + \DeclareUnicodeCharacter{0103}{\u{a}} + \DeclareUnicodeCharacter{0106}{\'C} + \DeclareUnicodeCharacter{0107}{\'c} + \DeclareUnicodeCharacter{0108}{\^C} + \DeclareUnicodeCharacter{0109}{\^c} + \DeclareUnicodeCharacter{010A}{\dotaccent{C}} + \DeclareUnicodeCharacter{010B}{\dotaccent{c}} + \DeclareUnicodeCharacter{010C}{\v{C}} + \DeclareUnicodeCharacter{010D}{\v{c}} + \DeclareUnicodeCharacter{010E}{\v{D}} + + \DeclareUnicodeCharacter{0112}{\=E} + \DeclareUnicodeCharacter{0113}{\=e} + \DeclareUnicodeCharacter{0114}{\u{E}} + \DeclareUnicodeCharacter{0115}{\u{e}} + \DeclareUnicodeCharacter{0116}{\dotaccent{E}} + \DeclareUnicodeCharacter{0117}{\dotaccent{e}} + \DeclareUnicodeCharacter{011A}{\v{E}} + \DeclareUnicodeCharacter{011B}{\v{e}} + \DeclareUnicodeCharacter{011C}{\^G} + \DeclareUnicodeCharacter{011D}{\^g} + \DeclareUnicodeCharacter{011E}{\u{G}} + \DeclareUnicodeCharacter{011F}{\u{g}} + + \DeclareUnicodeCharacter{0120}{\dotaccent{G}} + \DeclareUnicodeCharacter{0121}{\dotaccent{g}} + \DeclareUnicodeCharacter{0124}{\^H} + \DeclareUnicodeCharacter{0125}{\^h} + \DeclareUnicodeCharacter{0128}{\~I} + \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} + \DeclareUnicodeCharacter{012A}{\=I} + \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} + \DeclareUnicodeCharacter{012C}{\u{I}} + \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} + + \DeclareUnicodeCharacter{0130}{\dotaccent{I}} + \DeclareUnicodeCharacter{0131}{\dotless{i}} + \DeclareUnicodeCharacter{0132}{IJ} + \DeclareUnicodeCharacter{0133}{ij} + \DeclareUnicodeCharacter{0134}{\^J} + \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} + \DeclareUnicodeCharacter{0139}{\'L} + \DeclareUnicodeCharacter{013A}{\'l} + + \DeclareUnicodeCharacter{0141}{\L} + \DeclareUnicodeCharacter{0142}{\l} + \DeclareUnicodeCharacter{0143}{\'N} + \DeclareUnicodeCharacter{0144}{\'n} + \DeclareUnicodeCharacter{0147}{\v{N}} + \DeclareUnicodeCharacter{0148}{\v{n}} + \DeclareUnicodeCharacter{014C}{\=O} + \DeclareUnicodeCharacter{014D}{\=o} + \DeclareUnicodeCharacter{014E}{\u{O}} + \DeclareUnicodeCharacter{014F}{\u{o}} + + \DeclareUnicodeCharacter{0150}{\H{O}} + \DeclareUnicodeCharacter{0151}{\H{o}} + \DeclareUnicodeCharacter{0152}{\OE} + \DeclareUnicodeCharacter{0153}{\oe} + \DeclareUnicodeCharacter{0154}{\'R} + \DeclareUnicodeCharacter{0155}{\'r} + \DeclareUnicodeCharacter{0158}{\v{R}} + \DeclareUnicodeCharacter{0159}{\v{r}} + \DeclareUnicodeCharacter{015A}{\'S} + \DeclareUnicodeCharacter{015B}{\'s} + \DeclareUnicodeCharacter{015C}{\^S} + \DeclareUnicodeCharacter{015D}{\^s} + \DeclareUnicodeCharacter{015E}{\cedilla{S}} + \DeclareUnicodeCharacter{015F}{\cedilla{s}} + + \DeclareUnicodeCharacter{0160}{\v{S}} + \DeclareUnicodeCharacter{0161}{\v{s}} + \DeclareUnicodeCharacter{0162}{\cedilla{t}} + \DeclareUnicodeCharacter{0163}{\cedilla{T}} + \DeclareUnicodeCharacter{0164}{\v{T}} + + \DeclareUnicodeCharacter{0168}{\~U} + \DeclareUnicodeCharacter{0169}{\~u} + \DeclareUnicodeCharacter{016A}{\=U} + \DeclareUnicodeCharacter{016B}{\=u} + \DeclareUnicodeCharacter{016C}{\u{U}} + \DeclareUnicodeCharacter{016D}{\u{u}} + \DeclareUnicodeCharacter{016E}{\ringaccent{U}} + \DeclareUnicodeCharacter{016F}{\ringaccent{u}} + + \DeclareUnicodeCharacter{0170}{\H{U}} + \DeclareUnicodeCharacter{0171}{\H{u}} + \DeclareUnicodeCharacter{0174}{\^W} + \DeclareUnicodeCharacter{0175}{\^w} + \DeclareUnicodeCharacter{0176}{\^Y} + \DeclareUnicodeCharacter{0177}{\^y} + \DeclareUnicodeCharacter{0178}{\"Y} + \DeclareUnicodeCharacter{0179}{\'Z} + \DeclareUnicodeCharacter{017A}{\'z} + \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} + \DeclareUnicodeCharacter{017C}{\dotaccent{z}} + \DeclareUnicodeCharacter{017D}{\v{Z}} + \DeclareUnicodeCharacter{017E}{\v{z}} + + \DeclareUnicodeCharacter{01C4}{D\v{Z}} + \DeclareUnicodeCharacter{01C5}{D\v{z}} + \DeclareUnicodeCharacter{01C6}{d\v{z}} + \DeclareUnicodeCharacter{01C7}{LJ} + \DeclareUnicodeCharacter{01C8}{Lj} + \DeclareUnicodeCharacter{01C9}{lj} + \DeclareUnicodeCharacter{01CA}{NJ} + \DeclareUnicodeCharacter{01CB}{Nj} + \DeclareUnicodeCharacter{01CC}{nj} + \DeclareUnicodeCharacter{01CD}{\v{A}} + \DeclareUnicodeCharacter{01CE}{\v{a}} + \DeclareUnicodeCharacter{01CF}{\v{I}} + + \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} + \DeclareUnicodeCharacter{01D1}{\v{O}} + \DeclareUnicodeCharacter{01D2}{\v{o}} + \DeclareUnicodeCharacter{01D3}{\v{U}} + \DeclareUnicodeCharacter{01D4}{\v{u}} + + \DeclareUnicodeCharacter{01E2}{\={\AE}} + \DeclareUnicodeCharacter{01E3}{\={\ae}} + \DeclareUnicodeCharacter{01E6}{\v{G}} + \DeclareUnicodeCharacter{01E7}{\v{g}} + \DeclareUnicodeCharacter{01E8}{\v{K}} + \DeclareUnicodeCharacter{01E9}{\v{k}} + + \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} + \DeclareUnicodeCharacter{01F1}{DZ} + \DeclareUnicodeCharacter{01F2}{Dz} + \DeclareUnicodeCharacter{01F3}{dz} + \DeclareUnicodeCharacter{01F4}{\'G} + \DeclareUnicodeCharacter{01F5}{\'g} + \DeclareUnicodeCharacter{01F8}{\`N} + \DeclareUnicodeCharacter{01F9}{\`n} + \DeclareUnicodeCharacter{01FC}{\'{\AE}} + \DeclareUnicodeCharacter{01FD}{\'{\ae}} + \DeclareUnicodeCharacter{01FE}{\'{\O}} + \DeclareUnicodeCharacter{01FF}{\'{\o}} + + \DeclareUnicodeCharacter{021E}{\v{H}} + \DeclareUnicodeCharacter{021F}{\v{h}} + + \DeclareUnicodeCharacter{0226}{\dotaccent{A}} + \DeclareUnicodeCharacter{0227}{\dotaccent{a}} + \DeclareUnicodeCharacter{0228}{\cedilla{E}} + \DeclareUnicodeCharacter{0229}{\cedilla{e}} + \DeclareUnicodeCharacter{022E}{\dotaccent{O}} + \DeclareUnicodeCharacter{022F}{\dotaccent{o}} + + \DeclareUnicodeCharacter{0232}{\=Y} + \DeclareUnicodeCharacter{0233}{\=y} + \DeclareUnicodeCharacter{0237}{\dotless{j}} + + \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} + \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} + \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} + \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} + \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} + \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} + \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} + \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} + \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} + \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} + \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} + \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} + + \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} + \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} + + \DeclareUnicodeCharacter{1E20}{\=G} + \DeclareUnicodeCharacter{1E21}{\=g} + \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} + \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} + \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} + \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} + \DeclareUnicodeCharacter{1E26}{\"H} + \DeclareUnicodeCharacter{1E27}{\"h} + + \DeclareUnicodeCharacter{1E30}{\'K} + \DeclareUnicodeCharacter{1E31}{\'k} + \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} + \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} + \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} + \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} + \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} + \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} + \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} + \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} + \DeclareUnicodeCharacter{1E3E}{\'M} + \DeclareUnicodeCharacter{1E3F}{\'m} + + \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} + \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} + \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} + \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} + \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} + \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} + \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} + \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} + \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} + \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} + + \DeclareUnicodeCharacter{1E54}{\'P} + \DeclareUnicodeCharacter{1E55}{\'p} + \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} + \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} + \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} + \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} + \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} + \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} + \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} + \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} + + \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} + \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} + \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} + \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} + \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} + \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} + \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} + \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} + \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} + \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} + + \DeclareUnicodeCharacter{1E7C}{\~V} + \DeclareUnicodeCharacter{1E7D}{\~v} + \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} + \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} + + \DeclareUnicodeCharacter{1E80}{\`W} + \DeclareUnicodeCharacter{1E81}{\`w} + \DeclareUnicodeCharacter{1E82}{\'W} + \DeclareUnicodeCharacter{1E83}{\'w} + \DeclareUnicodeCharacter{1E84}{\"W} + \DeclareUnicodeCharacter{1E85}{\"w} + \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} + \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} + \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} + \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} + \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} + \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} + \DeclareUnicodeCharacter{1E8C}{\"X} + \DeclareUnicodeCharacter{1E8D}{\"x} + \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} + \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} + + \DeclareUnicodeCharacter{1E90}{\^Z} + \DeclareUnicodeCharacter{1E91}{\^z} + \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} + \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} + \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} + \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} + \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} + \DeclareUnicodeCharacter{1E97}{\"t} + \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} + \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} + + \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} + \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} + + \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} + \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} + \DeclareUnicodeCharacter{1EBC}{\~E} + \DeclareUnicodeCharacter{1EBD}{\~e} + + \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} + \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} + \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} + \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} + + \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} + \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} + + \DeclareUnicodeCharacter{1EF2}{\`Y} + \DeclareUnicodeCharacter{1EF3}{\`y} + \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} + + \DeclareUnicodeCharacter{1EF8}{\~Y} + \DeclareUnicodeCharacter{1EF9}{\~y} + + \DeclareUnicodeCharacter{2013}{--} + \DeclareUnicodeCharacter{2014}{---} + \DeclareUnicodeCharacter{2022}{\bullet} + \DeclareUnicodeCharacter{2026}{\dots} + \DeclareUnicodeCharacter{20AC}{\euro} + + \DeclareUnicodeCharacter{2192}{\expansion} + \DeclareUnicodeCharacter{21D2}{\result} + + \DeclareUnicodeCharacter{2212}{\minus} + \DeclareUnicodeCharacter{2217}{\point} + \DeclareUnicodeCharacter{2261}{\equiv} +}% end of \utfeightchardefs + + +% US-ASCII character definitions. +\def\asciichardefs{% nothing need be done + \relax +} + +% Make non-ASCII characters printable again for compatibility with +% existing Texinfo documents that may use them, even without declaring a +% document encoding. +% +\setnonasciicharscatcode \other + + +\message{formatting,} + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be so finicky about underfull hboxes, either. +\hbadness = 2000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{46\baselineskip}{6in}% + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {\voffset}{.25in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{51\baselineskip}{160mm} + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1 + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\catcode`\$=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} +\def\normaldollar{$}%$ font-lock fix + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\let\realunder=_ +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +\catcode`\$=\active +\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work + +% \realbackslash is an actual character `\' with catcode other, and +% \doublebackslash is two of them (for the pdf outlines). +{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} + +% In texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active +@def@normalbackslash{{@tt@backslashcurfont}} +% On startup, @fixbackslash assigns: +% @let \ = @normalbackslash + +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. +@gdef@rawbackslash{@let\=@backslashcurfont} +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +@def@normalturnoffactive{% + @let\=@normalbackslash + @let"=@normaldoublequote + @let~=@normaltilde + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let<=@normalless + @let>=@normalgreater + @let+=@normalplus + @let$=@normaldollar %$ font-lock fix + @unsepspaces +} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\' in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also turn back on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active + @catcode`@_=@active +} + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These look ok in all fonts, so just make them not special. +@catcode`@& = @other +@catcode`@# = @other +@catcode`@% = @other + + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore diff --git a/common/ChangeLog-2011 b/common/ChangeLog-2011 new file mode 100644 index 0000000..4b95b35 --- /dev/null +++ b/common/ChangeLog-2011 @@ -0,0 +1,2494 @@ +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-11-30 Werner Koch + + Rewrite dns-cert.c to not use the gpg-only iobuf stuff. + * dns-cert.c: Remove iobuf.h. + (get_dns_cert): Rename to _get_dns_cert. Remove MAX_SIZE arg. + Change iobuf arg to a estream-t. Rewrite function to make use of + estream instead of iobuf. Require all parameters. Return an + gpg_error_t error instead of the type. Add arg ERRSOURCE. + * dns-cert.h (get_dns_cert): New macro to pass the error source to + _gpg_dns_cert. + * t-dns-cert.c (main): Adjust for changes in get_dns_cert. + + * estream.c (es_fopenmem_init): New. + * estream.h (es_fopenmem_init): New. + +2011-11-29 Werner Koch + + * estream.c (func_mem_create): Don't set FUNC_REALLOC if GROW is + not set. Require FUNC_REALLOC if DATA is NULL and FUNC_FREE is + given. + + * dns-cert.c: Use new CERTTYPE_ constants for better readability. + +2011-11-28 Werner Koch + + * t-dns-cert.c (main): Increase MAX_SIZE to 64k. + + * dns-cert.c (get_dns_cert): Factor test code out to ... + * t-dns-cert.c: new file. + +2011-10-24 Werner Koch + + * dotlock.h, dotlock.c: Add alternative to allow distribution of + these files under a modified BSD license + +2011-09-30 Werner Koch + + Change the license of all JNLIB parts from LPGLv3+ to to LGPLv3+ + or GPLv2+. + + * dotlock.h (DOTLOCK_EXT_SYM_PREFIX): New macro. + +2011-09-29 Werner Koch + + * dotlock.c (DOTLOCK_USE_PTHREAD): New macro. + [DOTLOCK_USE_PTHREAD] (all_lockfiles_mutex): New. + (LOCK_all_lockfiles, UNLOCK_all_lockfiles): New. Use them to + protect access to all_lockfiles. + (dotlock_set_fd, dotlock_get_fd): New. + +2011-09-28 Werner Koch + + * dotlock.c (dotlock_take, dotlock_take_unix, dotlock_take_w32): + Implement arbitrary timeout values. + (dotlock_create): Add arg FLAGS for future extensions. + +2011-09-27 Werner Koch + + * dotlock.c (dotlock_take_unix): Check only the link count and not + the error return from link. + (use_hardlinks_p): New. + (dotlock_create_unix): Test for hardlinks. + (dotlock_take_unix): Implement O_EXCL locking. + +2011-09-23 Werner Koch + + * dotlock.c: Factor Unix and W32 specific code out into specific + functions. Define HAVE_POSIX_SYSTEM. Rearrange some functions. + (disable_dotlock): Rename to dotlock_disable. + (create_dotlock): Rename to dotlock_create. + (destroy_dotlock): Rename to dotlock_destroy. + (make_dotlock): Rename to dotlock_take. + (release_dotlock): Rename to dotlock_release. + +2011-09-22 Werner Koch + + * dotlock.c: Remove support for RISCOS. + +2011-08-10 Werner Koch + + * t-exechelp.c (test_close_all_fds): Don't use the DUMMY_FD var. + + * pka.c (get_pka_info): Remove unused var. + + * signal.c (got_fatal_signal): Remove unused var. + + * estream.c (es_fread, es_fwrite): Remove unused var. + +2011-07-20 Werner Koch + + * ssh-utils.c, ssh-utils.h: New. + * t-ssh-utils.c: New. + * Makefile.am (t_ssh_utils_LDADD): New. + (module_tests): Add t-ssh-utils.c + +2011-06-01 Marcus Brinkmann + + * util.h: Undef snprintf before redefining it. + +2011-05-20 Werner Koch + + * util.h: Remove some error code substitutes. + +2011-04-25 Werner Koch + + * userids.c (classify_user_id): Add arg OPENPGP_HACK to fix + regression from 2009-12-08. + +2011-04-01 Werner Koch + + * sysutils.c (get_uint_nonce): New. + +2011-03-03 Werner Koch + + * estream.c (struct estream_list): Rename to estream_list_s and + simplify. A double linked list is overkill for our purpose. + (do_list_add, do_list_remove): Adjust accordingly. + (_es_get_std_stream): Ditto. + (do_list_iterate, estream_iterator_t): Remove; it is used only at + one place. + (es_fflush): Replace iteration function. Also lock each stream + while flushing all streams. + +2011-02-27 Werner Koch + + * gettime.c (isotime2epoch): Factor check code out to .. + (isotime_p): .. new. + (isotime_human_p): New. + (string2isotime): New. + * t-gettime.c (test_string2isotime): New. + +2011-02-11 Andrey Jivsov + + * openpgp-oid.c (openpgp_oid_to_str): Use unsigned int for + get_opaque. Fixes a bug on 64 bit platforms. + +2011-02-08 Werner Koch + + * http.c (connect_server): Add arg R_HOST_NOT_FOUND. + +2011-02-07 Werner Koch + + * http.c (my_socket_new, my_socket_ref, my_socket_unref): New. + (cookie_close, cookie_read, cookie_write, http_close, _http_open) + (send_request): Replace use of an socket integer by the new socket + object. + (_http_raw_connect): New. + (fp_onclose_notification): New. + (_http_raw_connect, _http_wait_response, http_close): Register and + unregister this notification. + * http.h (http_raw_connect): New. + + * http.h (parsed_uri_s): Add field IS_OPAQUE. + (http_req_t): Add HTTP_REQ_OPAQUE. + * http.c (do_parse_uri): Parse unknown schemes into PATH. + (my_socket_new, my_socket_ref, my_socket_unref): New. + (send_request): Simplify save_errno stuff. + +2011-02-03 Werner Koch + + * status.h (STATUS_DECRYPTION_INFO): New. + + * argparse.c (strusage): Update copyright year. + +2011-01-31 Werner Koch + + * openpgp-oid.c: New. + * t-openpgp-oid.c: New. + +2011-01-20 Werner Koch + + Fix bug#1313. + + * http.c (my_select): New. Define to pth_select if building with Pth. + (start_server, write_server, cookie_read, cookie_write): Use it. + (my_connect): New. Define to pth_connect if building with Pth. + (connect_server): Use it. + (my_accept): New. Define to pth_accept if building with Pth. + (start_server): Use it. + +2011-01-20 Werner Koch + + * util.h (struct b64state): Add field LASTERR. + * b64enc.c (enc_start, b64enc_write, b64enc_finish): Handle + LASTERR. This is to make sure that we don't leak strduped data. + * b64dec.c (b64dec_start, b64dec_proc, b64dec_finish): Ditto. + + * http.c (escape_data): New. + (insert_escapes): Implement using escape_data. + (http_escape_data): New. + +2011-01-19 Werner Koch + + * homedir.c (gnupg_module_name): Use NAME_OF_INSTALLED_GPG instead + of "gpg2". + +2011-01-18 Werner Koch + + * iobuf.c (file_es_filter_ctx_t): New. + (file_es_filter): New. + (iobuf_esopen): New. + + * membuf.c (clear_membuf, peek_membuf): New. + + * util.h (GPG_ERR_NO_KEYSERVER): New. + + * keyserver.h (keyserver_spec): Move from ../g10/options.h to here. + + * http.c (do_parse_uri): Add arg NO_SCHEME_CHECK. Change all + callers. Support HKP and HKPS. + (_http_parse_uri): Do proper error management. + * http.h (parsed_uri_s): Add field IS_HTTP. + (http_parse_uri): Support NO_SCHEME_CHECK arg. + + * estream.c (es_func_mem_write): Fix computation of NEWSIZE. + +2011-01-10 Werner Koch + + * session-env.c (update_var): Fix same value detection. Fixes + bug#1311. + +2010-12-17 Werner Koch + + * asshelp.c (lock_spawning): Add arg VERBOSE. Improve timeout + management. Make callers pass a value for VERBOSE. + (lock_agent_spawning, unlock_agent_spawning): Remove. Change + callers to use lock_spawning and unlock_spawning. + +2010-12-17 Marcus Brinkmann + + * homedir.c (gnupg_cachedir): Create /temp subdirectories. + +2010-12-02 Werner Koch + + * miscellaneous.c (gnupg_cipher_algo_name): New. Replace all + users of gcry_cipher_algo_name by this one. + + * logging.c (fun_cookie_s) [W32CE]: Add field USE_WRITEFILE. + (fun_writer) [W32CE]: Make use of it. + (set_file_fd) [W32CE]: Implement special filename "GPG2:". + +2010-11-25 Werner Koch + + * asshelp.c (start_new_gpg_agent): Change style of startup info. + (start_new_dirmngr): Ditto. + +2010-11-23 Werner Koch + + * asshelp.c (SECS_TO_WAIT_FOR_AGENT, SECS_TO_WAIT_FOR_DIRMNGR): + Use these constants. For W32CE increase them to 30 seconds. + (start_new_gpg_agent): Print time to startup agent. + (start_new_dirmngr): Ditto. + +2010-11-04 Werner Koch + + * logging.c (do_logv) [W32]: Don't set a default log stream if the + registry entry is empty. + +2010-10-27 Werner Koch + + * gettime.c (gnupg_get_isotime): Compare to (time_t)-1. + (epoch2isotime): Ditto. + (IS_INVALID_TIME_T): New. + (asctimestamp): Use new macro. + (strtimestamp, isotimestamp): Ditto. Use snprintf. + +2010-10-25 Werner Koch + + * logging.c (do_log): Rename to log_log and make global. + +2010-10-20 Werner Koch + + * i18n.c (i18n_init) [USE_SIMPLE_GETTEXT]: Call textdomain. + +2010-10-14 Werner Koch + + * asshelp.c (start_new_gpg_agent): Print a notice once the agent + has been started. + (start_new_dirmngr): Likewise. + +2010-10-13 Werner Koch + + * miscellaneous.c (parse_version_number, parse_version_string) + (gnupg_compare_version): New. + +2010-10-04 Werner Koch + + * gettime.c (asctimestamp) [W32CE]: Do not print the timezone. + +2010-09-30 Werner Koch + + * util.h (GPG_ERR_FULLY_CANCELED): Add replacement. + +2010-09-17 Werner Koch + + * http.c (INADDR_NONE): Provide fallback. + * logging.c (INADDR_NONE): Ditto. + +2010-09-16 Werner Koch + + * util.h: Add GPG_ERR_MISSING_ISSUER_CERT. + * status.c (get_inv_recpsgnr_code): Ditto. + +2010-09-13 Werner Koch + + * homedir.c (gnupg_bindir) [W32CE]: Change to bin/. + (gnupg_libexecdir) [W32]: Call gnupg_bindir. + (gnupg_libdir, gnupg_datadir, gnupg_localedir) [W32]: Simplify by + using xstrconcat. + (gnupg_module_name): Ditto. + (w32_rootdir): Strip a trailing "bin". + +2010-09-02 Werner Koch + + * util.h (GPG_ERR_NOT_INITIALIZED): Define if not defined. + +2010-09-01 Marcus Brinkmann + + * estream.c (_es_set_std_fd): Disable debug output. + +2010-08-26 Werner Koch + + * estream.c (es_convert_mode): Rename to parse_mode. + (parse_mode): Add arg R_CMODE and parse key value pairs. Use Use + 664 as the default mode. Change callers. + (ES_DEFAULT_OPEN_MODE): Remove. + (es_fopen, do_fpopen, do_w32open, es_freopen): Support a creation + mode. + (es_func_file_create): Rename to func_file_create and add arg CMODE. + (es_func_fd_create): Rename to func_fd_create. + (es_func_fp_create): Rename to func_fp_create. + (es_list_add): Rename to do_list_add. + (es_list_remove): Rename to do_list_remove. + (es_list_iterate): Rename to do_list_iterate. + (es_pth_read): Rename to do_pth_read. + (es_deinit): Rename to do_deinit. + (es_init_do): Rename to do_init. + (es_func_mem_create): Rename to func_mem_create. + +2010-08-23 Werner Koch + + * exechelp-w32ce.c: Rewrite all spawn stuff. + + * exechelp-w32.c (close_all_fds) [W32]: Make it a dummy function. + + * estream.c (es_onclose): New. + (notify_list_t, onclose): New. + (struct estream_internal): Add field ONCLOSE. + (es_initialize, es_deinitialize): Manage new field. + (do_close): Call onclose notify functions. + +2010-08-20 Werner Koch + + * exechelp-w32.c (create_inheritable_pipe): Change arg to HANDLE. + + * estream.h (es_sysopen_t): New. + * estream.c (es_func_w32_create, es_func_w32_read) + (es_func_w32_write, es_func_w32_seek, es_func_w32_destroy) + (estream_functions_w32, estream_cookie_fd): New. Only for W32. + (es_sysopen, es_sysopen_nc): New. + (do_w32open, do_sysopen): New. + (es_syshd, es_syshd_unlocked): New. + (struct estream_internal): Replace filed FD by SYSHD. + (es_initialize): Clear SYSHD_VALID. + (map_w32_to_errno): New. + (es_get_fd): Remove. + (es_fileno_unlocked): Re-implement using es_syshd. + (es_initialize, es_create): Replace arg FD by SYSHD. + (es_fopen, es_mopen, es_fopenmem, do_fdopen, do_fpopen) + (es_tmpfile): Use SYSHD instead of FD. + (es_destroy): Rename to do_close. + +2010-08-19 Werner Koch + + * exechelp-posix.c (create_pipe_and_estream): New. + (gnupg_spawn_process): Rework this function and its calling + convention; it is not used anyway. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + +2010-08-18 Werner Koch + + * logging.c (writen): Add arg IS_SOCKET. + (fun_writer): Pass the is_socket flag. + (do_logv) [W32]: Allow for a default log stream + + * estream.c (struct estream_internal): Remove obsolete fields + PRINT_FP, PRINT_ERRNO, PRINT_ERR and all remaining code cruft. + +2010-08-16 Werner Koch + + * estream.c (es_printf_unlocked, es_printf): New. + + * asshelp.c (lock_agent_t): Rename to lock_spawn_t. + (lock_agent_spawning, unlock_agent_spawning): Factor code out to ... + (lock_spawning, unlock_spawning): .. new. + (start_new_gpg_agent): Make more use of ERRSOURCE. + (start_new_dirmngr): New. + +2010-08-13 Werner Koch + + * Makefile.am (audit-events.h, status-codes.h): Fix srcdir problem + amd depend on Makefile.am instead of Makefile. + +2010-08-12 Werner Koch + + * sysutils.c (gnupg_remove) [W32CE]: Fix returned error. + +2010-08-09 Werner Koch + + * logging.c (WITH_IPV6): New macro. + (parse_portno): New. From libassuan. + (fun_writer): Support TCP logging on all platforms. + (sock_close): New. + +2010-08-06 Werner Koch + + * homedir.c (dirmngr_socket_name) [W32CE]: Base on default homedir. + (gnupg_cachedir) [W32CE]: Drop drive letter. + + * http.c (http_open_document): Rename to _http_open_document and + add arg ERRSOURCE. Pass ERRSOURCE to all called funcs. + (http_wait_response, http_open, http_parse_uri): Likewise. + (do_parse_uri, parse_response, store_header): Change to return an + gpg_err_code_t. Change callers. + (send_request): Add arg ERRSOURCE. Change callers. + * http.h (http_open_document, http_wait_response, http_open) + (http_parse_uri): Define as macro. + +2010-08-05 Werner Koch + + * estream.h (es_asprintf, es_vasprintf): Add lost prototyps. + + * http.c: Require estream and make HTTP_USE_ESTREAM obsolete. It + make the code unreadable and we require estream anyway for GnuPG. + (http_wait_response): Get use of cookies right. + (send_request): s/xtryasprintf/es_asprintf/ to allow standalone + use of the code. + (insert_escapes, connect_server): s/sprintf/snprintf/. + (parse_response): s/my_read_line/es_read_line/. + (my_read_line): Remove. + (write_server): Use pth_write. + +2010-07-26 Werner Koch + + * estream.c (es_func_fp_write) [W32]: Write smaller chunks. + +2010-07-25 Werner Koch + + * argparse.c (initialize): Use ARGPARSE_PRINT_WARNING constant. + +2010-07-24 Werner Koch + + * estream.c (es_set_binary): New. + +2010-07-19 Werner Koch + + * utf8conv.c (utf8_to_wchar): s/malloc/jnlib_malloc/. + +2010-07-16 Werner Koch + + * http.h (HTTP_FLAG_IGNORE_CL): Add flag . + * http.c (WITHOUT_GNU_PTH): Test macro for Pth support. + (http_parse_uri): s/xcalloc/xtrycalloc/. + (send_request): Replace of discrete allocation and sprintf by + xtryasprintf. + (http_wait_response): Replace HTTP_FLAG_NO_SHUTDOWN by + HTTP_FLAG_SHUTDOWN to change the default to no shutdown. + (cookie_read) [HAVE_PTH]: Use pth_read. + (longcounter_t): New. + (struct cookie_s): Add support for content length. Turn flag + fields into bit types. + (parse_response): Parse content length header. + (cookie_read): Take care of the content length. + +2010-07-08 Werner Koch + + * estream.c (estream_functions_file): Remove and replace by + identical estream_functions_fd. + +2010-07-06 Werner Koch + + * util.h (b64state): Add field STREAM. + * b64enc.c (b64enc_start): Factor code out to .. + (enc_start): new. + (b64enc_start_es, my_fputs): New. + (b64enc_write, b64enc_finish): Support estream. + +2010-06-24 Werner Koch + + * asshelp.c (lock_agent_spawning) [W32]: Use CreateMutexW. + (start_new_gpg_agent): Use HANG option for gnupg_wait_progress. + Fixes regression from 2010-06-09. + +2010-06-21 Werner Koch + + * util.h (xfree_fnc): New. + +2010-06-18 Werner Koch + + * util.h (GPG_ERR_MISSING_KEY) [!GPG_ERR_MISSING_KEY]: New. + + * sexputil.c (make_canon_sexp_pad): Add arg SECURE. + +2010-06-17 Werner Koch + + * sexputil.c (make_canon_sexp_pad): New. + +2010-06-14 Werner Koch + + * membuf.c (put_membuf): Add shortcut for !LEN. + +2010-06-11 Marcus Brinkmann + + * sysutils.c (translate_sys2libc_fd): Revert last change. + (translate_sys2libc_fd_int): Revert last change. + +2010-06-10 Marcus Brinkmann + + * sysutils.c (translate_sys2libc_fd) [HAVE_W32CE_SYSTEM]: + Implement. + (translate_sys2libc_fd_int) [HAVE_W32CE_SYSTEM]: Don't call + translate_sys2libc_fd. + + * estream.c (_es_get_std_stream): Fix cut&paste bug. + +2010-06-09 Werner Koch + + * exechelp-posix.c, exechelp-w32.c + * exechelp-w32ce.c (gnupg_wait_process): Add new arg HANG. Change + all callers. + (gnupg_release_process): New. Use it after all calls to + gnupg_wait_process. + + * util.h (GNUPG_MODULE_NAME_DIRMNGR_LDAP): New. + * homedir.c (gnupg_cachedir): New. + (w32_try_mkdir): New. + (dirmngr_socket_name): Change standard socket name. + (gnupg_module_name): Support GNUPG_MODULE_NAME_DIRMNGR_LDAP. + + * logging.c (log_set_get_tid_callback): Replace by ... + (log_set_pid_suffix_cb): .. new. + (do_logv): Change accordingly. + +2010-06-08 Marcus Brinkmann + + * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS). + (t_common_ldadd): Add $(LIBASSUAN_LIBS). + * sysutils.c: Include . + (translate_sys2libc_fd_int): Cast to silence gcc warning. + * iobuf.c: Include + (translate_file_handle): Fix syntax error. + +2010-06-08 Werner Koch + + * iobuf.c (translate_file_handle) [W32CE]: Handle rendezvous ids. + +2010-06-07 Werner Koch + + * sysutils.c [W32CE]: Finish pipe creation. + + * estream.c (es_fname_get, es_fname_set): New. + (fname_set_internal): New. + (struct estream_internal): Add fields printable_fname and + printable_fname_inuse. + (_es_get_std_stream): Set stream name. + (es_fopen, es_freopen, es_deinitialize): Set fname. + + * exechelp-posix.c (gnupg_spawn_process): Allow passing INFILE or + OUTFILE as NULL. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + * exechelp-w32ce.c (gnupg_spawn_process): Return an error for + INFILE or OUTFILE passed as NULL. + +2010-06-01 Werner Koch + + * logging.c (log_get_stream): Make sture a log stream is available. + +2010-05-30 Werner Koch + + * init.c (writestring_via_estream): New. + (init_common_subsystems): Register with argparse. + + * argparse.c (argparse_register_outfnc): New. + (writestrings, flushstrings): New. Use them instead of stdout or + stderr based functions. + +2010-05-04 Werner Koch + + * estream.c (_es_get_std_stream): Re-use registered standard fds. + (IS_INVALID_FD, ESTREAM_SYS_YIELD): New. + (es_func_fd_read, es_func_fd_write, es_func_fd_seek) + (es_func_fd_destroy): Implement a dummy stream. + + * exechelp-w32ce.c (build_w32_commandline): Add args FD0_ISNULL + and FD1_ISNULL. Remove arg PGMNAME. Change callers. + (gnupg_spawn_process_detached): Implement. + (gnupg_spawn_process_fd): Implement one special case for now. + +2010-05-03 Werner Koch + + * asshelp.c (lock_agent_spawning, unlock_agent_spawning): New. + (start_new_gpg_agent): Test for configured standard socket and + try to fire up the agent in this case. + + * exechelp-posix.c (gnupg_wait_process): Do not log a message if + EXITCODE is given. + (gnupg_spawn_process_detached): Do not reuse PID for the second fork. + +2010-04-26 Werner Koch + + * utf8conv.c (load_libiconv) [W32CE]: No libiconv warning + + * init.c (init_common_subsystems) [W32CE]: Register the sleep + function before es_init. + +2010-04-20 Werner Koch + + * estream.c (es_deinit): New. + (es_init_do): Install atexit handler to flush all streams. + + * Makefile.am (common_sources): Add gettime.h. + +2010-04-20 Marcus Brinkmann + + * logging.c (do_log_ignore_arg): New helper function. + (log_string): Use it to remove ugly volatile hack that causes gcc + warning. + (log_flush): Likewise. + * sysutils.c (gnupg_unsetenv) [!HAVE_W32CE_SYSTEM]: Return something. + (gnupg_setenv) [!HAVE_W32CE_SYSTEM]: Likewise. + * pka.c (get_pka_info): Solve strict aliasing rule violation. + * t-exechelp.c (test_close_all_fds): Use dummy variables to + silence gcc warning. + +2010-04-15 Werner Koch + + * util.h: Factor time related functions out to ... + * gettime.h: New. + (gnupg_copy_time): Move to ... + * gettime.c (gnupg_copy_time): New. + + * sysutils.c (gnupg_setenv) [!W32CE]: Add missing return. + (gnupg_unsetenv) [!W32CE]: Add missing return. + +2010-04-14 Werner Koch + + * Makefile.am (noinst_LIBRARIES) [W32CE]: Exclude libsimple-pwquery. + + * w32help.h (umask) [W32CE]: New. + + * sysutils.c (_gnupg_isatty): New. + * util.h (gnupg_isatty): New. + + * asshelp.c (setup_libassuan_logging): Read ASSUAN_DEBUG envvar. + (my_libassuan_log_handler): Use it. + * sysutils.c (_gnupg_getenv): Implement ASSUAN_DEBUG. + +2010-04-08 Werner Koch + + * w32help.h (_setmode, setmode) [W32CE]: Provide prototype and + macro. + +2010-04-07 Werner Koch + + * mischelp.c (timegm): Replace unsetenv/putenv by gnupg_unsetenv. + + * sysutils.c: Include setenv.h. + (gnupg_setenv, gnupg_unsetenv): New. + + +2010-04-06 Werner Koch + + * sysutils.c (gnupg_mkdir): New. + +2010-03-29 Werner Koch + + * init.c (sleep_on_exit): Change to 400ms. + +2010-03-25 Werner Koch + + * init.c (sleep_on_exit) [W32CE]: New. + (init_common_subsystems): Call it. + +2010-03-24 Werner Koch + + * stringhelp.c (change_slashes, compare_filenames): Replace + HAVE_DRIVE_LETTERS by HAVE_DOSISH_SYSTEM. + (make_basename, make_dirname): Detect backslashes and drive + letters separately. + + * dotlock.c (make_dotlock, create_dotlock, release_dotlock): Use + LockFileEx and UnlockFileEx to support W32CE. + + * ttyio.c (USE_W32_CONSOLE): Replace all _WIN32 by this. + (init_ttyfp) [W32CE]: Use stderr. + + * iobuf.c (FD_FOR_STDIN, FD_FOR_STDOUT) [W32CE]: Use estream. + (translate_file_handle) [W32CE]: Remove handle translation. + +2010-03-23 Werner Koch + + * sysutils.c (gnupg_remove): New. + +2010-03-22 Werner Koch + + * exechelp-w32ce.c (build_w32_commandline): Replace by code from + libassuan. + (create_inheritable_pipe): Use _assuan_w32ce_prepare_pipe. + (build_w32_commandline_copy, do_create_pipe): Remove. + + * exechelp-posix.c (gnupg_spawn_process): Change to use estream + also for INFILE and STATUSFILE. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + +2010-03-22 Werner Koch + + * exechelp.c: Remove after factoring all code out to ... + * exechelp-posix.c, exechelp-w32.c, exechelp-w32ce.c: .. new. + + * exechelp.c (create_inheritable_pipe_r) + (create_inheritable_pipe_w): Fold both into ... + (create_inheritable_pipe): .. New. Change callers to use this. + (gnupg_create_inbound_pipe, gnupg_create_outbound_pipe): Factor + code out to ... + (do_create_pipe): .. New. + + * init.c (parse_std_file_handles): Change to use rendezvous ids. + +2010-03-15 Werner Koch + + * init.c (init_common_subsystems): Add args ARGCP and + ARGVP. Change all callers to provide them. + (parse_std_file_handles): New. + + * t-sysutils.c (rewind) [W32CE]: Provide a replacement. + + * Makefile.am (module_tests) [W32CE]: Don't build t-exechelp for now. + + * sysutils.c (gnupg_allow_set_foregound_window) [W32CE]: Don't + call AllowSetForegroundWindow. + + * logging.c (isatty) [W32CE]: New. + (fun_writer, set_file_fd): Use estream even for the internal error + messages. + (log_string, log_flush): Make DUMMY_ARG_PTR static. + +2010-03-15 Werner Koch + + * asshelp.c (send_pinentry_environment) [!HAVE_SETLOCALE]: Do not + define OLD_LC. + * http.c (connect_server) [!USE_DNS_SRV]: Mark SRVTAG unused. + * dns-cert.c (get_dns_cert) [!USE_DNS_CERT]: Mark args unused. + * pka.c (get_pka_info): Ditto. + + * signal.c (pause_on_sigusr): Remove. It was used in ancient gpg + version with shared memory IPC. Last caller removed on 2006-04-18. + (do_block) [W32]: Mark arg unused. + + * exechelp.c (w32_open_null): Use CreateFileW. + + * init.c (init_common_subsystems): Add args ARGCP and ARGVP. + Change all callers to pass them. + + * logging.c (S_IRGRP, S_IROTH, S_IWGRP, S_IWOTH) [W32]: New. + (fun_writer, set_file_fd) [W32]: Disable socket code. + + * localename.c: Include gpg-error.h. + + * util.h (GPG_ERR_NOT_ENABLED): Remove this temporary definition. + +2010-03-12 Werner Koch + + * status.h (STATUS_ENTER): New. + + * ttyio.c (tty_fprintf): Change to use estream. + + * miscellaneous.c (print_utf8_string): Rename to print_utf8_buffer + and change FP arg to an estream. Change all callers. + (print_utf8_string2): Ditto; new name is to print_utf8_buffer2. + +2010-03-11 Werner Koch + + * miscellaneous.c (print_string): Remove. + + * estream.c (es_setvbuf): Fix parameter check. + (es_set_buffering): Allow a SIZE of 0. + * asshelp.c (setup_libassuan_logging, my_libassuan_log_handler): New. + * logging.c (do_logv): Add arg IGNORE_ARG_PTR. Change all callers. + (log_string): New. + (log_flush): New. + (set_file_fd): Simplify by using estreams es_stderr. + + * estream.h (es_stdout, es_stderr, es_stdin): New. + +2010-03-10 Werner Koch + + * estream.c (es_func_fp_read, es_func_fp_write, es_func_fp_seek) + (es_func_fp_destroy): Allow a NULL FP to implement a dummy stream. + (do_fpopen): Ditto. + (es_vfprintf_unlocked): New. + (es_fprintf_unlocked): Make public. + (es_fputs_unlocked): New. + + * logging.h: Replace FILE* by estream_t. + * logging.c: Remove USE_FUNWRITER cpp conditional because we now + use estream. + (my_funopen_hook_ret_t, my_funopen_hook_size_t): Replace by + ssize_t. + (log_get_stream): Change to return an estream_t. + (set_file_fd): Always close the log stream because it can't be + assigned to stderr or stdout directly. Use a dummy estream as + last resort log stream. + (log_test_fd, log_get_fd): Use es_fileno. + (log_get_stream): Assert that we have a log stream. + (do_logv): Use estream functions and lock the output. + +2010-03-10 Werner Koch + + * util.h: Replace jnlib path part by common. + (snprintf): Use the replacement macro on all platforms. + + * Makefile.am (jnlib_sources): New. + (libcommon_a_SOURCES, libcommonpth_a_SOURCES): Add jnlib_sources. + (jnlib_tests): New. + (noinst_PROGRAMS, TESTS): Add jnlib_tests. + (t_common_ldadd): Remove libjnlib.a. + + * README.jnlib, ChangeLog.jnlib, libjnlib-config.h, argparse.c + * argparse.h, dotlock.c, dotlock.h, dynload.h, logging.c + * logging.h, mischelp.c, mischelp.h, stringhelp.c, stringhelp.h + * strlist.c, strlist.h, types.h, utf8conv.c, utf8conv.h + * w32-afunix.c, w32-afunix.h, w32-reg.c, w32help.h, xmalloc.c + * xmalloc.h, t-stringhelp.c, t-support.c, t-support.h + * t-timestuff.c, t-w32-reg.c: Move from jnlib to here. + + * init.c: Remove "estream.h". + * util.h: Include "estream.h". + + * xasprintf.c, ttyio.c: Remove "estream-printf.h". + +2010-03-08 Werner Koch + + * exechelp.c [!HAVE_SIGNAL_H]: Do not include signal.h. + (DETACHED_PROCESS, CREATE_NEW_PROCESS_GROUP) [W32CE]: Provide stubs. + + * iobuf.h (iobuf_ioctl_t): New. Use the new macros instead of the + hard wired values. + * iobuf.c (iobuf_append): Remove. + (iobuf_fdopen): Factor code out to ... + (do_iobuf_fdopen): ... new. + (iobuf_fdopen_nc): New. + (iobuf_open_fd_or_name): Implement using iobuf_fdopen_nc. + + * iobuf.c (INVALID_FD): Replace by GNUPG_INVALID_FD. + (fp_or_fd_t): Replace by gnupg_fd_t. + (my_fileno): Replace by the FD2INT macro. + (FILEP_OR_FD_FOR_STDIN, FILEP_OR_FD_FOR_STDOUT): Rename to + FD_FOR_STDIN, FD_FOR_STDOUT. + (file_filter): Make full use of FD_FOR_STDIN. + (USE_SETMODE): Remove. Not needed without stdio. + (my_fopen_ro, my_fopen): Replace unneeded macros. + + * iobuf.c [FILE_FILTER_USES_STDIO]: Remove all code. It has not + been used for a long time. + + * exechelp.h: Include "estream.h". + + * exechelp.c (gnupg_spawn_process): Change OUTFILE to an estream_t. + +2010-03-02 Werner Koch + + * estream.c, estream.h, estream-printf.c, estream-printf.h: Update + from libestream. + +2010-03-01 Werner Koch + + * signal.c [!HAVE_SIGNAL_H]: Don't include signal.h. + + * iobuf.c (direct_open) [W32CE]: Make filename to wchar_t. + (iobuf_cancel) [W32CE]: Use DeleteFile. + + * gettime.c (dump_isotime): Use "%s" to print "none". + + * homedir.c (standard_homedir) [W32CE]: Use wchar_t to create the + directory. + (w32_rootdir) [W32CE]: Likewise. + + * sysutils.c (translate_sys2libc_fd) [W32CE]: Add support. + (gnupg_tmpfile) [W32CE]: Ditto. + (_gnupg_getenv) [W32CE]: New. + + * util.h (getpid, getenv) [W32CE]: New. + + * i18n.c (i18n_switchto_utf8) + (i18n_switchback) [USE_SIMPLE_GETTEXT]: Use new function from + libgpg-error which supports proper restoring. + + * sysutils.c (get_session_marker): Simplified by using gcrypt. + +2009-12-08 Marcus Brinkmann + + * Makefile.am (audit-events.h, status.h) [!MAINTAINER_MODE]: No + longer include these rules if not in maintainer mode. + +2009-12-08 Werner Koch + + * userids.h, userids.c: New. + (classify_user_id): Merged from similar fucntions in sm/ and g10/. + + * dns-cert.c (get_dns_cert): Add support for ADNS. + +2009-12-08 Marcus Brinkmann + + * asshelp.c (start_new_gpg_agent): Convert posix FD to assuan FD. + + * asshelp.c (start_new_gpg_agent) [HAVE_W32_SYSTEM]: Add missing + argument in assuan_socket_connect invocation. + * iobuf.c (iobuf_open_fd_or_name): Fix type of FD in function + declaration. + +2009-12-07 Werner Koch + + * pka.c (get_pka_info): Add support for ADNS. + * src.v (getsrv): Add support for ADNS. + + * srv.c (getsrv): s/xrealloc/xtryrealloc/. + +2009-12-04 Werner Koch + + * Makefile.am (audit-events.h, status-codes.h): Create files in + the source dir. Fixes bug#1164. + +2009-12-02 Werner Koch + + * audit.c (proc_type_decrypt, proc_type_sign): Implemented. + (proc_type_verify): Print hash algo infos. + * audit.h (AUDIT_DATA_CIPHER_ALGO, AUDIT_BAD_DATA_CIPHER_ALSO) + (AUDIT_NEW_RECP, AUDIT_DECRYPTION_RESULT, AUDIT_RECP_RESULT) + (AUDIT_ATTR_HASH_ALGO, AUDIT_SIGNED_BY, AUDIT_SIGNING_DONE): + +2009-11-05 Marcus Brinkmann + + * asshelp.c (start_new_gpg_agent): Update use of + assuan_socket_connect and assuan_pipe_connect. + +2009-11-02 Marcus Brinkmann + + * get-passphrase.c (default_inq_cb, membuf_data_cb): Change return + type to gpg_error_t. + +2009-10-28 Werner Koch + + * status.h (STATUS_MOUNTPOINT): New. + +2009-10-16 Marcus Brinkmann + + * Makefile.am (libcommon_a_CFLAGS): Use LIBASSUAN_CFLAGS instead + of LIBASSUAN_PTH_CFLAGS. + +2009-10-13 Werner Koch + + * exechelp.c (gnupg_kill_process): New. + +2009-09-29 Werner Koch + + * exechelp.c (create_inheritable_pipe): Rename to + create_inheritable_pipe_w. + (create_inheritable_pipe_r): New. + (gnupg_create_outbound_pipe): New. + + * iobuf.h: Include "sysutils.h" + + * iobuf.c (iobuf_open_fd_or_name): New. + (iobuf_get_fname_nonnull): New. + +2009-09-23 Marcus Brinkmann + + * asshelp.c (start_new_gpg_agent): Allocate assuan context before + starting server. + +2009-09-03 Werner Koch + + Update from libestream: + * estream-printf.c: Include stdint.h only if HAVE_STDINT_H is + defined. + * estream-printf.c: Remove all test code. Use macro DEBUG instead + of TEST for debugging. + * estream-printf.c (pr_float): Make buffer larger for silly high + numbers. + +2009-08-11 David Shaw + + * ttyio.h, ttyio.c (tty_enable_completion): Some ifdefs around + HAVE_LIBREADLINE to allow building when readline isn't available. + +2009-08-06 Werner Koch + + * status.h (STATUS_INV_SGNR, STATUS_NO_SGNR): New. + * status.c (get_inv_recpsgnr_code): New. + +2009-07-23 David Shaw + + * srv.c (getsrv): Fix type-punning warning. + +2009-07-23 Werner Koch + + * util.h (GPG_ERR_NOT_ENABLED): New. + * audit.h (enum): Add AUDIT_CRL_CHECK. + * audit.c (proc_type_verify): Show CRL check result. + +2009-07-06 Werner Koch + + * get-passphrase.c (struct agentargs): Add SESSION_ENV and remove + obsolete args. + (gnupg_prepare_get_passphrase): Ditto. + + * session-env.c, session-env.h: New. + * t-session-env.c: New. + * Makefile.am (common_sources, module_tests): Add them. + * asshelp.h: Include "session-env.h" + * asshelp.c (send_one_option): Add arg PUTENV. + (send_pinentry_environment): Replace most args by SESSION_ENV and + rewrite fucntion. + (start_new_gpg_agent): Likewise. + + * t-exechelp.c (test_close_all_fds): Remove debug code. + +2009-07-01 Werner Koch + + * sexputil.c (get_pk_algo_from_canon_sexp): New. + +2009-06-29 Werner Koch + + * estream.c (BUFFER_ROUND_TO_BLOCK): Remove unused macro. + (es_func_mem_write): Rewrite reallocation part. + + * estream.c (es_write_sanitized_utf8_buffer): Typo typo fix. + +2009-06-25 Werner Koch + + * estream.c (es_write_sanitized_utf8_buffer): Typo fix. + +2009-06-24 Werner Koch + + * estream.c (es_read_line): In the malloc error case, set + MAX_LENGTH to 0 only if requested. + * xreadline.c (read_line): Ditto. + * estream.c (es_write_sanitized_utf8_buffer): Pass on error from + es_fputs. + * sexputil.c (get_rsa_pk_from_canon_sexp): Check for error after + the loop. Reported by Fabian Keil. + +2009-06-22 Werner Koch + + * estream.c (es_pth_read, es_pth_write) [W32]: New. + (ESTREAM_SYS_READ, ESTREAM_SYS_WRITE) [HAVE_PTH]: Use them. + +2009-06-03 Werner Koch + + * estream.c (es_convert_mode): Rewrite and support the "x" flag. + +2009-05-28 David Shaw + + From 1.4: + + * http.h, http.c (send_request) Pass in a STRLIST for additional + headers. Change all callers. + +2009-05-27 David Shaw + + From 1.4: + + * http.h, http.c (send_request): Pass in srvtag and make its + presence sufficient to turn the feature on. + (http_open): From here. + (http_document): And here. + + * srv.c (getsrv): Raise maximum packet size to 2048, as PACKETSZ + is too small these days. + +2009-05-22 Werner Koch + + * ttyio.c (tty_cleanup_after_signal): New. + +2009-05-19 Werner Koch + + * simple-pwquery.c (agent_open): Use SUN_LEN + (JNLIB_NEED_AFLOCAL): Define and include mischelp.h. + +2009-05-07 Werner Koch + + * sexputil.c (get_rsa_pk_from_canon_sexp): New. + * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): Extend the test. + +2009-04-28 Werner Koch + + * sexputil.c (make_canon_sexp_from_rsa_pk): New. + * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): New. + +2009-04-01 Werner Koch + + * iobuf.c: Port David's changes from 1.4: + (fd_cache_invalidate): Pass return code from close back. + (direct_open, iobuf_ioctl): Check that return value. + (fd_cache_synchronize): New. + (iobuf_ioctl): Add new sub command 4 (fsync). + + * iobuf.c (fd_cache_strcmp): New. Taken from 1.4. + (fd_cache_invalidate, fd_cache_close, fd_cache_open): Use it. + + * exechelp.c (gnupg_spawn_process): Implement new flag bit 6. + * sysutils.c (gnupg_allow_set_foregound_window): Allow the use of + ASFW_ANY. + + * membuf.c (put_membuf, get_membuf): Wipe memory on out of core. + +2009-03-31 Werner Koch + + * percent.c (percent_unescape, percent_plus_unescape): New. + (percent_plus_unescape_inplace, percent_unescape_inplace): New. + (do_plus_or_plain_unescape, count_unescape, do_unescape): New. + (do_unescape_inplace): New. + * t-percent.c (test_percent_plus_escape): Test percent_plus_unescape. + + * get-passphrase.c, get-passphrase.h: New. + * Makefile.am (without_pth_sources): New. + +2009-03-18 Werner Koch + + * exechelp.c: Include sys/resource.h and sys/stat.h. + (get_max_open_fds): New. + (do_exec): Use it. + (get_all_open_fds): New. + (close_all_fds): New. + (do_exec): Use close_all_fds. + * t-exechelp.c: New. + +2009-03-13 David Shaw + + * http.c (do_parse_uri): Properly handle IPv6 literal addresses as + per RFC-2732. Adapted from patch by Phil Pennock. + +2009-03-12 Werner Koch + + * gettime.c: Include i18n.h. + (dump_isotime): New. + +2009-03-06 Werner Koch + + * sexputil.c (make_canon_sexp): New. + +2009-03-03 Werner Koch + + * exechelp.c (do_exec): Make sure that /dev/null connected FDs are + not closed. + +2009-01-19 Werner Koch + + * audit.c (writeout_li): Translate a few more result strings. + Fixes bug#970. + + * convert.c (hex2str): Fix optimization to append a nul character. + +2008-12-05 Werner Koch + + * percent.c, t-percent.c: New. + + * exechelp.c (gnupg_spawn_process, gnupg_spawn_process_fd) + (gnupg_spawn_process_detached) [W32]: Remove debug output. + +2008-11-20 Werner Koch + + * audit.c (writeout_li): Translate OKTEXT. + +2008-11-04 Werner Koch + + * i18n.c (i18n_init) [USE_SIMPLE_GETTEXT]: Adjust for changed + w32-gettext.c. + * homedir.c (gnupg_localedir): New. + +2008-10-20 Werner Koch + + * http.c (http_register_tls_callback) [!HTTP_USE_GNUTLS]: Mark + unused arg. + * localename.c (do_nl_locale_name): Ditto. + * audit.c (event2str): Silent gcc warning. + * sysutils.c (translate_sys2libc_fd): Mark unused arg. + (translate_sys2libc_fd_int): Ditto. + * iobuf.c (translate_file_handle): Ditto. + * asshelp.c (send_one_option): Ditto. + * exechelp.c (gnupg_spawn_process): Ditto. + * signal.c (got_usr_signal): Ditto + * estream.c (es_func_fd_create) [!W32]: Ditto. + (es_func_fp_create) [!W32]: Ditto. + (es_write_hexstring): Ditto. + (dummy_mutex_call_void, dummy_mutex_call_int) [HAVE_PTH]: New. + (ESTREAM_MUTEX_LOCK, ESTREAM_MUTEX_UNLOCK, ESTREAM_MUTEX_TRYLOCK) + (ESTREAM_MUTEX_INITIALIZE) [HAVE_PTH]: Use dummy calls so to mark + unused arg. + +2008-10-19 Werner Koch + + * estream-printf.c (estream_vsnprintf): Fix return value. + (check_snprintf): Add a new test. + (one_test) [W32]: Disable test. + +2008-10-17 Werner Koch + + * util.h (snprintf) [W32]: Redefine to estream_snprintf. + +2008-09-03 Werner Koch + + * convert.c (hex2str): New. + (hex2str_alloc): New. + * t-convert.c (test_hex2str): New. + +2008-08-19 Werner Koch + + * iobuf.c: Avoid passing a NULL (iobuf_t)->desc to the log + function. Should in general never be NULL, but well. Reported by + M. Heneka. + +2008-06-26 Werner Koch + + * estream.c (es_write_sanitized): Loose check for control + characters to better cope with utf-8. The range 0x80..0x9f is + nowadays not anymore accidently used for control charaters. + +2008-06-25 Marcus Brinkmann + + Revert last three changes related to handle translation. + * sysutils.c: + (FD_TRANSLATE_MAX, fd_translate, fd_translate_len) + (translate_table_init, translate_table_lookup): Removed. + * iobuf.c (check_special_filename): Do not use + translate_table_lookup. + * sysutils.h (translate_table_init, translate_table_lookup): + Remove prototypes. + +2008-06-19 Werner Koch + + * sysutils.c: Remove . + (fd_translate_max): Use macro for the size. + (translate_table_init): Protect read against EINTR and replace + isspace by spacep. + +2008-06-18 Marcus Brinkmann + + * sysutils.c (TRANS_MAX): Bump up to 350 to be on the safe side. + + * sysutils.h (translate_table_init, translate_table_lookup): New + prototypes. + * sysutils.c: Include . + (FD_TRANSLATE_MAX): New macro. + (fd_translate, fd_translate_len): New static variables. + (translate_table_init, translate_table_lookup): New functions. + (translate_sys2libc_fd_int): Translate file descriptor. + * iobuf.c (check_special_filename): Translate handle values from + special filenames. + +2008-06-16 Werner Koch + + * homedir.c (w32_commondir): New. + (gnupg_sysconfdir): Use it. + +2008-06-09 Werner Koch + + * b64dec.c: New. + +2008-06-05 Werner Koch + + * util.h (gnupg_copy_time): Replace strcpy by memcpy. + +2008-05-26 Werner Koch + + * asshelp.c (send_one_option, send_pinentry_environment): use + xfree and xtrystrdup. + + * i18n.c (i18n_switchto_utf8) [USE_SIMPLE_GETTEXT]: Return NULL. + + * homedir.c (gnupg_module_name): Add + GNUPG_MODULE_NAME_CONNECT_AGENT and GNUPG_MODULE_NAME_GPGCONF. + +2008-04-21 Werner Koch + + * http.c (http_wait_response) [W32]: Use DuplicateHandle because + it is a socket. + (cookie_read) [W32]: Use recv in place of read. + +2008-04-08 Werner Koch + + * i18n.c (i18n_switchto_utf8, i18n_switchback) + [USE_SIMPLE_GETTEXT]: Implement. + +2008-04-07 Werner Koch + + * b64enc.c (b64enc_start): Detect PGP mode. + (b64enc_finish): Write PGP CRC. + * util.h (struct b64state): Add field CRC. + * t-b64.c: New. + + * pka.c (get_pka_info): Use xtrymalloc and check result. + +2008-03-25 Werner Koch + + * localename.c: Strip all W32 code. Include w32help.h. + (gnupg_messages_locale_name) [W32]: Use the gettext_localename. + +2008-03-17 Werner Koch + + * iobuf.c (IOBUF_BUFFER_SIZE): Actually use this macro. + + * simple-pwquery.c (agent_send_all_options): Fix last change. + +2008-03-06 Werner Koch + + * simple-pwquery.c (agent_send_all_options): Add support for + XAUTHORITY and PINENTRY_USER_DATA. + +2008-02-15 Marcus Brinkmann + + * exechelp.c (gnupg_spawn_process_fd): Add flag DETACHED_PROCESS + unconditionally (required for all callers at the moment). + +2008-02-14 Werner Koch + + * sysutils.c (gnupg_allow_set_foregound_window): New. + (WINVER) [W32]: Define. + +2008-01-31 Werner Koch + + * audit.c (audit_print_result): Make sure that the output is + always UTF8. + +2008-01-27 Werner Koch + + * exechelp.c (gnupg_spawn_process): Add arg FLAGS and changed all + callers to pass 0 for it. + +2007-12-13 Werner Koch + + * sexputil.c (hash_algo_from_sigval): New. + * t-sexputil.c: New. + * Makefile.am (module_tests): Add it. + +2007-12-11 Werner Koch + + * asshelp.c (send_pinentry_environment): Allow using of old + gpg-agents not capabale of the xauthority and pinentry_user_data + options. + +2007-12-04 Werner Koch + + * Makefile.am (t_helpfile_LDADD, module_maint_tests): New. + * t-helpfile.c: New. + * helpfile.c: New. + * membuf.h (is_membuf_ready, MEMBUF_ZERO): New. + * localename.c: New. Taken from gettext with modifications as done + for GpgOL. Export one new function. + * util.h (gnupg_messages_locale_name, gnupg_get_help_string): Added. + + * sysutils.c (gnupg_reopen_std): New. Taken from ../g10/gpg.c. + +2007-11-27 Werner Koch + + * Makefile.am (CLEANFILES): New. + + * homedir.c (dirmngr_socket_name): Use CSIDL_WINDOWS. + +2007-11-15 Werner Koch + + * asshelp.c (send_pinentry_environment): Add args XAUTHORITY and + PINENTRY_USER_DATA. + (start_new_gpg_agent): Ditto. + +2007-11-07 Werner Koch + + * status.h: New. + * errors.h: Remove. + +2007-11-05 Werner Koch + + * audit.c, audit.h: New. + * Makefile.am: Add rules to build audit-events.h. + * exaudit.awk: New. + * mkstrtable.awk: New. Taken from libgpg-error. + +2007-10-19 Werner Koch + + * i18n.c (i18n_switchto_utf8, i18n_switchback): New. + +2007-10-01 Werner Koch + + * sysutils.h (FD2INT, INT2FD): New. + +2007-09-21 Werner Koch + + * homedir.c (default_homedir): Make registry work. Reported by + Marc Mutz. + +2007-08-29 Werner Koch + + * exechelp.c (gnupg_wait_process): Add arg EXITCODE. Changed all + callers. + (gnupg_create_inbound_pipe): New. + * util.h (GNUPG_MODULE_NAME_GPGSM, GNUPG_MODULE_NAME_GPG): New. + * homedir.c (gnupg_module_name): Add them + +2007-08-28 Werner Koch + + * gettime.c (check_isotime, add_isotime): New. Originally written + for DirMngr by me. + (add_days_to_isotime): New. + (date2jd, jd2date, days_per_month, days_per_year): New. Taken from + my ancient (1988) code used in Wedit (time2.c). + +2007-08-27 Werner Koch + + * util.h (GNUPG_MODULE_NAME_CHECK_PATTERN): New. + * homedir.c (gnupg_module_name): Add it. + * exechelp.c (w32_fd_or_null) [W32]: New. + (gnupg_spawn_process_fd): New. + (gnupg_wait_process) [W32]: Close the handle after if the process has + returned. + +2007-08-22 Werner Koch + + Updated estream from libestream. + + * estream.c (mem_malloc, mem_realloc, mem_free): New. Use them + instead of the ES_MEM_foo. + * estream.c (estream_cookie_mem): Remove members DONT_FREE, + APPEND_ZERO, PTR and SIZE. Add MEMORY_LIMIT. Put GROW into a new + FLAGS struct. + (es_func_mem_create): Remove APPEND_ZERO, DONT_FREE, PTR and + SIZE. Add MEMORY_LIMIT. + (es_func_mem_write, es_func_mem_seek, es_func_mem_destroy): Revamp. + (es_open_memstream): Change API to just take a memory limit and a + mode argument. Rename to .. + (es_fopenmem): .. this. + (HAVE_W32_SYSTEM) [_WIN32]: Define if not defined. + (tmpfd) [W32]: Implement directly using the W32 API. + (es_fgets): Rewrite without using doreadline. + +2007-08-21 Werner Koch + + * sysutils.c (gnupg_tmpfile): New. + * t-sysutils.c: New. + * Makefile.am (module_tests): Add t-sysutils. + +2007-08-20 Werner Koch + + * exechelp.c [W32]: Redefine X_OK to F_OK. + +2007-08-16 Werner Koch + + * Makefile.am (t_convert_DEPENDENCIES): Remove + ($(PROGRAMS)): Remove. + (t_common_ldadd): Use libcommon.a and not the macro. + +2007-08-14 Werner Koch + + * homedir.c (dirmngr_socket_name): New. + +2007-08-07 Werner Koch + + * tlv.c, tlv.h: Move from ../scd/. + * tlv.c (parse_sexp, parse_ber_header): Add ERRSOURCE arg and prefix + name with a _. + * tlv.h: Use macro to convey ERRSOURCE. + +2007-08-02 Werner Koch + + * gc-opt-flags.h: New. + +2007-08-01 Werner Koch + + * estream-printf.c (read_dummy_value): Removed as it is useless now. + (read_values): Remove check on !vaargs which is not anymore needed + and anyway not portable. Reported by Peter O'Gorman. + +2007-07-16 Werner Koch + + * estream.c (es_func_file_create): Clear NO_CLOSE flag. + +2007-07-12 Werner Koch + + * sysutils.h (gnupg_fd_t): New. + * sysutils.c (translate_sys2libc_fd): Use that type instead of int. + (translate_sys2libc_fd_int): New. + +2007-07-09 Werner Koch + + * t-gettime.c (test_isotime2epoch): Use time_t and not u32. + +2007-07-05 Werner Koch + + * t-gettime.c: New. + * gettime.c (isotime2epoch, epoch2isotime): New. + +2007-07-04 Werner Koch + + * estream.c (es_init_do): Do not throw an error if pth has already + been initialized. + +2007-06-26 Werner Koch + + * Makefile.am ($(PROGRAMS)): New. + + * util.h (init_common_subsystems): Moved to .. + * init.h: .. New. + * util.h: Include init.h. + + * homedir.c (standard_homedir): New. + (default_homedir) [W32]: Reimplemented in terms of + standard_homedir. Fixed memory leak. + +2007-06-25 Werner Koch + + * iobuf.c: Add more documentation and slighly restructured macro + defintion for better readability. + (FILEP_OR_FD): Rename to fp_or_fd_t. + (CLOSE_CACHE): Rename to close_cache_t. + + * sysutils.c (translate_sys2libc_fd): New using the code from iobuf.c. + * iobuf.c: Include sysutils.h. + (iobuf_translate_file_handle): Remove. + (translate_file_handle): Use new function. + + * estream-printf.c [TEST]: Header including fixes. + (do_format): Do not append a trailing Nul. This avoids spurious + Nuls in the es_printf output. + (estream_vsnprintf, estream_vasprintf): Take this in account. + + * estream.h (struct es__stream): Change FLAGS to a bit structure. + (ES__FLAG_WRITING): Replace by a bit from FLAGS. * estream.c + (struct estream_internal): Rename FLAGS to MODEFLAGS so that they + are not confused with the estream flags. + (es_initialize, es_create): Add arg MODEFLAGS so that we can setup + the intial writemode. Changed all callers to pass them. + (es_convert_mode): Set O_BINARY. + (es_func_fd_create, es_func_fp_create, es_func_file_create) [W32]: + Call setmode if requested. + +2007-06-24 Werner Koch + + * estream.c (do_fpopen, es_fpopen, es_fpopen_nc): New. + (es_func_fp_create, es_func_fp_read, es_func_fp_write) + (es_func_fp_seek, es_func_fp_destroy): New. + +2007-06-22 Werner Koch + + * estream.c (es_fdopen): Factored code out to.. + (do_fdopen): .. new. + (es_fdopen_nc): New. + (estream_cookie_fd): Add field NO_CLOSE. + (es_func_fd_create): Add arg NO_CLOSE and changed all callers. + (es_func_fd_destroy): Handle the new flag. + + * homedir.c (gnupg_libexecdir) [W32]: Factor code out to .. + (w32_rootdir): .. new. + (gnupg_sysconfdir, gnupg_libdir, gnupg_datadir) [W32]: Return + name based on w32_rootdir(). + +2007-06-21 Werner Koch + + * membuf.h (get_membuf_len): New. + + * membuf.c (init_membuf_secure): Really allocate in secure memory. + (put_membuf_str): New. + + * ttyio.c (tty_getf): New. + + * util.h (ctrl_t): Declare it here. + + * asshelp.c (start_new_gpg_agent): New. Based on code from + ../sm/call-agent.c + +2007-06-20 Werner Koch + + * sysutils.c (gnupg_sleep): New. + * sysutils.h [W32]: Remove _sleep wrapper. Changed all callers to + use gnupg_sleep. + + * exechelp.c (build_w32_commandline_copy): New. + (build_w32_commandline): Factored some code out to new function + and correctly process a PGMNAME with spaces. + (gnupg_spawn_process_detached) [W32]: Implement. + +2007-06-14 Werner Koch + + * simple-pwquery.h (MAP_SPWQ_ERROR_IMPL): New. + (SPWQ_NO_PIN_ENTRY): New. + * simple-pwquery.c (simple_pw_set_socket): New. + (agent_open): Use it if GPG_AGENT_INFO is not set. + (simple_pwquery): Extended to allow returning of other error codes. + + * util.h (GNUPG_MODULE_NAME_AGENT, GNUPG_MODULE_NAME_PINENTRY) + (GNUPG_MODULE_NAME_SCDAEMON, GNUPG_MODULE_NAME_DIRMNGR) + (GNUPG_MODULE_NAME_PROTECT_TOOL): New. + * homedir.c (gnupg_module_name): New. + (gnupg_bindir): New. + +2007-06-12 Werner Koch + + * homedir.c (gnupg_sysconfdir): New. + (gnupg_libexecdir): New. Taken from g10/misc.c:get_libexecdir. + (gnupg_datadir): New. + (gnupg_libdir): New. + + * http.c (connect_server) [W32]: Do not call init_sockets if + HTTP_NO_WSASTARTUP is defined. + + * init.c: New. + + * estream.c (es_init_do): Init stream lock here because we can't + use a static initialization with W32pth. + +2007-06-11 Werner Koch + + * Makefile.am (t_common_ldadd): Use libcommonstd macro. + +2007-06-06 Werner Koch + + * Makefile.am: Include am/cmacros.am. + + * sysutils.h [W32]: Remove prototypes for the registry access. + * w32reg.c: Move to ../jnlib/w32-reg.c. + + * i18n.c (i18n_init): New. + + * simple-gettext.c: Remove. + + * iobuf.c (iobuf_get_filelength): Rename SIZE to EXSIZE to silent + shadowing warning. + +2007-06-04 Werner Koch + + * http.c [W32]: Include unistd.h also in this case. + (write_server) [W32]: Fixed error code. + (init_sockets): Fixed syntax error. + (cookie_close): Replace close by sock_close macro. + + * estream.c [w32]: Do not init Mutex. + + * Makefile.am (common_sources) [USE_SNS_SRV]: Build srv.c only + when needed. + + * ttyio.c (init_ttyfp) [W32]: Do not use TTYFP. + + * util.h: Include ../jnlib/dynload.h. + + * dynload.h: Move to ../jnlib. + +2007-05-30 Werner Koch + + * estream.c (MEM_FREE, MEM_ALLOC, MEM_REALLOC): Prefix with ES_ as + windows.h also has such definitions, + +2007-05-15 Werner Koch + + * util.h: Do not include gnulib's vasprintf. Redefine asprintf + and vasprintf. + + * xasprintf.c (xasprintf, xtryasprintf): Use estream_vasprintf. + + * estream-printf.h, estream-printf.c: New. Taken from current + libestream SVN. + * Makefile.am (common_sources): Add them. + +2007-05-14 Werner Koch + + * sexp-parse.h (smklen): New. + * sexputil.c: Include sexp-parse.h. + (make_simple_sexp_from_hexstr): Replace sprintf by smklen. + +2007-05-07 Werner Koch + + * signal.c (got_fatal_signal): Protect SIG from being clobbered by + a faulty signal implementaion. Suggested by James Juran. + +2007-04-25 Werner Koch + + * i18n.h (ngettext): New. + * simple-gettext.c (ngettext): New. + +2007-04-20 Werner Koch + + * miscellaneous.c (my_gcry_logger, my_gcry_outofcore_handler): + Moved from gpg-agent to here. + (my_gcry_fatalerror_handler): new. + (setup_libgcrypt_logging): New. + +2007-03-19 Werner Koch + + * miscellaneous.c (print_hexstring): New. + * estream.c (es_fprintf_unlocked): New. + (es_write_sanitized): New. + (es_write_hexstring): New. + (es_write_sanitized_utf8_buffer) [GNUPG_MAJOR_VERSION]: New. + +2007-03-09 David Shaw + + From STABLE-BRANCH-1-4 + + * http.c (do_parse_uri): Remove the hkp port 11371 detection. We + implement hkp in the keyserver handler, and the support here makes + it appear like a bad hkp request actually succeeded. + +2007-01-31 Werner Koch + + * Makefile.am (t_common_ldadd): Add LIBINCONV and LIBINTL. + +2007-01-25 Werner Koch + + * simple-pwquery.c (simple_pwquery): New arg OPT_CHECK. + +2006-12-13 David Shaw + + * Makefile.am (AM_CPPFLAGS): Include intl/ so we can reference the + built-in headers. + +2006-11-23 Werner Koch + + * http.c: Include i18n.h + +2006-11-21 Werner Koch + + * estream.c: Remove explicit Pth soft mapping diabling becuase it + is now done in config.h. + +2006-11-15 Werner Koch + + * estream.c: Disabled Pth soft mapping. + (my_funopen_hook_ret_t): New. + (print_fun_writer): Use it here. + + * iobuf.c (fd_cache_close): Use %d instead of %p for debug output. + +2006-11-03 Werner Koch + + * Makefile.am (t_convert_DEPENDENCIES): Add libcommon. From + Gentoo. + +2006-10-24 Marcus Brinkmann + + * Makefile.am (libcommon_a_CFLAGS): Add $(LIBASSUAN_CFLAGS). + (libsimple_pwquery_a_CFLAGS): New variable. + +2006-10-20 Werner Koch + + * convert.c (hex2bin): New. + +2006-10-17 Werner Koch + + * estream.c (struct estream_internal, es_initialize) + (es_deinitialize, print_fun_writer, es_print): New and modified + functions to avoid tempfiles for printf style printing. + + * Makefile.am (libcommonpth_a_SOURCES): New. We now build a secon + version of the library with explicit Pth support. + * exechelp.c, estream.c: Make use of WITHOUT_GNU_PTH. + +2006-10-08 Werner Koch + + * gpgrlhelp.c: Trun all functions into dummies if readline is not + available. + +2006-10-06 Werner Koch + + * Makefile.am (AM_CFLAGS): Use PTH version of libassuan. + + * util.h (GNUPG_GCC_A_SENTINEL): Defined for gcc >= 4. + +2006-10-04 David Shaw + + * gpgrlhelp.c: readline requires stdio.h. + +2006-10-04 Werner Koch + + * membuf.c (init_membuf_secure): New. + (put_membuf): Make sure that ERRNO is set even if the underlying + malloc code does not work properly. + (get_membuf): Set ERRNO on error. + (get_membuf): Allow to pass LEN as NULL. + +2006-10-02 Werner Koch + + * iobuf.c (iobuf_unread): Removed. This code is not required. + Also removed the entire unget buffer stuff. + +2006-09-27 Werner Koch + + * util.h: Do not include strsep.h and strpbrk.h. + (isascii): Removed as it is now in jnlib. + + * iobuf.c (pop_filter, underflow, iobuf_close): Free the unget + buffer. + +2006-09-27 Florian Weimer (wk) + + * iobuf.c (iobuf_unread): New. + +2006-09-22 Werner Koch + + * i18n.h: Changed license to an all permissive one. + + * ttyio.c (tty_get): We need to use readline too. Added two more + hooks. + +2006-09-21 Werner Koch + + * ttyio.c (tty_private_set_rl_hooks): New. + (tty_enable_completion, tty_disable_completion): Use a hook to + enable readline support. Now always available. + (tty_cleanup_rl_after_signal): New. + + * ttyio.h: Removed readline specific stuff. Included util.h. + * common-defs.h: New. + +2006-09-15 Werner Koch + + * convert.c: New. + (hexcolon2bin): New. + (bin2hex, bin2hexcolon, do_binhex): New. + * t-convert.c: New + +2006-09-14 Werner Koch + + * util.h (out_of_core): Use new gpg_error_from_syserror function. + + * http.c (init_sockets): Changed it to require 2.2 unless it is + build within gnupg 1 where we require 1.1 (and not anymore allow + for 1.0). + +2006-09-07 Werner Koch + + * exechelp.c (gnupg_spawn_process): Factor out post fork code to .. + (do_exec): .. new function. Allow passing of -1 for the fds. + (gnupg_spawn_process): Terminate gcrypt's secure memory in the child. + (gnupg_spawn_process_detached): New. + +2006-09-06 Werner Koch + + * maperror.c: Removed. + + * util.h (out_of_core): New. + +2006-09-04 Werner Koch + + * http.c (http_get_header): New. + (capitalize_header_name, store_header): New. + (parse_response): Store headers away. + (send_request): Return GPG_ERR_NOT_FOUND if connect_server failed. + * http.h: New flag HTTP_FLAG_NEED_HEADER. + +2006-08-21 Werner Koch + + * Makefile.am (libcommon_a_SOURCES): Added keyserver.h + + * openpgpdefs.h: New. Stripped from ..g10/packet.h. + +2006-08-16 Werner Koch + + * keyserver.h: Moved from ../include to here. + + * http.c: Include srv.h. + + * srv.c, srv.h: New. Taken from GnuPG 1.4 + +2006-08-14 Werner Koch + + * http.h (struct http_context_s): Moved to implementation. + * http.c (http_open): Changed call to return a context. + (http_open_document): Ditto. + (http_get_read_ptr, http_get_read_ptr, http_get_status_code): New. + (do_parse_uri): Replaced strlwr by straight code to ease + standalone use of this file. + (http_wait_response): Removed arg STATUS_CODE as it is available + through an accessor function. Adjusted caller. + (http_escape_string): New. + + * estream.c (es_read_line): Renamed to .. + (doreadline): .. this. Changed all callers. + (es_read_line): New. This is theusual limited getline variabnt as + used at several places. Here taken and adjusted from xreadline.c + (es_free): New. + +2006-08-11 Werner Koch + + * http.c: Major internal changes to optionallly support GNUTLS and + ESTREAM. + (http_open): Move initialization of the stream ... + (send_request): .. here. + (http_register_tls_callback): New. + + * estream.c (es_writen): Try to seek only is a seek function has + been registered. + +2006-08-09 Werner Koch + + * http.c, http.h: New. Taken from gnupg 1.4.5, merged with + changes done for the Dirmngr project (by g10 Code) and cleaned up + some stuff. + (make_header_line): New. Change all caller to make user of the new + * Makefile.am (libcommon_a_SOURCES): Added http.c and http.h. + +2006-05-23 Werner Koch + + * gettime.c (isotimestamp): New. + + * ttyio.c (tty_get_ttyname): Posixly correct usage of ctermid. + + * dns-cert.c: New. Taken from 1.4.3's util/cert.c. + * dns-cert.h: New. + +2006-05-22 Werner Koch + + * pka.c: New. Taked from 1.4.3. + * pka.h: New. + * Makefile.am: Added pka. + +2006-05-19 Werner Koch + + * yesno.c (answer_is_yes_no_default, answer_is_yes_no_quit): + Updated from 1.4.3. + (answer_is_okay_cancel): new. From 1.4.3. + + * miscellaneous.c (match_multistr): New. Taken from 1.4.3. + + * ttyio.c (tty_enable_completion, tty_disable_completion): New + dummy functions. + * ttyio.h: Add prototypes and stubs. + +2006-04-19 Werner Koch + + * iobuf.c (iobuf_get_fd): New. Taken from 1.4.3. + (iobuf_is_pipe_filename): New. + (pop_filter): Made static. + (iobuf_skip_rest): New. Orginal patch by Florian + Weimer. Added new argument PARTIAL. + (block_filter): Remove the old gpg indeterminate length mode. + (block_filter): Properly handle a partial body stream + that ends with a 5-byte length that happens to be zero. + (iobuf_set_block_mode, iobuf_in_block_mode): Removed as + superfluous. + (iobuf_get_filelength): New arg OVERFLOW. + (iobuf_get_filelength) [W32]: Use GetFileSizeEx if available + * miscellaneous.c (is_file_compressed): Take care of OVERFLOW. + +2006-04-18 Werner Koch + + * homedir.c (w32_shgetfolderpath): New. Taken from gpg 1.4.3. + (default_homedir): Use it. + +2005-10-08 Marcus Brinkmann + + * signal.c (get_signal_name): Check value of HAVE_DECL_SYS_SIGLIST + instead of just if it is defined. + +2005-09-28 Marcus Brinkmann + + * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS). + +2005-07-04 Marcus Brinkmann + + * simple-pwquery.h (simple_pwclear): New prototype. + * simple-pwquery.c (simple_pwclear): New function. + +2005-06-15 Werner Koch + + * miscellaneous.c (make_printable_string): Made P a void*. + + * sexputil.c (keygrip_from_canon_sexp, cmp_simple_canon_sexp): + Fixed signed/unsigned pointer mismatch. + (make_simple_sexp_from_hexstr): Ditto. This is all too ugly; I + wonder why gcc-4's default is to warn about them and forcing us to + use cast the warning away. + * iobuf.c (block_filter): Ditto. + (iobuf_flush): Ditto. + (iobuf_read_line): Ditto. + (iobuf_read): Make BUFFER a void *. + (iobuf_write): Make BUFFER a const void *. + * ttyio.c (tty_print_utf8_string2): Ditto. + * estream.c (estream_cookie_mem): Make MEMORY unsigned char*. + (es_write): Make BUFFER a void *. + (es_writen): Ditto. + (es_func_fd_read, es_func_fd_write, es_func_mem_read) + (es_func_mem_write): Ditto. + (es_read, es_readn): Ditto. + (es_func_mem_write): Made MEMORY_NEW an unsigned char *. + * estream.h (es_cookie_read_function_t) + (es_cookie_write_function_t): Changed buffer arg to void*. + +2005-06-03 Werner Koch + + * estream.c: Use HAVE_CONFIG_H and not USE_CONFIG_H! + (es_func_fd_read, es_func_fd_write): Protect against EINTR. + +2005-06-01 Werner Koch + + * Makefile.am (AM_CPPFLAGS): Added. + + * util.h: Add some includes for gnulib. + (ttyname, isascii): Define them inline. + * fseeko.c, ftello.c: Removed. + * strsep.c, mkdtemp.c: Removed. + * ttyname.c, isascii.c: Removed. + +2005-05-31 Werner Koch + + * dynload.h: s/__inline__/inline/. + +2005-05-13 Werner Koch + + * signal.c (got_fatal_signal): Print the signal number if we can't + get a name for it. + (get_signal_name): Return NULL if no name is available. Fixed + conditional for sys_siglist to the correct one. + +2005-04-17 Werner Koch + + * sexputil.c (cmp_simple_canon_sexp): New. + (make_simple_sexp_from_hexstr): New. + +2005-04-07 Werner Koch + + * sexputil.c: New. + +2005-04-11 Marcus Brinkmann + + * simple-pwquery.c (simple_pwquery): Use spwq_secure_free. + +2005-03-03 Werner Koch + + * Makefile.am (AM_CFLAGS): Added PTH_CFLAGS. Noted by Kazu Yamamoto. + +2005-02-25 Werner Koch + + * xasprintf.c (xtryasprintf): New. + +2005-01-26 Moritz Schulte + + * Makefile.am (libcommon_a_SOURCES): New source files: estream.c, + estream.h. + * estream.c, estream.h: New files. + +2005-01-03 Werner Koch + + * asshelp.c (send_pinentry_environment): Fixed changed from + 2004-12-18; cut+paste error for lc-messages. + +2004-12-21 Werner Koch + + * simple-pwquery.c (agent_open) [W32]: Implement for W32. + (readline) [W32]: Use recv instead of read. + (writen) [W32]: Use send instead of write. + (my_stpcpy): Define a stpcpy replacement so that this file + continues to be self-contained. + (agent_send_all_options) [W32]: Don't call ttyname. + +2004-12-21 Marcus Brinkmann + + * simple-pwquery.h (simple_query): Add prototype. + * simple-pwquery.c (simple_query): New function. + +2004-12-21 Werner Koch + + * signal.c (got_fatal_signal, got_usr_signal) + (got_fatal_signal) [DOSISH]: Don't build. + * simple-gettext.c: Include sysutils.h + + * homedir.c: New. Use CSIDL_APPDATA for W32 as the default home + directory. + * Makefile.am (libcommon_a_SOURCES): Add it. + (EXTRA_DIST): Removed mkerror and mkerrtok. + +2004-12-20 Werner Koch + + * sysutils.h [W32]: Define sleep. + * util.h: Add prototype for mkdtemp. + + * membuf.c (put_membuf): Wipe out buffer after a failed realloc. + +2004-12-19 Werner Koch + + * maperror.c (map_assuan_err_with_source): Oops, args were swapped. + +2004-12-18 Werner Koch + + * maperror.c (map_assuan_err): Renamed to .. + (map_assuan_err_with_source): .. this and add arg SOURCE.c + * asshelp.c (send_pinentry_environment, send_one_option): Add arg + ERRSOURCE. + +2004-12-15 Werner Koch + + * sysutils.h [W32]: Prototypes for registry functions. + * w32reg.c: Include sysutils.h + + * simple-pwquery.c [W32]: Dummy code to allow a build. + + * exechelp.c [W32]: Implemented for W32 . + + * ttyname.c: New. + + * asshelp.c (send_one_option): New. + (send_pinentry_environment): Cleaned up and made sure that empty + values are not send. + +2004-12-07 Werner Koch + + * asshelp.c (send_pinentry_environment) [W32]: Do not use ttyname. + +2004-12-06 Werner Koch + + * exechelp.h, exechelp.c: New. Based on code from ../sm/import.c. + +2004-12-03 Werner Koch + + * strsep.c: Fixed copyright comments. + +2004-11-26 Werner Koch + + * simple-gettext.c: New taken from gnupg 1.3.x + + * simple-pwquery.c [_WIN32]: Include winsock2.h. + (agent_open): Disable it until we have our AF_UNIX implementation + ready. + * fseeko.c, ftello.c: Include sys/types for the sake of W32. + +2004-11-23 Werner Koch + + * b64enc.c: Include stdio.h and string.h + +2004-08-18 Werner Koch + + * simple-pwquery.c (simple_pwquery): Handle gpg-error style return + code for canceled. + +2004-07-20 Werner Koch + + * maperror.c: Removed header ksba.h. Not required anymore. + +2004-06-14 Werner Koch + + * xreadline.c: New. Based on the iobuf_read_line function. + +2004-05-12 Werner Koch + + * util.h (xtrycalloc_secure,xtrymalloc_secure): New. + +2004-05-11 Werner Koch + + * sysutils.c (disable_core_dumps): Only set the current limit. + (enable_core_dumps): New. + +2004-04-13 Werner Koch + + * simple-pwquery.c (copy_and_escape): Relaxed quoting. + +2004-04-05 Werner Koch + + * errors.h (STATUS_NEWSIG): New. + +2004-03-11 Werner Koch + + * dynload.h [__MINGW32__]: Define RTLD_LAZY. + +2004-03-09 Werner Koch + + * maperror.c (map_assuan_err): Map the Locale_Problem item. + +2004-03-03 Werner Koch + + * asshelp.c, asshelp.h: New. + (send_pinentry_environment): New. Code taken from ../sm/call-agent.c. + +2004-02-19 Werner Koch + + * simple-pwquery.c (agent_open): Don't mangle INFOSTR. + +2004-02-17 Werner Koch + + * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO. + + * errors.h: Added STATUS_IMPORT_OK. + +2004-02-10 Werner Koch + + * b64enc.c: New. Based on code from ../sm/base64.c. + +2004-01-30 Marcus Brinkmann + + * Makefile.am (libcommon_a_SOURCES): Add xasprintf.c. + * miscellaneous.c (xasprintf): Moved to ... + * xasprintf (xasprintf): ... here. New file. + This allows to use xasprintf without sucking in gpg-error. + +2004-01-27 Werner Koch + + * sexp-parse.h: New; moved from../agent. + + * util.h (xtoi_4): New. + +2003-12-23 Werner Koch + + * maperror.c (map_assuan_err): Prepared for a new error code. + +2003-12-17 Werner Koch + + * gettime.c (asctimestamp): Add a note on a non-avoidable gcc warning. + + * util.h [!HAVE_VASPRINTF]: Add printf format attribute to the + replacement function. + + * miscellaneous.c (xasprintf): New. + +2003-11-14 Werner Koch + + * mkdtemp.c (mkdtemp): Use gcry_create_nonce. + + * cryptmiss.c: Removed. + +2003-11-13 Werner Koch + + * util.h (vasprintf): Also fixed the prototype. + + * vasprintf.c (vasprintf): ARGS should not be a pointer. Fixed + segv on Solaris. Reported by Andrew J. Schorr. + +2003-11-12 Werner Koch + + * maperror.c (map_ksba_err, map_gcry_err, map_kbx_err): Removed. + +2003-10-31 Werner Koch + + * util.h (gnupg_isotime_t): New. + (gnupg_copy_time): New. + + * gettime.c (gnupg_get_isotime): New. + +2003-09-23 Werner Koch + + * iobuf.c (check_special_filename): Replaced is isdigit by digitp + to avoid passing negative values and potential locale problems. + Problem noted by Christian Biere. + + * util.h (ascii_isspace): New. + +2003-09-18 Werner Koch + + * ttyio.c (tty_fprintf): New. + (tty_print_string, tty_print_utf8_string2) + (tty_print_utf8_string): Made P argument const byte*. + +2003-08-20 Marcus Brinkmann + + * maperror.c (map_ksba_err): Map -1. Use gpg_err_make to set + the error source. + +2003-08-14 Timo Schulz + + * dynload.h. New. W32 wrapper around the dynload mechanism. + +2003-07-15 Werner Koch + + * simple-pwquery.c, simple-pwquery.h: New; moved from ../agent. + * Makefile.am (libsimple_pwquery_a_LIBADD): New. + +2003-06-25 Werner Koch + + * maperror.c (map_to_assuan_status): Directly map 0 to 0. + +2003-06-17 Werner Koch + + * gettime.c (scan_isodatestr,add_days_to_timestamp,strtimevalue) + (strtimestamp,asctimestamp): New. Code taken from gnupg 1.3.2 + mischelp.c. + + * yesno.c: New. Code taken from gnupg 1.3.2 mischelp.c + + * miscellaneous.c: New. + + * util.h: Include utf8conf.h + +2003-06-16 Werner Koch + + * gettime.c (make_timestamp): New. + + * ttyio.c: New. Taken from gnupg 1.2. + * ttyio.h: Move from ../include. + +2003-06-13 Werner Koch + + * util.h (seterr): Removed macro. + (xmalloc_secure,xcalloc_secure): New. + +2003-06-11 Werner Koch + + * iobuf.c (iobuf_writebyte,iobuf_write): Return error code from + iobuf_flush. + (iobuf_writestr): Ditto. + +2003-06-10 Werner Koch + + * iobuf.c, iobuf.h: New. Taken from current gnupg 1.3 CVS. Run + indent on it and adjusted error handling to libgpg-error style. + Replaced IOBUF by iobuf_t. Renamed malloc functions. + +2003-06-04 Werner Koch + + * errors.h: Removed all error codes. We keep the status codes for + now. + * Makefile.am: Do not create errors.c anymore; remove it from the + sources. + + * maperror.c: Don't include error.h. Change all error codes to + libgpg-error style. + (map_assuan_err): Changed to new Assuan error code convention. + (map_to_assuan_status): Likewise. + (map_gcry_err,map_kbx_err): Not needed. For now dummy functions. + + * membuf.c, membuf.h: New. Code taken from ../sm/call-agent.h. + * Makefile.am: Added above. + +2003-04-29 Werner Koch + + * util.h (fopencokokie): Removed prototype and struct. + + * fopencookie.c: Removed. + + * maperror.c: Use system assuan.h + +2002-10-31 Neal H. Walfield + + * isascii.c: New file. + * putc_unlocked.c: Likewise. + +2002-10-28 Neal H. Walfield + + * signal.c (caught_fatal_sig): Remove superfluous zero + initializer. + (caught_sigusr1): Likewise. + +2002-09-04 Neal H. Walfield + + * vasprintf.c (vasprintf) [va_copy]: Use va_copy. + [!va_copy && __va_copy]: Use __va_copy. + [!va_copy && !__va_copy]: Only now fall back to using memcpy. + +2002-08-21 Werner Koch + + * errors.h: Added STATUS_IMPORT_PROBLEM. + +2002-08-20 Werner Koch + + * vasprintf.c: Hack to handle NULL for %s. + +2002-08-09 Werner Koch + + * signal.c: New. Taken from GnuPG 1.1.91. + +2002-07-23 Werner Koch + + * util.h (_IO_cookie_io_functions_t): Fixed typo. Noted by + Richard Lefebvre. + +2002-07-22 Werner Koch + + * fseeko.c, ftello.c: New. + +2002-06-28 Werner Koch + + * maperror.c (map_to_assuan_status): Map more errorcodes to Bad + Certificate. + +2002-06-26 Werner Koch + + * maperror.c (map_to_assuan_status): Map EOF to No_Data_Available. + +2002-06-10 Werner Koch + + * errors.h (gnupg_error_token): Add new prototype. + (STATUS_ERROR): New. + + * mkerrtok: New. + * Makefile.am: Use it to create the new error token function. + +2002-06-04 Werner Koch + + * maperror.c (map_to_assuan_status): Map Bad_CA_Certificate. + +2002-05-23 Werner Koch + + * no-pth.c, Makefile.am: Removed. + +2002-05-22 Werner Koch + + * mkdtemp.c: Replaced byte by unsigned char because it is no longer + defined in gcrypt.h. + +2002-05-21 Werner Koch + + * maperror.c (map_gcry_err): Add libgcrypt's new S-expression errors. + (map_ksba_err): Add a few mappings. + +2002-05-14 Werner Koch + + * gettime.c: New. + +2002-05-03 Werner Koch + + * errors.h: Added STARUS_EXPSIG and STATUS_EXPKEYSIG. + +2002-04-15 Werner Koch + + * cryptmiss.c: New. + +2002-02-14 Werner Koch + + * maperror.c: Add more assuan<->gnupg mappings. + +2002-02-12 Werner Koch + + * fopencookie.c: Dummy function. + + * vasprintf.c: New. Taken from binutils-2.9.1 and dropped all non + ANSI-C stuff. Merged with asprintf version. + + * no-pth.c: New. + +2002-01-23 Werner Koch + + * mkdtemp.c: Copied from gnupg-1.0.6c and changed to use libgcrypt. + +2002-01-19 Werner Koch + + * sysutils.c: New. This is the misc.c file from gnupg 1.0.6 with + the OpenPGP stuff removed. + * sysutils.h: New. + +2002-01-15 Werner Koch + + * maperror.c: Add mapping for Not_Trusted. + +2002-01-11 Werner Koch + + * maperror.c (map_assuan_err): Codes for CRL + +2002-01-08 Werner Koch + + * util.h (spacep): New. + +2002-01-02 Werner Koch + + * maperror.c (map_to_assuan_status): New. Merged from ../agent + and ../sm. + +2001-12-20 Werner Koch + + * maperror.c (map_gcry_err): Add some mappings. + +2001-12-18 Werner Koch + + * Makefile.am (AM_CPPFLAGS): Include flags for gcrypt and ksba + +2001-12-14 Werner Koch + + * util.h (digitp, hexdigitp): New ctype like macros. + (atoi_1,atoi_2,atoi_4,xtoi_1,xtoi_2): New. + + + Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009, 2010, 2011 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/common/ChangeLog-2011.include b/common/ChangeLog-2011.include new file mode 100644 index 0000000..b5a9a5e --- /dev/null +++ b/common/ChangeLog-2011.include @@ -0,0 +1,458 @@ +# This is the ChangeLog-2011 from the former ../include directory. It +# was moved to here after the removal of the directory on 2014-01-29. + +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-02-01 Werner Koch + + * cipher.h (PUBKEY_MAX_NPKEY, PUBKEY_MAX_NSKEY): Bump up to + accommodate gcrypt ECC keys. + +2011-01-21 Werner Koch + + * cipher.h (GCRY_PK_USAGE_CERT): Remove compatibility macros + because we now require libgcrypt 1.4.6. + (GCRY_PK_ECDH): Add replacement. + +2009-08-20 Daiki Ueno (wk) + + * cipher.h (struct DEK): Add field S2K_CACHEID. + +2008-04-18 Werner Koch + + * cipher.h (CIPHER_ALGO_CAMELLIA256): Change ID to 13. + (CIPHER_ALGO_CAMELLIA192): New. + +2007-12-12 Werner Koch + + * cipher.h (CIPHER_ALGO_CAMELLIA128, CIPHER_ALGO_CAMELLIA256): New. + +2006-09-20 Werner Koch + + * errors.h, http.h, memory.h, mpi.h, util.h, i18n.h: Removed. + * Makefile.am: New. + * distfiles: Removed. + +2006-08-16 Werner Koch + + * keyserver.h: Moved to ../common. + * http.h: Retired. + +2006-04-28 Werner Koch + + * cipher.h (DIGEST_ALGO_SHA224): Define it. + +2006-04-18 Werner Koch + + * keyserver.h, i18n.h, http.h, cipher.h: Updated to gpg 1.4.3. + +2003-09-04 David Shaw + + * cipher.h: Drop TIGER/192 support. + + * types.h: Prefer using uint64_t when creating a 64-bit unsigned + type. This avoids a warning on compilers that support but complain + about unsigned long long. + + * util.h: Make sure that only ascii is passed to isfoo + functions. (From Werner on stable branch). + +2003-09-04 Werner Koch + + * cipher.h (PUBKEY_USAGE_AUTH): Added. + +2003-07-03 Werner Koch + + * cipher.h (DBG_CIPHER,g10c_debug_mode): Removed. + +2003-06-11 Werner Koch + + * cipher.h: Include gcrypt.h and mapped cipher algo names to + gcrypt ones. Removed twofish_old and skipjack. Removed all + handle definitions and other raerely used stuff. This file will + eventually be entirely removed. + +2003-06-10 Werner Koch + + * types.h (struct strlist): Removed. + +2003-05-24 David Shaw + + * cipher.h, i18n.h, iobuf.h, memory.h, mpi.h, types.h, util.h: + 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. + +2003-05-14 David Shaw + + * types.h: Add initializer macros for 64-bit unsigned type. + +2003-05-02 David Shaw + + * cipher.h: Add constants for compression algorithms. + +2003-03-11 David Shaw + + * http.h: Add HTTP_FLAG_TRY_SRV. + +2003-02-11 David Shaw + + * types.h: Try and use uint64_t for a 64-bit type. + +2003-02-04 David Shaw + + * cipher.h: Add constants for new SHAs. + +2002-11-13 David Shaw + + * util.h [__CYGWIN32__]: Don't need the registry prototypes. From + Werner on stable branch. + +2002-11-06 David Shaw + + * util.h: Add wipememory2() macro (same as wipememory, but can + specify the byte to wipe with). + +2002-10-31 Stefan Bellon + + * util.h [__riscos__]: Prefixed all RISC OS prototypes with + riscos_* + + * zlib-riscos.h: New. This is macro magic in order to make the + zlib library calls indeed call the RISC OS ZLib module. + +2002-10-31 David Shaw + + * util.h: Add wipememory() macro. + +2002-10-29 Stefan Bellon + + * util.h: Added parameter argument to make_basename() needed for + filetype support. + [__riscos__]: Added prototype. + +2002-10-28 Stefan Bellon + + * util.h [__riscos__]: Added prototypes for new filetype support. + +2002-10-19 David Shaw + + * distfiles, _regex.h: Add _regex.h from glibc 2.3.1. + +2002-10-14 David Shaw + + * keyserver.h: Go to KEYSERVER_PROTO_VERSION 1. + +2002-10-08 David Shaw + + * keyserver.h: Add new error code KEYSERVER_UNREACHABLE. + +2002-10-03 David Shaw + + * util.h: Add new log_warning logger command which can be switched + between log_info and log_error via log_set_strict. + +2002-09-24 David Shaw + + * keyserver.h: Add some new error codes for better GPA support. + +2002-09-10 Werner Koch + + * mpi.h (mpi_is_protected, mpi_set_protect_flag) + (mpi_clear_protect_flag): Removed. + (mpi_get_nbit_info, mpi_set_nbit_info): Removed. + +2002-08-13 David Shaw + + * cipher.h: Add AES aliases for RIJNDAEL algo numbers. + +2002-08-07 David Shaw + + * cipher.h: Add md_algo_present(). + +2002-08-06 Stefan Bellon + + * util.h [__riscos__]: Added riscos_getchar(). + +2002-06-21 Stefan Bellon + + * util.h [__riscos__]: Further moving away of RISC OS specific + stuff from general code. + +2002-06-20 Stefan Bellon + + * util.h [__riscos__]: Added riscos_set_filetype(). + +2002-06-14 David Shaw + + * util.h: Add pop_strlist() from strgutil.c. + +2002-06-07 Stefan Bellon + + * util.h [__riscos__]: RISC OS needs strings.h for strcasecmp() + and strncasecmp(). + +2002-05-22 Werner Koch + + * util.h: Add strncasecmp. Removed stricmp and memicmp. + +2002-05-10 Stefan Bellon + + * mpi.h: New function mpi_debug_alloc_like for M_DEBUG. + + * util.h [__riscos__]: Make use of __func__ that later + Norcroft compiler provides. + + * memory.h: Fixed wrong definition of m_alloc_secure_clear. + +2002-04-23 David Shaw + + * util.h: New function answer_is_yes_no_default() to give a + default answer. + +2002-04-22 Stefan Bellon + + * util.h [__riscos__]: Removed riscos_open, riscos_fopen and + riscos_fstat as those special versions aren't needed anymore. + +2002-02-19 David Shaw + + * keyserver.h: Add KEYSERVER_NOT_SUPPORTED for unsupported actions + (say, a keyserver that has no way to search, or a readonly + keyserver that has no way to add). + +2002-01-02 Stefan Bellon + + * util.h [__riscos__]: Updated prototype list. + + * types.h [__riscos__]: Changed comment wording. + +2001-12-27 David Shaw + + * KEYSERVER_SCHEME_NOT_FOUND should be 127 to match the POSIX + system() (via /bin/sh) way of signaling this. + + * Added G10ERR_KEYSERVER + +2001-12-27 Werner Koch + + * util.h [MINGW32]: Fixed name of include file. + +2001-12-22 Timo Schulz + + * util.h (is_file_compressed): New. + +2001-12-19 Werner Koch + + * util.h [CYGWIN32]: Allow this as an alias for MINGW32. Include + stdarg.h becuase we use the va_list type. By Disastry. + +2001-09-28 Werner Koch + + * cipher.h (PUBKEY_USAGE_CERT): New. + +2001-09-07 Werner Koch + + * util.h: Add strsep(). + +2001-08-30 Werner Koch + + * cipher.h (DEK): Added use_mdc. + +2001-08-24 Werner Koch + + * cipher.h (md_write): Made buf arg const. + +2001-08-20 Werner Koch + + * cipher.h (DEK): Added algo_info_printed; + + * util.h [__riscos__]: Added prototypes and made sure that we + never use __attribute__. + * cipher.h, iobuf.h, memory.h, mpi.h [__riscos__]: extern hack. + * i18n.h [__riscos__]: Use another include file + +2001-05-30 Werner Koch + + * ttyio.h (tty_printf): Add missing parenthesis for non gcc. + * http.h: Removed trailing comma to make old ccs happy. Both are + by Albert Chin. + +2001-05-25 Werner Koch + + * ttyio.h (tty_printf): Add printf attribute. + +2001-04-23 Werner Koch + + * http.h: New flag HTTP_FLAG_NO_SHUTDOWN. + +2001-04-13 Werner Koch + + * iobuf.h: Removed iobuf_fopen. + +2001-03-01 Werner Koch + + * errors.h (G10ERR_UNU_SECKEY,G10ERR_UNU_PUBKEY): New + +2000-11-30 Werner Koch + + * iobuf.h (iobuf_translate_file_handle): Add prototype. + +2000-11-11 Paul Eggert + + * iobuf.h (iobuf_get_filelength): Now returns off_t, not u32. + (struct iobuf_struct, iobuf_set_limit, + iobuf_tell, iobuf_seek): Use off_t, not ulong, for file offsets. + +2000-10-12 Werner Koch + + * mpi.h: Changed the way mpi_limb_t is defined. + +Wed Sep 6 17:55:47 CEST 2000 Werner Koch + + * iobuf.c (IOBUF_FILELENGTH_LIMIT): New. + +2000-03-14 14:03:43 Werner Koch (wk@habibti.openit.de) + + * types.h (HAVE_U64_TYPEDEF): Defined depending on configure test. + +Thu Jan 13 19:31:58 CET 2000 Werner Koch + + * types.h (HAVE_U64_TYPEDEF): Add a test for _LONGLONG which fixes + this long living SGI bug. Reported by Alec Habig. + +Sat Dec 4 12:30:28 CET 1999 Werner Koch + + * iobuf.h (IOBUFCTRL_CANCEL): Nww. + +Mon Oct 4 21:23:04 CEST 1999 Werner Koch + + * errors.h (G10ERR_NOT_PROCESSED): New. + +Wed Sep 15 16:22:17 CEST 1999 Werner Koch + + + * i18n.h: Add support for simple-gettext. + +Tue Jun 29 21:44:25 CEST 1999 Werner Koch + + + * util.h (stricmp): Use strcasecmp as replacement. + +Sat Jun 26 12:15:59 CEST 1999 Werner Koch + + + * cipher.h (MD_HANDLE): Assigned a structure name. + +Fri Apr 9 12:26:25 CEST 1999 Werner Koch + + * cipher.h (BLOWFISH160): Removed. + +Tue Apr 6 19:58:12 CEST 1999 Werner Koch + + * cipher.h (DEK): increased max. key length to 32 bytes + + +Sat Feb 20 21:40:49 CET 1999 Werner Koch + + * g10lib.h: Removed file and changed all files that includes this. + +Tue Feb 16 14:10:02 CET 1999 Werner Koch + + * types.h (STRLIST): Add field flags. + +Wed Feb 10 17:15:39 CET 1999 Werner Koch + + * cipher.h (CIPHER_ALGO_TWOFISH): Chnaged ID to 10 and renamed + the old experimenatl algorithm to xx_OLD. + +Thu Jan 7 18:00:58 CET 1999 Werner Koch + + * cipher.h (MD_BUFFER_SIZE): Removed. + +Mon Dec 14 21:18:49 CET 1998 Werner Koch + + * types.h: fix for SUNPRO_C + +Tue Dec 8 13:15:16 CET 1998 Werner Koch + + * mpi.h (MPI): Changed the structure name to gcry_mpi and + changed all users. + +Tue Oct 20 11:40:00 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.h (iobuf_get_temp_buffer): New. + +Tue Oct 13 12:40:48 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.h (iobuf_get): Now uses .nofast + (iobuf_get2): Removed. + +Mon Sep 14 09:17:22 1998 Werner Koch (wk@(none)) + + * util.h (HAVE_ATEXIT): New. + (HAVE_RAISE): New. + +Mon Jul 6 10:41:55 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (PUBKEY_USAGE_): New. + +Mon Jul 6 09:49:51 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.h (iobuf_set_error): New. + (iobuf_error): New. + +Sat Jun 13 17:31:32 1998 Werner Koch (wk@isil.d.shuttle.de) + + * g10lib.h: New as interface for the g10lib. + +Mon Jun 8 22:14:48 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (CIPHER_ALGO_CAST5): Changed name from .. CAST + +Thu May 21 13:25:51 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h: removed ROT 5 and changed one id and add dummy + +Tue May 19 18:09:05 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (DIGEST_ALGO_TIGER): Chnaged id from 101 to 6. + +Mon May 4 16:37:17 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (PUBKEY_ALGO_ELGAMAL_E): New, with value of the + old one. + * (is_ELGAMAL, is_RSA): New macros + +Sun Apr 26 14:35:24 1998 Werner Koch (wk@isil.d.shuttle.de) + + * types.h: New type u64 + +Mon Mar 9 12:59:55 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h: Included dsa.h. + +Tue Mar 3 15:11:21 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (random.h): Add new header and move all relevalt + functions to this header. + + + Copyright 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 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/common/ChangeLog.jnlib b/common/ChangeLog.jnlib new file mode 100644 index 0000000..6c0db13 --- /dev/null +++ b/common/ChangeLog.jnlib @@ -0,0 +1,783 @@ +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + + [Update 2015-04-24: README.jnlib has been removed and all + references to JNLIB, except for this file, have been removed.] + +2010-03-10 Werner Koch + + See gnupg/common/ChangeLog for newer changes. + + JNLIB has been merged into GnuPG's common directory. README.jnlib + list the files making up JNLIB. + + * README: Rename to README.jnlib + * ChangeLog: Rename to ChangeLog.jnlib. + * Makefile.am: Remove. + +2010-03-01 Werner Koch + + * t-w32-reg.c: New. + + * w32-reg.c (read_w32_registry_string) + (write_w32_registry_string): Support W32CE. + +2010-02-26 Werner Koch + + * t-timestuff.c: New. + + * dynload.h (dlopen, dlsym) [W32CE]: Map to wchar_t. + + * mischelp.c (_jnlib_free): New. + (same_file_p) [W32CE]: Map to wchar_t. + + * utf8conv.c (set_native_charset) [W32CE]: Do not use + GetConsoleOutputCP. + (wchar_to_utf8, utf8_to_wchar) [W32]: New. + + * Makefile.am (t_jnlib_ldadd) [W32CE]: Add gpg-error. + + * t-support.h (getenv) [HAVE_GETENV]: Add getenv stub. + [W32CE]: Include gpg-error.h + * t-support.c (gpg_err_code_from_errno) + (gpg_err_code_from_syserror) [GPG_ERROR_H]: Do not build. + + * t-stringhelp.c (gethome) [!HAVE_GETPWUID]: Keep result of getenv. + + * dotlock.c [!HAVE_SIGNAL_H]: Don't include signal.h. + (create_dotlock) [W32CE]: Map filename top wchar_t. + + * libjnlib-config.h [USE_SIMPLE_GETTEXT]: Include gpg-error.h and + remove w32help.h. + (jnlib_set_errno): New. Use it everywhere to set ERRNO. + (getenv) [!HAVE_GETENV]: New. + (getpid) [W32E]: New. + + * stringhelp.c (get_pwdir) [!HAVE_PWD_H]: Mark unused args. + (w32_strerror) [W32CE]: Use a simple implementation. + + * w32help.h [USE_SIMPLE_GETTEXT]: Remove all definitions; we are + now using the gpg-error included implementation. + * w32-gettext.c: Remove. + + * mischelp.c (same_file_p): Fix bug in case the second file can't + be opened. + +2009-10-19 Werner Koch + + * strlist.c (add_to_strlist_try): New. + +2009-09-22 Werner Koch + + * dotlock.h (DOTLOCK): Rename to dotlock_t. Change all users. + +2009-08-26 Werner Koch + + * stringhelp.c (do_make_filename): Factor some code out to .. + (get_pwdir): .. new. + +2009-08-26 Werner Koch + + * stringhelp.c [HAVE_PWD_H]: Include pwd.h. + (do_make_filename): New. + (make_filename, make_filename_try): Implement using the new + function. + * t-stringhelp.c (test_make_filename_try): New. + * t-support.c (gcry_strdup): Fix. + + * stringhelp.h (make_filename, make_filename_try): Add sentinel + attribute. + +2009-08-25 Werner Koch + + * stringhelp.c: Include errno.h. + (do_strconcat): New. + (strconcat, xstrconcat): New. + * types.h (GNUPG_GCC_A_SENTINEL): New. + * t-stringhelp.c (test_strconcat, test_xstrconcat): New. + (main): Run them. + +2009-07-07 Werner Koch + + * stringhelp.c (make_filename_try): Use jnlib_malloc. + + * dotlock.c (read_lockfile): Replace jnlib_xmalloc by jnlib_malloc. + +2009-06-04 Werner Koch + + * mischelp.h: Include SUN_LEN etc also for W32. + +2009-05-19 Werner Koch + + * mischelp.h: Define PF_LOCAL, AF_LOCAL and SUN_LEN if requested. + * logging.c (fun_writer): Use SUN_LEN to fix a Mac OS X freeze. + +2009-03-25 Werner Koch + + * logging.c (fun_closer): Never close fd 2. + (set_file_fd): Close logstream early. + +2009-02-25 Werner Koch + + * logging.c (get_tid_callback): New. + (do_logv): Use it. + (log_set_get_tid_callback): New. + +2009-01-22 Werner Koch + + * t-support.c (gpg_err_code_from_errno) + (gpg_err_code_from_syserror): New. + +2008-11-20 Werner Koch + + * argparse.c (arg_parse): Fix last change. + +2008-11-11 Werner Koch + + * argparse.h: Add a bunch of macros and constants. + * argparse.c: Use the new macros. Re-indent the code. Change + license back to LGPL 2.1. + +2008-11-04 Werner Koch + + * w32-gettext.c: Merged with code from libgpg-error and rewrote + most parts. + + * Makefile.am (AM_CFLAGS): Add -DJNLIB_IN_JNLIB. + +2008-10-29 Werner Koch + + * stringhelp.c (make_filename): Implement using macros. Factor some + code out to .. + (change_slashes): New. + (make_filename_try): New. + + * w32-gettext.c (gettext): Return if no domain is loaded. + Reported by Tom Pegios. + +2008-10-28 Werner Koch + + * w32-gettext.c (gettext): Try the binary search if the string was + not found in the hash table. + +2008-10-20 Werner Koch + + * w32-afunix.c (_w32_sock_connect): Mark ADDRLEN as unused. + + * dotlock.c (release_dotlock): Do not mix declaration and code. + + * stringhelp.c (make_basename): Silent gcc warning about unused arg. + * argparse.c (store_alias): Ditto. + (find_long_option): + +2008-10-15 Werner Koch + + * logging.c (do_logv) [W32]: Flush the log stream. + +2008-09-29 Werner Koch + + * argparse.c (ARGERR_): Use constants for error values. + (optfile_parse): Prettify. Replace xmalloc and xrealloc by malloc + and realloc. + * libjnlib-config.h (jnlib_strdup, jnlib_realloc): New. + +2008-06-26 Werner Koch + + * stringhelp.c (print_sanitized_buffer2): Loose check for control + characters to better cope with utf-8. The range 0x80..0x9f is + nowadays not anymore accidently used for control charaters. + +2008-06-13 Werner Koch + + * dotlock.c: Reformat code and implement locking for W32. + (create_dotlock): Use snprintf. + +2008-06-11 Werner Koch + + * utf8conv.c: Remove useless variable ACTIVE_CHARSET. Suggested + by Petr Uzel. + +2008-05-26 Werner Koch + + * argparse.c (usage): Make sure to print a trailing LF for usage(1). + +2008-04-08 Werner Koch + + * w32-gettext.c (gettext_select_utf8): New. + (get_string): Support switching encodings. + (load_domain): Allocate space for DATA_NATIVE. + +2008-03-25 Werner Koch + + * w32-gettext.c (_nl_locale_name): New. Taken from + ../common/localename and GNU gettext's localename.c. + (set_gettext_file): Rewritten. + (gettext_localename): New. + +2008-03-17 Werner Koch + + * logging.c (my_funopen_hook_size_t): New. + (fun_writer): Use it to cope with fopencookie/funopen differences. + * dotlock.c (read_lockfile): Initialize PID. Reported by Stéphane + Corthésy. + +2008-02-22 Werner Koch + + * argparse.c (strusage): Set copyright year to 2008. + +2007-11-19 Werner Koch + + * stringhelp.c (percent_escape): Factor code out to + (do_percent_escape): .. new. + (try_percent_escape): New. + +2007-10-01 Werner Koch + + * w32-afunix.c: Only keep the client related code. + (read_port_and_nonce): New. Taken from Assuan. + (_w32_sock_connect): Rewritten. + +2007-08-29 Werner Koch + + * argparse.c (initialize): Make strings translatable and remove + extra LF. + +2007-08-24 Werner Koch + + * mischelp.c (same_file_p): New. + (libjnlib_dummy_mischelp_func): Remove as we now always have one + function. + +2007-08-09 Werner Koch + + * argparse.c (show_help): Expand the @EMAIL@ macro in the package + bug reporting address. + +2007-08-02 Werner Koch + + * t-stringhelp.c (test_compare_filenames): New. + + * stringhelp.c (compare_filenames) [HAVE_DRIVE_LETTERS]: Fixed + comparison to take slash and backslash in account. + (make_filename): Avoid mixing / and \. + +2007-07-04 Werner Koch + + * utf8conv.c (load_libiconv): Remove URL from translatble string. + + Switched JNLIB from LGPLv2.1 to LGPLv3. + +2007-07-01 Werner Koch + + * argparse.c (strusage): Use id 10 for the license string; + default to GPL3+. Change long note to version 3 or later. + (show_version): Print the license info. + +2007-06-19 Werner Koch + + * Makefile.am: Add support for regression tests. + * t-support.h, t-support.c: New. + * t-stringhelp.c: New. + + * stringhelp.c (percent_escape): Add arg EXTRA to make it a more + general function. Changed all callers. + +2007-06-18 Werner Koch + + * w32-afunix.c (_w32_sock_bind): Changed to properly detect an + already used socket. + +2007-06-18 Marcus Brinkmann + + * stringhelp.h (percent_escape): New prototype. + * stringhelp.c (percent_escape): New function. + +2007-06-11 Werner Koch + + * utf8conv.c (jnlib_iconv_open, jnlib_iconv, jnlib_iconv_close): New. + +2007-06-06 Werner Koch + + * w32help.h: New. + * w32-gettext.c: New. Taken from gnupg 1.4, added ngettext, + changed to use jnlib malloc functions and put under the LGPL. + * w32-reg.c: New. Taken from../common/w32reg.c and changed to + LGPL. Changed API to use the jnlib malloc functions. + * Makefile.am (libjnlib_a_SOURCES) [!W32]: Do not build the w32 + specific modules. + + * dotlock.c: Include stringhelp.h for stpcpy prototype. + +2007-06-04 Werner Koch + + * dynload.h: New. Taken from ../common and changed to LGPL. + + * utf8conv.c (load_libiconv): New. Taken from GnuPG 1.4 + +2007-05-30 Werner Koch + + * w32-pth.h, w32-pth.c: Remove. + +2007-04-25 Werner Koch + + * argparse.c (long_opt_strlen): Fixed for utf-8. + +2007-03-07 Werner Koch + + * argparse.c (strusage): Set copyright year to 2007. + +2007-01-25 Werner Koch + + * stringhelp.c (utf8_charcount): New. + +2006-11-29 Werner Koch + + * utf8conv.c (set_native_charset) [HAVE_W32_SYSTEM]: Fixed typo in + macro name. + +2006-11-15 Werner Koch + + * logging.c (my_funopen_hook_ret_t): New. + (fun_writer): Use it. + +2006-10-19 Werner Koch + + * stringhelp.c (memrchr) [!HAVE_MEMRCHR]: Provide a replacement. + +2006-09-27 Werner Koch + + * mischelp.c: New. + (timegm): Copied from gnupg 1.4, changed from GPL to LGPL. Fixed + a memory leak. + + * stringhelp.h (isascii): New. + + * stringhelp.c (strsep): New. Copied from gnupg 1.4.5 + util/strgutil.c. + + * strlist.h (STRLIST): Removed deprecated typedef. + + * types.h: Made cpp commands work with old compilers. Also shows + up nicer with Emacs' font locking. + + * w32-afunix.c (_w32_sock_connect): Set ERRNO for an invalid port. + + Changed license from GPL to LGPL. Note that all code has either + been written by me, David, employees of g10 Code or taken from + glibc. + + * libjnlib-config.h, stringhelp.c, stringhelp.h: + * strlist.c, strlist.h, utf8conv.c, utf8conv.h: + * argparse.c, argparse.h, logging.c, logging.h: + * dotlock.c, dotlock.h, types.h, mischelp.h: + * xmalloc.c, xmalloc.h, w32-pth.c, w32-pth.h: + * w32-afunix.c, w32-afunix.h: Tagged them to be long to jnlib + which is a part of GnuPG but also used by other projetcs. + +2006-09-22 Werner Koch + + * utf8conv.c: Reworked to match the gnupg 1.4.5 code. This now + requires iconv support but this is reasonable for all modern + systems. + +2006-08-29 Werner Koch + + * logging.c (do_logv): Emit a missing LF for fatal errors. + +2006-06-28 Werner Koch + + * dotlock.c (make_dotlock, release_dotlock, read_lockfile) + (maybe_deadlock, destroy_dotlock, create_dotlock): Re-indented. + (create_dotlock): Repalces some log_fatal by log_error as it was + not intended that they should terminate. Write the nodename to + the locking file. Code cleanups. + (read_lockfile): Reworked to read the node name. + (make_dotlock): Test for identical node name and delete lock stale + file. + (release_dotlock): Likewise. + +2006-05-23 Werner Koch + + * libjnlib-config.h (JNLIB_NEED_UTF8CONV): Fixed typo in name. + + * dotlock.c (release_dotlock): Don't act if we don't have any + locks at all. + (destroy_dotlock): New. From 1.4.3. + (dotlock_remove_lockfiles): Make use of destroy function. + +2006-05-19 Werner Koch + + * strlist.c (append_to_strlist2): Enabled. + + * stringhelp.c (print_sanitized_buffer2): New. Changed the rules + to match the behaviour of print_string2 from gnupg 1.4.3. + (print_sanitized_buffer): Use the new function. + (print_sanitized_string2): New. + (hextobyte): New. Taken from gpg 1.4.3. + +2006-04-28 Werner Koch + + * stringhelp.c (print_sanitized_buffer): Fix bug where the count + got wrong for the \xNN representation. + (sanitize_buffer): Fix bug where some control characters lose part + of their \xNN representation. + +2006-04-20 Werner Koch + + * stringhelp.c (make_basename): New arg INPUTPATH for future + riscos compatibility. + +2006-04-18 Werner Koch + + * libjnlib-config.h (JNLIB_NEED_UTF8CONF): Defined. + * strlist.c (add_to_strlist2) [JNLIB_NEED_UTF8CONV]: Enabled. + +2005-06-15 Werner Koch + + * stringhelp.c (sanitize_buffer): Make P a void*. + (ascii_memistr, memistr): Ditto. + (ascii_memcasecmp): Ditto. + * logging.c (writen): Use void * for arg BUFFER. + * stringhelp.c (memistr): Fixed unsigned/signed pointer conflict. + (ascii_memistr): Ditto. + (ascii_memcasemem): Ditto. + * utf8conv.c (utf8_to_native): Ditto. + (utf8_to_native): Ditto. + * argparse.c (show_version): Removed non-required cast. + +2005-01-19 Werner Koch + + * logging.c (fun_writer): Don't fallback to stderr. Print to + stderr only if connected to a tty. + +2004-12-20 Werner Koch + + * w32-pth.c (do_pth_event_free): The events are hold in a ring + buffer. Adjust for that. + (do_pth_event_body): Ditto. + (pth_event_isolate): Ditto. + (do_pth_wait): Ditto. + (_pth_event_count): Renamed to .. + (event_count): .. and adjusted as above. + (pth_init): Define 3 debug levels and change all debug calls to + make use of them. This makes the moule now silent. + +2004-12-19 Werner Koch + + * w32-pth.c (pth_init): Enable debugging depending on env var. + (pth_self): New. + (pth_mutex_release, pth_mutex_acquire): Implemented directly using + the W32 API. + +2004-12-18 Werner Koch + + * w32-pth.c (pth_init): Reverse return values. Use TRUE and FALSE + constants. + (pth_kill, pth_mutex_acquire, pth_attr_set, pth_join, pth_cancel): + Ditto. + +2004-12-15 Werner Koch + + * logging.c [W32]: Don't include unavailable headers. + +2004-12-14 Werner Koch + + * w32-pth.c (_pth_strerror): Renamed to ... + (w32_strerror): .. this. And let callers provide a buffer. + (spawn_helper_thread): Removed HD arg and hardwire the stack size + to 32k. + (do_pth_wait): Removed use of ATTR; not needed for the helper + threads. + (helper_thread): Renamed to .. + (launch_thread): .. this. Release handle if not joinable. + (struct pth_priv_hd_s): Renamed to ... + (struct thread_info_s): .. this. Add member JOINABLE and TH. + +2004-12-14 Timo Schulz + + * w32-pth.c (pth_kill): Just release the crit section if + pth_init was really called. And set all handles to NULL. + (_pth_strerror): New. + (do_pth_wait): Before we enter the loop we check if there + are too much events in the ring. + +2004-12-14 Werner Koch + + * w32-pth.h (pth_event_occured): Removed macro. + * w32-pth.c: Fixed license statement; its under the LGPL. + (enter_pth, leave_pth): Use them to bracket almost all public + functions. + +2004-12-13 Timo Schulz + + * w32-pth.c (enter_pth, leave_pth): New. + (pth_init): Initialize global mutex section. + (pth_kill): Release global mutex section. + (helper_thread): New. + (pth_spawn): Make sure only one thread is running. + +2004-12-13 Werner Koch + + * stringhelp.c (w32_strerror) [W32]: New. + + * w32-pth.c, w32-pth.h: Added real code written by Timo Schulz. + Not finished, though. + +2004-12-07 Werner Koch + + * w32-pth.c, w32-pth.h: New. + +2004-11-26 Werner Koch + + * logging.c [_WIN32]: Don't include socket headers. + +2004-11-30 Timo Schulz + + * w32-afunix.c: New. AF_UNIX emulation for W32. + * w32-afunix.h: Likewise. + +2004-11-22 Werner Koch + + * logging.c (log_test_fd): Add test on LOGSTREAM. Reported by + Barry Schwartz. + +2004-11-18 Werner Koch + + * logging.c: Explicitly include sys/stat.h for the S_I* constants. + +2004-10-21 Werner Koch + + * logging.c (do_logv): Use set_log_stream to setup a default. + (log_set_file): Factored code out to .. + (set_file_fd): .. New function to allow using a file descriptor. + (log_set_fd): Make use of new fucntion. + (fun_writer): Reworked. + +2004-08-18 Werner Koch + + * stringhelp.c (print_sanitized_utf8_string): Actually implement + it. + +2004-06-21 Werner Koch + + * logging.c (log_set_file): Do not close an old logstream if it + used to be stderr or stdout. + +2004-05-05 Werner Koch + + * logging.c (log_set_file): Oops, don't close if LOGSTREAM is NULL. + +2004-04-30 Werner Koch + + * logging.c (log_set_file): Make sure the log stream will be + closed even if the stderr fileno will be assigned to a new socket. + +2004-04-16 Werner Koch + + * logging.h (JNLIB_LOG_WITH_PREFIX): Add constants for the flag + values. + * logging.c (log_set_prefix): New flag DETACHED. + (fun_writer): Take care of this flag. + (log_test_fd): New. + +2004-02-18 Werner Koch + + * stringhelp.c (print_sanitized_buffer): Don't care about + non-ASCII characaters. + (sanitize_buffer): Ditto. + +2004-02-12 Werner Koch + + * Makefile.am: Replaced INCLUDES by AM_CPPFLAGS. + +2004-01-05 Werner Koch + + * argparse.c (strusage): Changed default copyright year to 2004. + +2003-12-17 Werner Koch + + * argparse.c (initialize): Replaced use of non-literal format + args. Suggested by Florian Weimer. + +2003-12-16 Werner Koch + + * logging.c (writen, fun_writer, fun_closer): New. + (log_set_file): Add feature to log to a socket. + (log_set_file, do_logv): Force printing with prefix and pid. + +2003-11-13 Werner Koch + + * strlist.c (strlist_copy): New. + + * dotlock.c: Define DIRSEP_C et al. if not defined. + +2003-11-06 Werner Koch + + * strlist.h (strlist_t): New. STRLIST is now deprecated. + +2003-06-18 Werner Koch + + * strlist.c (strlist_pop): New. + + * dotlock.c (dotlock_remove_lockfiles): Prefixed with dotlock_ and + made global. + +2003-06-17 Werner Koch + + * stringhelp.c (length_sans_trailing_chars) + (length_sans_trailing_ws): New. + + * logging.c (log_inc_errorcount): New. + + * stringhelp.c (print_sanitized_utf8_buffer): Implement utf8 + conversion. + (sanitize_buffer): New. Based on gnupg 1.3.2 make_printable_string. + + * dotlock.c: Updated to match the version from 1.3.2 + * utf8conv.c: New. Code taken from strgutil.c of gnupg 1.3.2. + * utf8conv.h: New. + +2003-06-16 Werner Koch + + * logging.c (do_logv): Hack to optionally suppress a leading space. + + * stringhelp.c (ascii_strncasecmp): New. Taken from gnupg 1.3. + (ascii_memistr): New. Taken from gnupg 1.3 + +2003-06-13 Werner Koch + + * mischelp.h (wipememory2,wipememory): New. Taken from GnuPG 1.3.2. + +2002-06-04 Werner Koch + + * stringhelp.c (print_sanitized_utf8_string): New. No real + implementation for now. + (print_sanitized_utf8_buffer): Ditto. + +2002-04-04 Werner Koch + + * logging.c (log_get_prefix): New. + +2002-03-15 Werner Koch + + * argparse.c (optfile_parse): Fixed missing argument handling. + +2002-02-25 Werner Koch + + * stringhelp.c (ascii_memcasemem): New. + +2002-02-14 Werner Koch + + * Makefile.am (INCLUDES): Add cflags for libgcrypt. + +2002-02-07 Werner Koch + + * logging.c (log_set_fd): New. + + * stringhelp.c (print_sanitized_buffer): New. + (print_sanitized_string): New. + +2002-01-24 Werner Koch + + * argparse.c (strusage): Set default copyright notice year to 2002. + + Fixed the copyright notice of this file, as it has always been + part of GnuPG and therefore belongs to the FSF. + +2001-11-01 Marcus Brinkmann + + * logging.c (log_printf): Do not initialize ARG_PTR with 0, we + don't know the correct type. Instead, run va_start and va_end + unconditionally. + Reported by Jose Carlos Garcia Sogo . + +2002-01-19 Werner Koch + + * logging.c (log_get_stream): New. + +2001-12-05 Werner Koch + + * logging.c (log_set_prefix): New. + (do_logv): Include prefix and pid only if enabled. Print time only + when explicitly enabled. + (log_logv): New. + * logging.h: Include log_logv() only when requested. + +2001-11-06 Werner Koch + + * strlist.c, strlist.h: New. Taken from pgnupg/util/strgutil.c + +2001-08-30 Werner Koch + + * logging.c (log_printf): Don't pass NULL instead of arg_ptr. + +2001-07-19 Werner Koch + + * stringhelp.c (ascii_memistr,ascii_isupper,ascii_islower, + ascii_toupper,ascii_tolower, ascii_strcasecmp, ascii_memcasecmp): New. + +2000-07-26 10:02:51 Werner Koch (wk@habibti.openit.de) + + * stringhelp.c.: Add stdarg.h + * argparse.h: s/ulong/unsigned long/ although this should be defined + by types.h. + +2000-06-28 19:40:23 Werner Koch (wk@habibti.openit.de) + + * Makefile.am: Replaced second logging.c by .h + +2000-05-24 08:58:15 Werner Koch (wk@habibti.openit.de) + + * logging.c (log_get_errorcount): New. + +2000-05-24 08:44:47 Werner Koch (wk@habibti.openit.de) + + * stringhelp.c: Added a few filename related helper functions. + +2000-05-11 18:04:43 Werner Koch (wk@habibti.openit.de) + + * xmalloc.c (xstrcat2): Replaced stpcpy to quickly address W32 + problems. + +2000-05-02 19:43:38 Werner Koch (wk@habibti.openit.de) + + * xmalloc.c (xstrcat2): New. + +Mon Jan 24 13:04:28 CET 2000 Werner Koch + + * README: New. + * Makefile.am: new. + * argparse.c, argparse.h, logging.c, logging.h: + * mischelp.h, stringhelp.c, stringhelp.h, xmalloc.c: + * xmalloc.h, dotlock.c: Moved from ../util to here. + * dotlock.h: New. + * libjnlib-config.h: New. + + * logging.c (log_set_file): New. + (log_printf): New. + (do_logv): Add kludge to insert LFs. + + + *********************************************************** + * Please note that JNLIB is maintained as part of GnuPG. * + * You may find it source-copied in other packages. * + *********************************************************** + + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2010 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..72e3fb4 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,224 @@ +# Makefile for common gnupg modules +# Copyright (C) 2001, 2003, 2007, 2010 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 3 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, see . + +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ + audit-events.h status-codes.h ChangeLog.jnlib \ + ChangeLog-2011.include w32info-rc.h.in gnupg.ico + +noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a +if !HAVE_W32CE_SYSTEM +noinst_LIBRARIES += libsimple-pwquery.a +endif +noinst_PROGRAMS = $(module_tests) $(module_maint_tests) +TESTS = $(module_tests) + +BUILT_SOURCES = audit-events.h status-codes.h + +MAINTAINERCLEANFILES = audit-events.h status-codes.h + +AM_CPPFLAGS = + +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) + +include $(top_srcdir)/am/cmacros.am + + +common_sources = \ + common-defs.h \ + util.h utilproto.h fwddecl.h i18n.c i18n.h \ + types.h host2net.h dynload.h w32help.h \ + mapstrings.c stringhelp.c stringhelp.h \ + strlist.c strlist.h \ + utf8conv.c utf8conv.h \ + argparse.c argparse.h \ + logging.c logging.h \ + dotlock.c dotlock.h \ + mischelp.c mischelp.h \ + status.c status.h\ + shareddefs.h \ + openpgpdefs.h \ + gc-opt-flags.h \ + keyserver.h \ + sexp-parse.h \ + tlv.c tlv.h \ + init.c init.h \ + sexputil.c \ + sysutils.c sysutils.h \ + homedir.c \ + gettime.c gettime.h \ + yesno.c \ + b64enc.c b64dec.c zb32.c zb32.h \ + convert.c \ + percent.c \ + mbox-util.c mbox-util.h \ + miscellaneous.c \ + xasprintf.c \ + xreadline.c \ + membuf.c membuf.h \ + ccparray.c ccparray.h \ + iobuf.c iobuf.h \ + ttyio.c ttyio.h \ + asshelp.c asshelp2.c asshelp.h \ + exechelp.h \ + signal.c \ + audit.c audit.h \ + localename.c \ + session-env.c session-env.h \ + userids.c userids.h \ + openpgp-oid.c \ + ssh-utils.c ssh-utils.h \ + agent-opt.c \ + helpfile.c \ + mkdir_p.c mkdir_p.h \ + strlist.c strlist.h \ + exectool.c exectool.h \ + server-help.c server-help.h \ + name-value.c name-value.h \ + recsel.c recsel.h + +if HAVE_W32_SYSTEM +common_sources += w32-reg.c +endif + +# To make the code easier to read we have split home some code into +# separate source files. +if HAVE_W32_SYSTEM +if HAVE_W32CE_SYSTEM +common_sources += exechelp-w32ce.c +else +common_sources += exechelp-w32.c +endif +else +common_sources += exechelp-posix.c +endif + +# Sources only useful without NPTH. +without_npth_sources = \ + get-passphrase.c get-passphrase.h + +# Sources only useful with NPTH. +with_npth_sources = \ + call-gpg.c call-gpg.h + +libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) +libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 + +libcommonpth_a_SOURCES = $(common_sources) $(with_npth_sources) +libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) + +if !HAVE_W32CE_SYSTEM +libsimple_pwquery_a_SOURCES = \ + simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h +libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) +endif + +libgpgrl_a_SOURCES = \ + gpgrlhelp.c + +if MAINTAINER_MODE +# Note: Due to the dependency on Makefile, the file will always be +# rebuilt, so we allow this only in maintainer mode. + +# Create the audit-events.h include file from audit.h +# Note: We create the target file in the source directory because it +# is a distributed built source. If we would not do that we may end +# up with two files and then it is not clear which version of the +# files will be picked up. +audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h + $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ + | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ + -v namespace=eventstr_ > $(srcdir)/audit-events.h + +# Create the status-codes.h include file from status.h +status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h + $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ + | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ + -v namespace=statusstr_ > $(srcdir)/status-codes.h +endif + +# +# Module tests +# +module_tests = t-stringhelp t-timestuff \ + t-convert t-percent t-gettime t-sysutils t-sexputil \ + t-session-env t-openpgp-oid t-ssh-utils \ + t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ + t-name-value t-ccparray t-recsel +if !HAVE_W32CE_SYSTEM +module_tests += t-exechelp t-exectool +endif +if HAVE_W32_SYSTEM +module_tests += t-w32-reg +endif + +if MAINTAINER_MODE +module_maint_tests = t-helpfile t-b64 +else +module_maint_tests = +endif + +t_extra_src = t-support.h + +t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) +t_common_ldadd = libcommon.a \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) + + +# Common tests +t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) +t_stringhelp_LDADD = $(t_common_ldadd) + +t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) +t_timestuff_LDADD = $(t_common_ldadd) + +t_convert_LDADD = $(t_common_ldadd) +t_percent_LDADD = $(t_common_ldadd) +t_gettime_LDADD = $(t_common_ldadd) +t_sysutils_LDADD = $(t_common_ldadd) +t_helpfile_LDADD = $(t_common_ldadd) +t_sexputil_LDADD = $(t_common_ldadd) +t_b64_LDADD = $(t_common_ldadd) +t_exechelp_LDADD = $(t_common_ldadd) +t_exectool_LDADD = $(t_common_ldadd) +t_session_env_LDADD = $(t_common_ldadd) +t_openpgp_oid_LDADD = $(t_common_ldadd) +t_ssh_utils_LDADD = $(t_common_ldadd) +t_mapstrings_LDADD = $(t_common_ldadd) + +t_zb32_SOURCES = t-zb32.c $(t_extra_src) +t_zb32_LDADD = $(t_common_ldadd) + +t_mbox_util_LDADD = $(t_common_ldadd) +t_iobuf_LDADD = $(t_common_ldadd) +t_strlist_LDADD = $(t_common_ldadd) +t_name_value_LDADD = $(t_common_ldadd) +t_ccparray_LDADD = $(t_common_ldadd) +t_recsel_LDADD = $(t_common_ldadd) + +# System specific test +if HAVE_W32_SYSTEM +t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) +t_w32_reg_LDADD = $(t_common_ldadd) +endif + +# All programs should depend on the created libs. +$(PROGRAMS) : libcommon.a libcommonpth.a diff --git a/common/Makefile.in b/common/Makefile.in new file mode 100644 index 0000000..0844840 --- /dev/null +++ b/common/Makefile.in @@ -0,0 +1,2897 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# 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. + +@SET_MAKE@ + +# Makefile for common gnupg modules +# Copyright (C) 2001, 2003, 2007, 2010 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 3 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, see . + +# cmacros.am - C macro definitions +# 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 3 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, see . + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_W32CE_SYSTEM_FALSE@am__append_1 = libsimple-pwquery.a +noinst_PROGRAMS = $(am__EXEEXT_3) $(am__EXEEXT_4) +TESTS = $(am__EXEEXT_3) +DIST_COMMON = $(top_srcdir)/am/cmacros.am $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/build-aux/mkinstalldirs \ + $(srcdir)/w32info-rc.h.in $(top_srcdir)/build-aux/depcomp \ + README +@HAVE_DOSISH_SYSTEM_FALSE@am__append_2 = -DGNUPG_BINDIR="\"$(bindir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\"" + + +# If a specific protect tool program has been defined, pass its name +# to cc. Note that these macros should not be used directly but via +# the gnupg_module_name function. +@GNUPG_AGENT_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\"" +@GNUPG_PINENTRY_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\"" +@GNUPG_SCDAEMON_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\"" +@GNUPG_DIRMNGR_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\"" +@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\"" +@GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_8 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\"" +@HAVE_W32_SYSTEM_TRUE@am__append_9 = w32-reg.c + +# To make the code easier to read we have split home some code into +# separate source files. +@HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__append_10 = exechelp-w32ce.c +@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__append_11 = exechelp-w32.c +@HAVE_W32_SYSTEM_FALSE@am__append_12 = exechelp-posix.c +@HAVE_W32CE_SYSTEM_FALSE@am__append_13 = t-exechelp t-exectool +@HAVE_W32_SYSTEM_TRUE@am__append_14 = t-w32-reg +subdir = common +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ + $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = w32info-rc.h +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libcommon_a_AR = $(AR) $(ARFLAGS) +libcommon_a_LIBADD = +am__libcommon_a_SOURCES_DIST = common-defs.h util.h utilproto.h \ + fwddecl.h i18n.c i18n.h types.h host2net.h dynload.h w32help.h \ + mapstrings.c stringhelp.c stringhelp.h strlist.c strlist.h \ + utf8conv.c utf8conv.h argparse.c argparse.h logging.c \ + logging.h dotlock.c dotlock.h mischelp.c mischelp.h status.c \ + status.h shareddefs.h openpgpdefs.h gc-opt-flags.h keyserver.h \ + sexp-parse.h tlv.c tlv.h init.c init.h sexputil.c sysutils.c \ + sysutils.h homedir.c gettime.c gettime.h yesno.c b64enc.c \ + b64dec.c zb32.c zb32.h convert.c percent.c mbox-util.c \ + mbox-util.h miscellaneous.c xasprintf.c xreadline.c membuf.c \ + membuf.h ccparray.c ccparray.h iobuf.c iobuf.h ttyio.c ttyio.h \ + asshelp.c asshelp2.c asshelp.h exechelp.h signal.c audit.c \ + audit.h localename.c session-env.c session-env.h userids.c \ + userids.h openpgp-oid.c ssh-utils.c ssh-utils.h agent-opt.c \ + helpfile.c mkdir_p.c mkdir_p.h exectool.c exectool.h \ + server-help.c server-help.h name-value.c name-value.h recsel.c \ + recsel.h w32-reg.c exechelp-w32ce.c exechelp-w32.c \ + exechelp-posix.c get-passphrase.c get-passphrase.h +@HAVE_W32_SYSTEM_TRUE@am__objects_1 = libcommon_a-w32-reg.$(OBJEXT) +@HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__objects_2 = libcommon_a-exechelp-w32ce.$(OBJEXT) +@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__objects_3 = libcommon_a-exechelp-w32.$(OBJEXT) +@HAVE_W32_SYSTEM_FALSE@am__objects_4 = \ +@HAVE_W32_SYSTEM_FALSE@ libcommon_a-exechelp-posix.$(OBJEXT) +am__objects_5 = libcommon_a-i18n.$(OBJEXT) \ + libcommon_a-mapstrings.$(OBJEXT) \ + libcommon_a-stringhelp.$(OBJEXT) libcommon_a-strlist.$(OBJEXT) \ + libcommon_a-utf8conv.$(OBJEXT) libcommon_a-argparse.$(OBJEXT) \ + libcommon_a-logging.$(OBJEXT) libcommon_a-dotlock.$(OBJEXT) \ + libcommon_a-mischelp.$(OBJEXT) libcommon_a-status.$(OBJEXT) \ + libcommon_a-tlv.$(OBJEXT) libcommon_a-init.$(OBJEXT) \ + libcommon_a-sexputil.$(OBJEXT) libcommon_a-sysutils.$(OBJEXT) \ + libcommon_a-homedir.$(OBJEXT) libcommon_a-gettime.$(OBJEXT) \ + libcommon_a-yesno.$(OBJEXT) libcommon_a-b64enc.$(OBJEXT) \ + libcommon_a-b64dec.$(OBJEXT) libcommon_a-zb32.$(OBJEXT) \ + libcommon_a-convert.$(OBJEXT) libcommon_a-percent.$(OBJEXT) \ + libcommon_a-mbox-util.$(OBJEXT) \ + libcommon_a-miscellaneous.$(OBJEXT) \ + libcommon_a-xasprintf.$(OBJEXT) \ + libcommon_a-xreadline.$(OBJEXT) libcommon_a-membuf.$(OBJEXT) \ + libcommon_a-ccparray.$(OBJEXT) libcommon_a-iobuf.$(OBJEXT) \ + libcommon_a-ttyio.$(OBJEXT) libcommon_a-asshelp.$(OBJEXT) \ + libcommon_a-asshelp2.$(OBJEXT) libcommon_a-signal.$(OBJEXT) \ + libcommon_a-audit.$(OBJEXT) libcommon_a-localename.$(OBJEXT) \ + libcommon_a-session-env.$(OBJEXT) \ + libcommon_a-userids.$(OBJEXT) \ + libcommon_a-openpgp-oid.$(OBJEXT) \ + libcommon_a-ssh-utils.$(OBJEXT) \ + libcommon_a-agent-opt.$(OBJEXT) libcommon_a-helpfile.$(OBJEXT) \ + libcommon_a-mkdir_p.$(OBJEXT) libcommon_a-strlist.$(OBJEXT) \ + libcommon_a-exectool.$(OBJEXT) \ + libcommon_a-server-help.$(OBJEXT) \ + libcommon_a-name-value.$(OBJEXT) libcommon_a-recsel.$(OBJEXT) \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) +am__objects_6 = libcommon_a-get-passphrase.$(OBJEXT) +am_libcommon_a_OBJECTS = $(am__objects_5) $(am__objects_6) +libcommon_a_OBJECTS = $(am_libcommon_a_OBJECTS) +libcommonpth_a_AR = $(AR) $(ARFLAGS) +libcommonpth_a_LIBADD = +am__libcommonpth_a_SOURCES_DIST = common-defs.h util.h utilproto.h \ + fwddecl.h i18n.c i18n.h types.h host2net.h dynload.h w32help.h \ + mapstrings.c stringhelp.c stringhelp.h strlist.c strlist.h \ + utf8conv.c utf8conv.h argparse.c argparse.h logging.c \ + logging.h dotlock.c dotlock.h mischelp.c mischelp.h status.c \ + status.h shareddefs.h openpgpdefs.h gc-opt-flags.h keyserver.h \ + sexp-parse.h tlv.c tlv.h init.c init.h sexputil.c sysutils.c \ + sysutils.h homedir.c gettime.c gettime.h yesno.c b64enc.c \ + b64dec.c zb32.c zb32.h convert.c percent.c mbox-util.c \ + mbox-util.h miscellaneous.c xasprintf.c xreadline.c membuf.c \ + membuf.h ccparray.c ccparray.h iobuf.c iobuf.h ttyio.c ttyio.h \ + asshelp.c asshelp2.c asshelp.h exechelp.h signal.c audit.c \ + audit.h localename.c session-env.c session-env.h userids.c \ + userids.h openpgp-oid.c ssh-utils.c ssh-utils.h agent-opt.c \ + helpfile.c mkdir_p.c mkdir_p.h exectool.c exectool.h \ + server-help.c server-help.h name-value.c name-value.h recsel.c \ + recsel.h w32-reg.c exechelp-w32ce.c exechelp-w32.c \ + exechelp-posix.c call-gpg.c call-gpg.h +@HAVE_W32_SYSTEM_TRUE@am__objects_7 = \ +@HAVE_W32_SYSTEM_TRUE@ libcommonpth_a-w32-reg.$(OBJEXT) +@HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__objects_8 = libcommonpth_a-exechelp-w32ce.$(OBJEXT) +@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__objects_9 = libcommonpth_a-exechelp-w32.$(OBJEXT) +@HAVE_W32_SYSTEM_FALSE@am__objects_10 = libcommonpth_a-exechelp-posix.$(OBJEXT) +am__objects_11 = libcommonpth_a-i18n.$(OBJEXT) \ + libcommonpth_a-mapstrings.$(OBJEXT) \ + libcommonpth_a-stringhelp.$(OBJEXT) \ + libcommonpth_a-strlist.$(OBJEXT) \ + libcommonpth_a-utf8conv.$(OBJEXT) \ + libcommonpth_a-argparse.$(OBJEXT) \ + libcommonpth_a-logging.$(OBJEXT) \ + libcommonpth_a-dotlock.$(OBJEXT) \ + libcommonpth_a-mischelp.$(OBJEXT) \ + libcommonpth_a-status.$(OBJEXT) libcommonpth_a-tlv.$(OBJEXT) \ + libcommonpth_a-init.$(OBJEXT) \ + libcommonpth_a-sexputil.$(OBJEXT) \ + libcommonpth_a-sysutils.$(OBJEXT) \ + libcommonpth_a-homedir.$(OBJEXT) \ + libcommonpth_a-gettime.$(OBJEXT) \ + libcommonpth_a-yesno.$(OBJEXT) libcommonpth_a-b64enc.$(OBJEXT) \ + libcommonpth_a-b64dec.$(OBJEXT) libcommonpth_a-zb32.$(OBJEXT) \ + libcommonpth_a-convert.$(OBJEXT) \ + libcommonpth_a-percent.$(OBJEXT) \ + libcommonpth_a-mbox-util.$(OBJEXT) \ + libcommonpth_a-miscellaneous.$(OBJEXT) \ + libcommonpth_a-xasprintf.$(OBJEXT) \ + libcommonpth_a-xreadline.$(OBJEXT) \ + libcommonpth_a-membuf.$(OBJEXT) \ + libcommonpth_a-ccparray.$(OBJEXT) \ + libcommonpth_a-iobuf.$(OBJEXT) libcommonpth_a-ttyio.$(OBJEXT) \ + libcommonpth_a-asshelp.$(OBJEXT) \ + libcommonpth_a-asshelp2.$(OBJEXT) \ + libcommonpth_a-signal.$(OBJEXT) libcommonpth_a-audit.$(OBJEXT) \ + libcommonpth_a-localename.$(OBJEXT) \ + libcommonpth_a-session-env.$(OBJEXT) \ + libcommonpth_a-userids.$(OBJEXT) \ + libcommonpth_a-openpgp-oid.$(OBJEXT) \ + libcommonpth_a-ssh-utils.$(OBJEXT) \ + libcommonpth_a-agent-opt.$(OBJEXT) \ + libcommonpth_a-helpfile.$(OBJEXT) \ + libcommonpth_a-mkdir_p.$(OBJEXT) \ + libcommonpth_a-strlist.$(OBJEXT) \ + libcommonpth_a-exectool.$(OBJEXT) \ + libcommonpth_a-server-help.$(OBJEXT) \ + libcommonpth_a-name-value.$(OBJEXT) \ + libcommonpth_a-recsel.$(OBJEXT) $(am__objects_7) \ + $(am__objects_8) $(am__objects_9) $(am__objects_10) +am__objects_12 = libcommonpth_a-call-gpg.$(OBJEXT) +am_libcommonpth_a_OBJECTS = $(am__objects_11) $(am__objects_12) +libcommonpth_a_OBJECTS = $(am_libcommonpth_a_OBJECTS) +libgpgrl_a_AR = $(AR) $(ARFLAGS) +libgpgrl_a_LIBADD = +am_libgpgrl_a_OBJECTS = gpgrlhelp.$(OBJEXT) +libgpgrl_a_OBJECTS = $(am_libgpgrl_a_OBJECTS) +libsimple_pwquery_a_AR = $(AR) $(ARFLAGS) +libsimple_pwquery_a_LIBADD = +am__libsimple_pwquery_a_SOURCES_DIST = simple-pwquery.c \ + simple-pwquery.h asshelp.c asshelp.h +@HAVE_W32CE_SYSTEM_FALSE@am_libsimple_pwquery_a_OBJECTS = libsimple_pwquery_a-simple-pwquery.$(OBJEXT) \ +@HAVE_W32CE_SYSTEM_FALSE@ libsimple_pwquery_a-asshelp.$(OBJEXT) +libsimple_pwquery_a_OBJECTS = $(am_libsimple_pwquery_a_OBJECTS) +@HAVE_W32CE_SYSTEM_FALSE@am__EXEEXT_1 = t-exechelp$(EXEEXT) \ +@HAVE_W32CE_SYSTEM_FALSE@ t-exectool$(EXEEXT) +@HAVE_W32_SYSTEM_TRUE@am__EXEEXT_2 = t-w32-reg$(EXEEXT) +am__EXEEXT_3 = t-stringhelp$(EXEEXT) t-timestuff$(EXEEXT) \ + t-convert$(EXEEXT) t-percent$(EXEEXT) t-gettime$(EXEEXT) \ + t-sysutils$(EXEEXT) t-sexputil$(EXEEXT) t-session-env$(EXEEXT) \ + t-openpgp-oid$(EXEEXT) t-ssh-utils$(EXEEXT) \ + t-mapstrings$(EXEEXT) t-zb32$(EXEEXT) t-mbox-util$(EXEEXT) \ + t-iobuf$(EXEEXT) t-strlist$(EXEEXT) t-name-value$(EXEEXT) \ + t-ccparray$(EXEEXT) t-recsel$(EXEEXT) $(am__EXEEXT_1) \ + $(am__EXEEXT_2) +@MAINTAINER_MODE_TRUE@am__EXEEXT_4 = t-helpfile$(EXEEXT) \ +@MAINTAINER_MODE_TRUE@ t-b64$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +t_b64_SOURCES = t-b64.c +t_b64_OBJECTS = t-b64.$(OBJEXT) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = libcommon.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +t_b64_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_ccparray_SOURCES = t-ccparray.c +t_ccparray_OBJECTS = t-ccparray.$(OBJEXT) +t_ccparray_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_convert_SOURCES = t-convert.c +t_convert_OBJECTS = t-convert.$(OBJEXT) +t_convert_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_exechelp_SOURCES = t-exechelp.c +t_exechelp_OBJECTS = t-exechelp.$(OBJEXT) +t_exechelp_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_exectool_SOURCES = t-exectool.c +t_exectool_OBJECTS = t-exectool.$(OBJEXT) +t_exectool_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_gettime_SOURCES = t-gettime.c +t_gettime_OBJECTS = t-gettime.$(OBJEXT) +t_gettime_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_helpfile_SOURCES = t-helpfile.c +t_helpfile_OBJECTS = t-helpfile.$(OBJEXT) +t_helpfile_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_iobuf_SOURCES = t-iobuf.c +t_iobuf_OBJECTS = t-iobuf.$(OBJEXT) +t_iobuf_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_mapstrings_SOURCES = t-mapstrings.c +t_mapstrings_OBJECTS = t-mapstrings.$(OBJEXT) +t_mapstrings_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_mbox_util_SOURCES = t-mbox-util.c +t_mbox_util_OBJECTS = t-mbox-util.$(OBJEXT) +t_mbox_util_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_name_value_SOURCES = t-name-value.c +t_name_value_OBJECTS = t-name-value.$(OBJEXT) +t_name_value_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_openpgp_oid_SOURCES = t-openpgp-oid.c +t_openpgp_oid_OBJECTS = t-openpgp-oid.$(OBJEXT) +t_openpgp_oid_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_percent_SOURCES = t-percent.c +t_percent_OBJECTS = t-percent.$(OBJEXT) +t_percent_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_recsel_SOURCES = t-recsel.c +t_recsel_OBJECTS = t-recsel.$(OBJEXT) +t_recsel_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_session_env_SOURCES = t-session-env.c +t_session_env_OBJECTS = t-session-env.$(OBJEXT) +t_session_env_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_sexputil_SOURCES = t-sexputil.c +t_sexputil_OBJECTS = t-sexputil.$(OBJEXT) +t_sexputil_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_ssh_utils_SOURCES = t-ssh-utils.c +t_ssh_utils_OBJECTS = t-ssh-utils.$(OBJEXT) +t_ssh_utils_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__objects_13 = +am_t_stringhelp_OBJECTS = t-stringhelp.$(OBJEXT) $(am__objects_13) +t_stringhelp_OBJECTS = $(am_t_stringhelp_OBJECTS) +t_stringhelp_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_strlist_SOURCES = t-strlist.c +t_strlist_OBJECTS = t-strlist.$(OBJEXT) +t_strlist_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_sysutils_SOURCES = t-sysutils.c +t_sysutils_OBJECTS = t-sysutils.$(OBJEXT) +t_sysutils_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_t_timestuff_OBJECTS = t-timestuff.$(OBJEXT) $(am__objects_13) +t_timestuff_OBJECTS = $(am_t_timestuff_OBJECTS) +t_timestuff_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__t_w32_reg_SOURCES_DIST = t-w32-reg.c t-support.h +@HAVE_W32_SYSTEM_TRUE@am_t_w32_reg_OBJECTS = t-w32-reg.$(OBJEXT) \ +@HAVE_W32_SYSTEM_TRUE@ $(am__objects_13) +t_w32_reg_OBJECTS = $(am_t_w32_reg_OBJECTS) +@HAVE_W32_SYSTEM_TRUE@t_w32_reg_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_t_zb32_OBJECTS = t-zb32.$(OBJEXT) $(am__objects_13) +t_zb32_OBJECTS = $(am_t_zb32_OBJECTS) +t_zb32_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcommon_a_SOURCES) $(libcommonpth_a_SOURCES) \ + $(libgpgrl_a_SOURCES) $(libsimple_pwquery_a_SOURCES) t-b64.c \ + t-ccparray.c t-convert.c t-exechelp.c t-exectool.c t-gettime.c \ + t-helpfile.c t-iobuf.c t-mapstrings.c t-mbox-util.c \ + t-name-value.c t-openpgp-oid.c t-percent.c t-recsel.c \ + t-session-env.c t-sexputil.c t-ssh-utils.c \ + $(t_stringhelp_SOURCES) t-strlist.c t-sysutils.c \ + $(t_timestuff_SOURCES) $(t_w32_reg_SOURCES) $(t_zb32_SOURCES) +DIST_SOURCES = $(am__libcommon_a_SOURCES_DIST) \ + $(am__libcommonpth_a_SOURCES_DIST) $(libgpgrl_a_SOURCES) \ + $(am__libsimple_pwquery_a_SOURCES_DIST) t-b64.c t-ccparray.c \ + t-convert.c t-exechelp.c t-exectool.c t-gettime.c t-helpfile.c \ + t-iobuf.c t-mapstrings.c t-mbox-util.c t-name-value.c \ + t-openpgp-oid.c t-percent.c t-recsel.c t-session-env.c \ + t-sexputil.c t-ssh-utils.c $(t_stringhelp_SOURCES) t-strlist.c \ + t-sysutils.c $(t_timestuff_SOURCES) \ + $(am__t_w32_reg_SOURCES_DIST) $(t_zb32_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_FILEVERSION = @BUILD_FILEVERSION@ +BUILD_HOSTNAME = @BUILD_HOSTNAME@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BUILD_REVISION = @BUILD_REVISION@ +BUILD_TIMESTAMP = @BUILD_TIMESTAMP@ +BUILD_VERSION = @BUILD_VERSION@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DL_LIBS = @DL_LIBS@ +DNSLIBS = @DNSLIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENCFS = @ENCFS@ +EXEEXT = @EXEEXT@ +FUSERMOUNT = @FUSERMOUNT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@ +GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@ +GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@ +GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ +GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ +GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ +GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ +GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ +GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ +GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@ +GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KSBA_CFLAGS = @KSBA_CFLAGS@ +KSBA_CONFIG = @KSBA_CONFIG@ +KSBA_LIBS = @KSBA_LIBS@ +LBER_LIBS = @LBER_LIBS@ +LDAPLIBS = @LDAPLIBS@ +LDAP_CPPFLAGS = @LDAP_CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@ +LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@ +LIBASSUAN_LIBS = @LIBASSUAN_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ +LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBREADLINE = @LIBREADLINE@ +LIBS = @LIBS@ +LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBUTIL_LIBS = @LIBUTIL_LIBS@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NETLIBS = @NETLIBS@ +NPTH_CFLAGS = @NPTH_CFLAGS@ +NPTH_CONFIG = @NPTH_CONFIG@ +NPTH_LIBS = @NPTH_LIBS@ +NTBTLS_CFLAGS = @NTBTLS_CFLAGS@ +NTBTLS_CONFIG = @NTBTLS_CONFIG@ +NTBTLS_LIBS = @NTBTLS_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_GT = @PACKAGE_GT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHRED = @SHRED@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ +STRIP = @STRIP@ +SYSROOT = @SYSROOT@ +SYS_SOCKET_H = @SYS_SOCKET_H@ +TAR = @TAR@ +USE_C99_CFLAGS = @USE_C99_CFLAGS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +W32SOCKLIBS = @W32SOCKLIBS@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZLIBS = @ZLIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = $(datadir)/locale +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ + audit-events.h status-codes.h ChangeLog.jnlib \ + ChangeLog-2011.include w32info-rc.h.in gnupg.ico + +noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a \ + $(am__append_1) +BUILT_SOURCES = audit-events.h status-codes.h +MAINTAINERCLEANFILES = audit-events.h status-codes.h + +# NB: AM_CFLAGS may also be used by tools running on the build +# platform to create source files. +AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" $(am__append_2) \ + $(am__append_3) $(am__append_4) $(am__append_5) \ + $(am__append_6) $(am__append_7) $(am__append_8) +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) +@HAVE_W32CE_SYSTEM_FALSE@extra_sys_libs = + +# Under Windows we use LockFileEx. WindowsCE provides this only on +# the WindowsMobile 6 platform and thus we need to use the coredll6 +# import library. We also want to use a stacksize of 256k instead of +# the 2MB which is the default with cegcc. 256k is the largest stack +# we use with pth. +@HAVE_W32CE_SYSTEM_TRUE@extra_sys_libs = -lcoredll6 +@HAVE_W32CE_SYSTEM_FALSE@extra_bin_ldflags = +@HAVE_W32CE_SYSTEM_TRUE@extra_bin_ldflags = -Wl,--stack=0x40000 +resource_objs = + +# Convenience macros +libcommon = ../common/libcommon.a +libcommonpth = ../common/libcommonpth.a +libcommontls = ../common/libcommontls.a +libcommontlsnpth = ../common/libcommontlsnpth.a +common_sources = common-defs.h util.h utilproto.h fwddecl.h i18n.c \ + i18n.h types.h host2net.h dynload.h w32help.h mapstrings.c \ + stringhelp.c stringhelp.h strlist.c strlist.h utf8conv.c \ + utf8conv.h argparse.c argparse.h logging.c logging.h dotlock.c \ + dotlock.h mischelp.c mischelp.h status.c status.h shareddefs.h \ + openpgpdefs.h gc-opt-flags.h keyserver.h sexp-parse.h tlv.c \ + tlv.h init.c init.h sexputil.c sysutils.c sysutils.h homedir.c \ + gettime.c gettime.h yesno.c b64enc.c b64dec.c zb32.c zb32.h \ + convert.c percent.c mbox-util.c mbox-util.h miscellaneous.c \ + xasprintf.c xreadline.c membuf.c membuf.h ccparray.c \ + ccparray.h iobuf.c iobuf.h ttyio.c ttyio.h asshelp.c \ + asshelp2.c asshelp.h exechelp.h signal.c audit.c audit.h \ + localename.c session-env.c session-env.h userids.c userids.h \ + openpgp-oid.c ssh-utils.c ssh-utils.h agent-opt.c helpfile.c \ + mkdir_p.c mkdir_p.h strlist.c strlist.h exectool.c exectool.h \ + server-help.c server-help.h name-value.c name-value.h recsel.c \ + recsel.h $(am__append_9) $(am__append_10) $(am__append_11) \ + $(am__append_12) + +# Sources only useful without NPTH. +without_npth_sources = \ + get-passphrase.c get-passphrase.h + + +# Sources only useful with NPTH. +with_npth_sources = \ + call-gpg.c call-gpg.h + +libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) +libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 +libcommonpth_a_SOURCES = $(common_sources) $(with_npth_sources) +libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) +@HAVE_W32CE_SYSTEM_FALSE@libsimple_pwquery_a_SOURCES = \ +@HAVE_W32CE_SYSTEM_FALSE@ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h + +@HAVE_W32CE_SYSTEM_FALSE@libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) +libgpgrl_a_SOURCES = \ + gpgrlhelp.c + + +# +# Module tests +# +module_tests = t-stringhelp t-timestuff t-convert t-percent t-gettime \ + t-sysutils t-sexputil t-session-env t-openpgp-oid t-ssh-utils \ + t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist t-name-value \ + t-ccparray t-recsel $(am__append_13) $(am__append_14) +@MAINTAINER_MODE_FALSE@module_maint_tests = +@MAINTAINER_MODE_TRUE@module_maint_tests = t-helpfile t-b64 +t_extra_src = t-support.h +t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) + +t_common_ldadd = libcommon.a \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) + + +# Common tests +t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) +t_stringhelp_LDADD = $(t_common_ldadd) +t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) +t_timestuff_LDADD = $(t_common_ldadd) +t_convert_LDADD = $(t_common_ldadd) +t_percent_LDADD = $(t_common_ldadd) +t_gettime_LDADD = $(t_common_ldadd) +t_sysutils_LDADD = $(t_common_ldadd) +t_helpfile_LDADD = $(t_common_ldadd) +t_sexputil_LDADD = $(t_common_ldadd) +t_b64_LDADD = $(t_common_ldadd) +t_exechelp_LDADD = $(t_common_ldadd) +t_exectool_LDADD = $(t_common_ldadd) +t_session_env_LDADD = $(t_common_ldadd) +t_openpgp_oid_LDADD = $(t_common_ldadd) +t_ssh_utils_LDADD = $(t_common_ldadd) +t_mapstrings_LDADD = $(t_common_ldadd) +t_zb32_SOURCES = t-zb32.c $(t_extra_src) +t_zb32_LDADD = $(t_common_ldadd) +t_mbox_util_LDADD = $(t_common_ldadd) +t_iobuf_LDADD = $(t_common_ldadd) +t_strlist_LDADD = $(t_common_ldadd) +t_name_value_LDADD = $(t_common_ldadd) +t_ccparray_LDADD = $(t_common_ldadd) +t_recsel_LDADD = $(t_common_ldadd) + +# System specific test +@HAVE_W32_SYSTEM_TRUE@t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) +@HAVE_W32_SYSTEM_TRUE@t_w32_reg_LDADD = $(t_common_ldadd) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj .rc +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu common/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu common/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(top_srcdir)/am/cmacros.am: + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +w32info-rc.h: $(top_builddir)/config.status $(srcdir)/w32info-rc.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libcommon.a: $(libcommon_a_OBJECTS) $(libcommon_a_DEPENDENCIES) $(EXTRA_libcommon_a_DEPENDENCIES) + $(AM_V_at)-rm -f libcommon.a + $(AM_V_AR)$(libcommon_a_AR) libcommon.a $(libcommon_a_OBJECTS) $(libcommon_a_LIBADD) + $(AM_V_at)$(RANLIB) libcommon.a + +libcommonpth.a: $(libcommonpth_a_OBJECTS) $(libcommonpth_a_DEPENDENCIES) $(EXTRA_libcommonpth_a_DEPENDENCIES) + $(AM_V_at)-rm -f libcommonpth.a + $(AM_V_AR)$(libcommonpth_a_AR) libcommonpth.a $(libcommonpth_a_OBJECTS) $(libcommonpth_a_LIBADD) + $(AM_V_at)$(RANLIB) libcommonpth.a + +libgpgrl.a: $(libgpgrl_a_OBJECTS) $(libgpgrl_a_DEPENDENCIES) $(EXTRA_libgpgrl_a_DEPENDENCIES) + $(AM_V_at)-rm -f libgpgrl.a + $(AM_V_AR)$(libgpgrl_a_AR) libgpgrl.a $(libgpgrl_a_OBJECTS) $(libgpgrl_a_LIBADD) + $(AM_V_at)$(RANLIB) libgpgrl.a + +libsimple-pwquery.a: $(libsimple_pwquery_a_OBJECTS) $(libsimple_pwquery_a_DEPENDENCIES) $(EXTRA_libsimple_pwquery_a_DEPENDENCIES) + $(AM_V_at)-rm -f libsimple-pwquery.a + $(AM_V_AR)$(libsimple_pwquery_a_AR) libsimple-pwquery.a $(libsimple_pwquery_a_OBJECTS) $(libsimple_pwquery_a_LIBADD) + $(AM_V_at)$(RANLIB) libsimple-pwquery.a + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + +t-b64$(EXEEXT): $(t_b64_OBJECTS) $(t_b64_DEPENDENCIES) $(EXTRA_t_b64_DEPENDENCIES) + @rm -f t-b64$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_b64_OBJECTS) $(t_b64_LDADD) $(LIBS) + +t-ccparray$(EXEEXT): $(t_ccparray_OBJECTS) $(t_ccparray_DEPENDENCIES) $(EXTRA_t_ccparray_DEPENDENCIES) + @rm -f t-ccparray$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_ccparray_OBJECTS) $(t_ccparray_LDADD) $(LIBS) + +t-convert$(EXEEXT): $(t_convert_OBJECTS) $(t_convert_DEPENDENCIES) $(EXTRA_t_convert_DEPENDENCIES) + @rm -f t-convert$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_convert_OBJECTS) $(t_convert_LDADD) $(LIBS) + +t-exechelp$(EXEEXT): $(t_exechelp_OBJECTS) $(t_exechelp_DEPENDENCIES) $(EXTRA_t_exechelp_DEPENDENCIES) + @rm -f t-exechelp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_exechelp_OBJECTS) $(t_exechelp_LDADD) $(LIBS) + +t-exectool$(EXEEXT): $(t_exectool_OBJECTS) $(t_exectool_DEPENDENCIES) $(EXTRA_t_exectool_DEPENDENCIES) + @rm -f t-exectool$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_exectool_OBJECTS) $(t_exectool_LDADD) $(LIBS) + +t-gettime$(EXEEXT): $(t_gettime_OBJECTS) $(t_gettime_DEPENDENCIES) $(EXTRA_t_gettime_DEPENDENCIES) + @rm -f t-gettime$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_gettime_OBJECTS) $(t_gettime_LDADD) $(LIBS) + +t-helpfile$(EXEEXT): $(t_helpfile_OBJECTS) $(t_helpfile_DEPENDENCIES) $(EXTRA_t_helpfile_DEPENDENCIES) + @rm -f t-helpfile$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_helpfile_OBJECTS) $(t_helpfile_LDADD) $(LIBS) + +t-iobuf$(EXEEXT): $(t_iobuf_OBJECTS) $(t_iobuf_DEPENDENCIES) $(EXTRA_t_iobuf_DEPENDENCIES) + @rm -f t-iobuf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_iobuf_OBJECTS) $(t_iobuf_LDADD) $(LIBS) + +t-mapstrings$(EXEEXT): $(t_mapstrings_OBJECTS) $(t_mapstrings_DEPENDENCIES) $(EXTRA_t_mapstrings_DEPENDENCIES) + @rm -f t-mapstrings$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_mapstrings_OBJECTS) $(t_mapstrings_LDADD) $(LIBS) + +t-mbox-util$(EXEEXT): $(t_mbox_util_OBJECTS) $(t_mbox_util_DEPENDENCIES) $(EXTRA_t_mbox_util_DEPENDENCIES) + @rm -f t-mbox-util$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_mbox_util_OBJECTS) $(t_mbox_util_LDADD) $(LIBS) + +t-name-value$(EXEEXT): $(t_name_value_OBJECTS) $(t_name_value_DEPENDENCIES) $(EXTRA_t_name_value_DEPENDENCIES) + @rm -f t-name-value$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_name_value_OBJECTS) $(t_name_value_LDADD) $(LIBS) + +t-openpgp-oid$(EXEEXT): $(t_openpgp_oid_OBJECTS) $(t_openpgp_oid_DEPENDENCIES) $(EXTRA_t_openpgp_oid_DEPENDENCIES) + @rm -f t-openpgp-oid$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_openpgp_oid_OBJECTS) $(t_openpgp_oid_LDADD) $(LIBS) + +t-percent$(EXEEXT): $(t_percent_OBJECTS) $(t_percent_DEPENDENCIES) $(EXTRA_t_percent_DEPENDENCIES) + @rm -f t-percent$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_percent_OBJECTS) $(t_percent_LDADD) $(LIBS) + +t-recsel$(EXEEXT): $(t_recsel_OBJECTS) $(t_recsel_DEPENDENCIES) $(EXTRA_t_recsel_DEPENDENCIES) + @rm -f t-recsel$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_recsel_OBJECTS) $(t_recsel_LDADD) $(LIBS) + +t-session-env$(EXEEXT): $(t_session_env_OBJECTS) $(t_session_env_DEPENDENCIES) $(EXTRA_t_session_env_DEPENDENCIES) + @rm -f t-session-env$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_session_env_OBJECTS) $(t_session_env_LDADD) $(LIBS) + +t-sexputil$(EXEEXT): $(t_sexputil_OBJECTS) $(t_sexputil_DEPENDENCIES) $(EXTRA_t_sexputil_DEPENDENCIES) + @rm -f t-sexputil$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_sexputil_OBJECTS) $(t_sexputil_LDADD) $(LIBS) + +t-ssh-utils$(EXEEXT): $(t_ssh_utils_OBJECTS) $(t_ssh_utils_DEPENDENCIES) $(EXTRA_t_ssh_utils_DEPENDENCIES) + @rm -f t-ssh-utils$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_ssh_utils_OBJECTS) $(t_ssh_utils_LDADD) $(LIBS) + +t-stringhelp$(EXEEXT): $(t_stringhelp_OBJECTS) $(t_stringhelp_DEPENDENCIES) $(EXTRA_t_stringhelp_DEPENDENCIES) + @rm -f t-stringhelp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_stringhelp_OBJECTS) $(t_stringhelp_LDADD) $(LIBS) + +t-strlist$(EXEEXT): $(t_strlist_OBJECTS) $(t_strlist_DEPENDENCIES) $(EXTRA_t_strlist_DEPENDENCIES) + @rm -f t-strlist$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_strlist_OBJECTS) $(t_strlist_LDADD) $(LIBS) + +t-sysutils$(EXEEXT): $(t_sysutils_OBJECTS) $(t_sysutils_DEPENDENCIES) $(EXTRA_t_sysutils_DEPENDENCIES) + @rm -f t-sysutils$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_sysutils_OBJECTS) $(t_sysutils_LDADD) $(LIBS) + +t-timestuff$(EXEEXT): $(t_timestuff_OBJECTS) $(t_timestuff_DEPENDENCIES) $(EXTRA_t_timestuff_DEPENDENCIES) + @rm -f t-timestuff$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_timestuff_OBJECTS) $(t_timestuff_LDADD) $(LIBS) + +t-w32-reg$(EXEEXT): $(t_w32_reg_OBJECTS) $(t_w32_reg_DEPENDENCIES) $(EXTRA_t_w32_reg_DEPENDENCIES) + @rm -f t-w32-reg$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_w32_reg_OBJECTS) $(t_w32_reg_LDADD) $(LIBS) + +t-zb32$(EXEEXT): $(t_zb32_OBJECTS) $(t_zb32_DEPENDENCIES) $(EXTRA_t_zb32_DEPENDENCIES) + @rm -f t-zb32$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_zb32_OBJECTS) $(t_zb32_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgrlhelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-agent-opt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-argparse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-asshelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-asshelp2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-audit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-b64dec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-b64enc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ccparray.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-convert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-dotlock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exechelp-posix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exechelp-w32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exechelp-w32ce.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exectool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-get-passphrase.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-gettime.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-helpfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-homedir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-i18n.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-init.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-iobuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-localename.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-logging.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mapstrings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mbox-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-membuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-miscellaneous.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mischelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mkdir_p.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-name-value.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-openpgp-oid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-percent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-recsel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-server-help.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-session-env.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-sexputil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-signal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ssh-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-status.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-stringhelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-strlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-sysutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-tlv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ttyio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-userids.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-utf8conv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-w32-reg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-xasprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-xreadline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-yesno.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-zb32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-agent-opt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-argparse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-asshelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-asshelp2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-audit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-b64dec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-b64enc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-call-gpg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ccparray.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-convert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-dotlock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exechelp-posix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exechelp-w32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exectool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-gettime.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-helpfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-homedir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-i18n.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-init.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-iobuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-localename.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-logging.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mapstrings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mbox-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-membuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-miscellaneous.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mischelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mkdir_p.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-name-value.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-openpgp-oid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-percent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-recsel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-server-help.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-session-env.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-sexputil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-signal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ssh-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-status.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-stringhelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-strlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-sysutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-tlv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ttyio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-userids.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-utf8conv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-w32-reg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-xasprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-xreadline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-yesno.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-zb32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsimple_pwquery_a-asshelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-b64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-ccparray.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-convert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-exechelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-exectool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-gettime.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-helpfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-iobuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-mapstrings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-mbox-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-name-value.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-openpgp-oid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-percent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-recsel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-session-env.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-sexputil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-ssh-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-stringhelp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-strlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-sysutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-timestuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-w32-reg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-zb32.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +libcommon_a-i18n.o: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-i18n.o -MD -MP -MF $(DEPDIR)/libcommon_a-i18n.Tpo -c -o libcommon_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-i18n.Tpo $(DEPDIR)/libcommon_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommon_a-i18n.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c + +libcommon_a-i18n.obj: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-i18n.obj -MD -MP -MF $(DEPDIR)/libcommon_a-i18n.Tpo -c -o libcommon_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-i18n.Tpo $(DEPDIR)/libcommon_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommon_a-i18n.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` + +libcommon_a-mapstrings.o: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mapstrings.o -MD -MP -MF $(DEPDIR)/libcommon_a-mapstrings.Tpo -c -o libcommon_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mapstrings.Tpo $(DEPDIR)/libcommon_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommon_a-mapstrings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c + +libcommon_a-mapstrings.obj: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mapstrings.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mapstrings.Tpo -c -o libcommon_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mapstrings.Tpo $(DEPDIR)/libcommon_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommon_a-mapstrings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` + +libcommon_a-stringhelp.o: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-stringhelp.o -MD -MP -MF $(DEPDIR)/libcommon_a-stringhelp.Tpo -c -o libcommon_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-stringhelp.Tpo $(DEPDIR)/libcommon_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommon_a-stringhelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c + +libcommon_a-stringhelp.obj: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-stringhelp.obj -MD -MP -MF $(DEPDIR)/libcommon_a-stringhelp.Tpo -c -o libcommon_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-stringhelp.Tpo $(DEPDIR)/libcommon_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommon_a-stringhelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` + +libcommon_a-strlist.o: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-strlist.o -MD -MP -MF $(DEPDIR)/libcommon_a-strlist.Tpo -c -o libcommon_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-strlist.Tpo $(DEPDIR)/libcommon_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommon_a-strlist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c + +libcommon_a-strlist.obj: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-strlist.obj -MD -MP -MF $(DEPDIR)/libcommon_a-strlist.Tpo -c -o libcommon_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-strlist.Tpo $(DEPDIR)/libcommon_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommon_a-strlist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` + +libcommon_a-utf8conv.o: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-utf8conv.o -MD -MP -MF $(DEPDIR)/libcommon_a-utf8conv.Tpo -c -o libcommon_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-utf8conv.Tpo $(DEPDIR)/libcommon_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommon_a-utf8conv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c + +libcommon_a-utf8conv.obj: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-utf8conv.obj -MD -MP -MF $(DEPDIR)/libcommon_a-utf8conv.Tpo -c -o libcommon_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-utf8conv.Tpo $(DEPDIR)/libcommon_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommon_a-utf8conv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` + +libcommon_a-argparse.o: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-argparse.o -MD -MP -MF $(DEPDIR)/libcommon_a-argparse.Tpo -c -o libcommon_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-argparse.Tpo $(DEPDIR)/libcommon_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommon_a-argparse.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c + +libcommon_a-argparse.obj: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-argparse.obj -MD -MP -MF $(DEPDIR)/libcommon_a-argparse.Tpo -c -o libcommon_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-argparse.Tpo $(DEPDIR)/libcommon_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommon_a-argparse.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` + +libcommon_a-logging.o: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-logging.o -MD -MP -MF $(DEPDIR)/libcommon_a-logging.Tpo -c -o libcommon_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-logging.Tpo $(DEPDIR)/libcommon_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommon_a-logging.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c + +libcommon_a-logging.obj: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-logging.obj -MD -MP -MF $(DEPDIR)/libcommon_a-logging.Tpo -c -o libcommon_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-logging.Tpo $(DEPDIR)/libcommon_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommon_a-logging.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` + +libcommon_a-dotlock.o: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-dotlock.o -MD -MP -MF $(DEPDIR)/libcommon_a-dotlock.Tpo -c -o libcommon_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-dotlock.Tpo $(DEPDIR)/libcommon_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommon_a-dotlock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c + +libcommon_a-dotlock.obj: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-dotlock.obj -MD -MP -MF $(DEPDIR)/libcommon_a-dotlock.Tpo -c -o libcommon_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-dotlock.Tpo $(DEPDIR)/libcommon_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommon_a-dotlock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` + +libcommon_a-mischelp.o: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mischelp.o -MD -MP -MF $(DEPDIR)/libcommon_a-mischelp.Tpo -c -o libcommon_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mischelp.Tpo $(DEPDIR)/libcommon_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommon_a-mischelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c + +libcommon_a-mischelp.obj: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mischelp.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mischelp.Tpo -c -o libcommon_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mischelp.Tpo $(DEPDIR)/libcommon_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommon_a-mischelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` + +libcommon_a-status.o: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-status.o -MD -MP -MF $(DEPDIR)/libcommon_a-status.Tpo -c -o libcommon_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-status.Tpo $(DEPDIR)/libcommon_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommon_a-status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c + +libcommon_a-status.obj: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-status.obj -MD -MP -MF $(DEPDIR)/libcommon_a-status.Tpo -c -o libcommon_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-status.Tpo $(DEPDIR)/libcommon_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommon_a-status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` + +libcommon_a-tlv.o: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-tlv.o -MD -MP -MF $(DEPDIR)/libcommon_a-tlv.Tpo -c -o libcommon_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-tlv.Tpo $(DEPDIR)/libcommon_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommon_a-tlv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c + +libcommon_a-tlv.obj: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-tlv.obj -MD -MP -MF $(DEPDIR)/libcommon_a-tlv.Tpo -c -o libcommon_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-tlv.Tpo $(DEPDIR)/libcommon_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommon_a-tlv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` + +libcommon_a-init.o: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-init.o -MD -MP -MF $(DEPDIR)/libcommon_a-init.Tpo -c -o libcommon_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-init.Tpo $(DEPDIR)/libcommon_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommon_a-init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c + +libcommon_a-init.obj: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-init.obj -MD -MP -MF $(DEPDIR)/libcommon_a-init.Tpo -c -o libcommon_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-init.Tpo $(DEPDIR)/libcommon_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommon_a-init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` + +libcommon_a-sexputil.o: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sexputil.o -MD -MP -MF $(DEPDIR)/libcommon_a-sexputil.Tpo -c -o libcommon_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sexputil.Tpo $(DEPDIR)/libcommon_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommon_a-sexputil.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c + +libcommon_a-sexputil.obj: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sexputil.obj -MD -MP -MF $(DEPDIR)/libcommon_a-sexputil.Tpo -c -o libcommon_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sexputil.Tpo $(DEPDIR)/libcommon_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommon_a-sexputil.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` + +libcommon_a-sysutils.o: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sysutils.o -MD -MP -MF $(DEPDIR)/libcommon_a-sysutils.Tpo -c -o libcommon_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sysutils.Tpo $(DEPDIR)/libcommon_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommon_a-sysutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c + +libcommon_a-sysutils.obj: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sysutils.obj -MD -MP -MF $(DEPDIR)/libcommon_a-sysutils.Tpo -c -o libcommon_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sysutils.Tpo $(DEPDIR)/libcommon_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommon_a-sysutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` + +libcommon_a-homedir.o: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-homedir.o -MD -MP -MF $(DEPDIR)/libcommon_a-homedir.Tpo -c -o libcommon_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-homedir.Tpo $(DEPDIR)/libcommon_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommon_a-homedir.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c + +libcommon_a-homedir.obj: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-homedir.obj -MD -MP -MF $(DEPDIR)/libcommon_a-homedir.Tpo -c -o libcommon_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-homedir.Tpo $(DEPDIR)/libcommon_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommon_a-homedir.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` + +libcommon_a-gettime.o: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-gettime.o -MD -MP -MF $(DEPDIR)/libcommon_a-gettime.Tpo -c -o libcommon_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-gettime.Tpo $(DEPDIR)/libcommon_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommon_a-gettime.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c + +libcommon_a-gettime.obj: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-gettime.obj -MD -MP -MF $(DEPDIR)/libcommon_a-gettime.Tpo -c -o libcommon_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-gettime.Tpo $(DEPDIR)/libcommon_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommon_a-gettime.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` + +libcommon_a-yesno.o: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-yesno.o -MD -MP -MF $(DEPDIR)/libcommon_a-yesno.Tpo -c -o libcommon_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-yesno.Tpo $(DEPDIR)/libcommon_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommon_a-yesno.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c + +libcommon_a-yesno.obj: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-yesno.obj -MD -MP -MF $(DEPDIR)/libcommon_a-yesno.Tpo -c -o libcommon_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-yesno.Tpo $(DEPDIR)/libcommon_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommon_a-yesno.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` + +libcommon_a-b64enc.o: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64enc.o -MD -MP -MF $(DEPDIR)/libcommon_a-b64enc.Tpo -c -o libcommon_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64enc.Tpo $(DEPDIR)/libcommon_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommon_a-b64enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c + +libcommon_a-b64enc.obj: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64enc.obj -MD -MP -MF $(DEPDIR)/libcommon_a-b64enc.Tpo -c -o libcommon_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64enc.Tpo $(DEPDIR)/libcommon_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommon_a-b64enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` + +libcommon_a-b64dec.o: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64dec.o -MD -MP -MF $(DEPDIR)/libcommon_a-b64dec.Tpo -c -o libcommon_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64dec.Tpo $(DEPDIR)/libcommon_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommon_a-b64dec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c + +libcommon_a-b64dec.obj: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64dec.obj -MD -MP -MF $(DEPDIR)/libcommon_a-b64dec.Tpo -c -o libcommon_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64dec.Tpo $(DEPDIR)/libcommon_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommon_a-b64dec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` + +libcommon_a-zb32.o: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-zb32.o -MD -MP -MF $(DEPDIR)/libcommon_a-zb32.Tpo -c -o libcommon_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-zb32.Tpo $(DEPDIR)/libcommon_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommon_a-zb32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c + +libcommon_a-zb32.obj: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-zb32.obj -MD -MP -MF $(DEPDIR)/libcommon_a-zb32.Tpo -c -o libcommon_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-zb32.Tpo $(DEPDIR)/libcommon_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommon_a-zb32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` + +libcommon_a-convert.o: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-convert.o -MD -MP -MF $(DEPDIR)/libcommon_a-convert.Tpo -c -o libcommon_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-convert.Tpo $(DEPDIR)/libcommon_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommon_a-convert.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c + +libcommon_a-convert.obj: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-convert.obj -MD -MP -MF $(DEPDIR)/libcommon_a-convert.Tpo -c -o libcommon_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-convert.Tpo $(DEPDIR)/libcommon_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommon_a-convert.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` + +libcommon_a-percent.o: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-percent.o -MD -MP -MF $(DEPDIR)/libcommon_a-percent.Tpo -c -o libcommon_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-percent.Tpo $(DEPDIR)/libcommon_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommon_a-percent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c + +libcommon_a-percent.obj: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-percent.obj -MD -MP -MF $(DEPDIR)/libcommon_a-percent.Tpo -c -o libcommon_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-percent.Tpo $(DEPDIR)/libcommon_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommon_a-percent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` + +libcommon_a-mbox-util.o: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mbox-util.o -MD -MP -MF $(DEPDIR)/libcommon_a-mbox-util.Tpo -c -o libcommon_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mbox-util.Tpo $(DEPDIR)/libcommon_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommon_a-mbox-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c + +libcommon_a-mbox-util.obj: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mbox-util.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mbox-util.Tpo -c -o libcommon_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mbox-util.Tpo $(DEPDIR)/libcommon_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommon_a-mbox-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` + +libcommon_a-miscellaneous.o: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-miscellaneous.o -MD -MP -MF $(DEPDIR)/libcommon_a-miscellaneous.Tpo -c -o libcommon_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-miscellaneous.Tpo $(DEPDIR)/libcommon_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommon_a-miscellaneous.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c + +libcommon_a-miscellaneous.obj: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-miscellaneous.obj -MD -MP -MF $(DEPDIR)/libcommon_a-miscellaneous.Tpo -c -o libcommon_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-miscellaneous.Tpo $(DEPDIR)/libcommon_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommon_a-miscellaneous.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` + +libcommon_a-xasprintf.o: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xasprintf.o -MD -MP -MF $(DEPDIR)/libcommon_a-xasprintf.Tpo -c -o libcommon_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xasprintf.Tpo $(DEPDIR)/libcommon_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommon_a-xasprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c + +libcommon_a-xasprintf.obj: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xasprintf.obj -MD -MP -MF $(DEPDIR)/libcommon_a-xasprintf.Tpo -c -o libcommon_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xasprintf.Tpo $(DEPDIR)/libcommon_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommon_a-xasprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` + +libcommon_a-xreadline.o: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xreadline.o -MD -MP -MF $(DEPDIR)/libcommon_a-xreadline.Tpo -c -o libcommon_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xreadline.Tpo $(DEPDIR)/libcommon_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommon_a-xreadline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c + +libcommon_a-xreadline.obj: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xreadline.obj -MD -MP -MF $(DEPDIR)/libcommon_a-xreadline.Tpo -c -o libcommon_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xreadline.Tpo $(DEPDIR)/libcommon_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommon_a-xreadline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` + +libcommon_a-membuf.o: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-membuf.o -MD -MP -MF $(DEPDIR)/libcommon_a-membuf.Tpo -c -o libcommon_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-membuf.Tpo $(DEPDIR)/libcommon_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommon_a-membuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c + +libcommon_a-membuf.obj: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-membuf.obj -MD -MP -MF $(DEPDIR)/libcommon_a-membuf.Tpo -c -o libcommon_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-membuf.Tpo $(DEPDIR)/libcommon_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommon_a-membuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` + +libcommon_a-ccparray.o: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ccparray.o -MD -MP -MF $(DEPDIR)/libcommon_a-ccparray.Tpo -c -o libcommon_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ccparray.Tpo $(DEPDIR)/libcommon_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommon_a-ccparray.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c + +libcommon_a-ccparray.obj: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ccparray.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ccparray.Tpo -c -o libcommon_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ccparray.Tpo $(DEPDIR)/libcommon_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommon_a-ccparray.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` + +libcommon_a-iobuf.o: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-iobuf.o -MD -MP -MF $(DEPDIR)/libcommon_a-iobuf.Tpo -c -o libcommon_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-iobuf.Tpo $(DEPDIR)/libcommon_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommon_a-iobuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c + +libcommon_a-iobuf.obj: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-iobuf.obj -MD -MP -MF $(DEPDIR)/libcommon_a-iobuf.Tpo -c -o libcommon_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-iobuf.Tpo $(DEPDIR)/libcommon_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommon_a-iobuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` + +libcommon_a-ttyio.o: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ttyio.o -MD -MP -MF $(DEPDIR)/libcommon_a-ttyio.Tpo -c -o libcommon_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ttyio.Tpo $(DEPDIR)/libcommon_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommon_a-ttyio.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c + +libcommon_a-ttyio.obj: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ttyio.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ttyio.Tpo -c -o libcommon_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ttyio.Tpo $(DEPDIR)/libcommon_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommon_a-ttyio.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` + +libcommon_a-asshelp.o: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp.o -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp.Tpo -c -o libcommon_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp.Tpo $(DEPDIR)/libcommon_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommon_a-asshelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c + +libcommon_a-asshelp.obj: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp.obj -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp.Tpo -c -o libcommon_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp.Tpo $(DEPDIR)/libcommon_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommon_a-asshelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` + +libcommon_a-asshelp2.o: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp2.o -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp2.Tpo -c -o libcommon_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp2.Tpo $(DEPDIR)/libcommon_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommon_a-asshelp2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c + +libcommon_a-asshelp2.obj: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp2.obj -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp2.Tpo -c -o libcommon_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp2.Tpo $(DEPDIR)/libcommon_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommon_a-asshelp2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` + +libcommon_a-signal.o: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-signal.o -MD -MP -MF $(DEPDIR)/libcommon_a-signal.Tpo -c -o libcommon_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-signal.Tpo $(DEPDIR)/libcommon_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommon_a-signal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c + +libcommon_a-signal.obj: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-signal.obj -MD -MP -MF $(DEPDIR)/libcommon_a-signal.Tpo -c -o libcommon_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-signal.Tpo $(DEPDIR)/libcommon_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommon_a-signal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` + +libcommon_a-audit.o: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-audit.o -MD -MP -MF $(DEPDIR)/libcommon_a-audit.Tpo -c -o libcommon_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-audit.Tpo $(DEPDIR)/libcommon_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommon_a-audit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c + +libcommon_a-audit.obj: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-audit.obj -MD -MP -MF $(DEPDIR)/libcommon_a-audit.Tpo -c -o libcommon_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-audit.Tpo $(DEPDIR)/libcommon_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommon_a-audit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` + +libcommon_a-localename.o: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-localename.o -MD -MP -MF $(DEPDIR)/libcommon_a-localename.Tpo -c -o libcommon_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-localename.Tpo $(DEPDIR)/libcommon_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommon_a-localename.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c + +libcommon_a-localename.obj: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-localename.obj -MD -MP -MF $(DEPDIR)/libcommon_a-localename.Tpo -c -o libcommon_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-localename.Tpo $(DEPDIR)/libcommon_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommon_a-localename.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` + +libcommon_a-session-env.o: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-session-env.o -MD -MP -MF $(DEPDIR)/libcommon_a-session-env.Tpo -c -o libcommon_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-session-env.Tpo $(DEPDIR)/libcommon_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommon_a-session-env.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c + +libcommon_a-session-env.obj: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-session-env.obj -MD -MP -MF $(DEPDIR)/libcommon_a-session-env.Tpo -c -o libcommon_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-session-env.Tpo $(DEPDIR)/libcommon_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommon_a-session-env.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` + +libcommon_a-userids.o: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-userids.o -MD -MP -MF $(DEPDIR)/libcommon_a-userids.Tpo -c -o libcommon_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-userids.Tpo $(DEPDIR)/libcommon_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommon_a-userids.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c + +libcommon_a-userids.obj: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-userids.obj -MD -MP -MF $(DEPDIR)/libcommon_a-userids.Tpo -c -o libcommon_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-userids.Tpo $(DEPDIR)/libcommon_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommon_a-userids.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` + +libcommon_a-openpgp-oid.o: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-openpgp-oid.o -MD -MP -MF $(DEPDIR)/libcommon_a-openpgp-oid.Tpo -c -o libcommon_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-openpgp-oid.Tpo $(DEPDIR)/libcommon_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommon_a-openpgp-oid.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c + +libcommon_a-openpgp-oid.obj: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-openpgp-oid.obj -MD -MP -MF $(DEPDIR)/libcommon_a-openpgp-oid.Tpo -c -o libcommon_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-openpgp-oid.Tpo $(DEPDIR)/libcommon_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommon_a-openpgp-oid.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` + +libcommon_a-ssh-utils.o: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ssh-utils.o -MD -MP -MF $(DEPDIR)/libcommon_a-ssh-utils.Tpo -c -o libcommon_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ssh-utils.Tpo $(DEPDIR)/libcommon_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommon_a-ssh-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c + +libcommon_a-ssh-utils.obj: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ssh-utils.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ssh-utils.Tpo -c -o libcommon_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ssh-utils.Tpo $(DEPDIR)/libcommon_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommon_a-ssh-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` + +libcommon_a-agent-opt.o: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-agent-opt.o -MD -MP -MF $(DEPDIR)/libcommon_a-agent-opt.Tpo -c -o libcommon_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-agent-opt.Tpo $(DEPDIR)/libcommon_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommon_a-agent-opt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c + +libcommon_a-agent-opt.obj: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-agent-opt.obj -MD -MP -MF $(DEPDIR)/libcommon_a-agent-opt.Tpo -c -o libcommon_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-agent-opt.Tpo $(DEPDIR)/libcommon_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommon_a-agent-opt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` + +libcommon_a-helpfile.o: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-helpfile.o -MD -MP -MF $(DEPDIR)/libcommon_a-helpfile.Tpo -c -o libcommon_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-helpfile.Tpo $(DEPDIR)/libcommon_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommon_a-helpfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c + +libcommon_a-helpfile.obj: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-helpfile.obj -MD -MP -MF $(DEPDIR)/libcommon_a-helpfile.Tpo -c -o libcommon_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-helpfile.Tpo $(DEPDIR)/libcommon_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommon_a-helpfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` + +libcommon_a-mkdir_p.o: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mkdir_p.o -MD -MP -MF $(DEPDIR)/libcommon_a-mkdir_p.Tpo -c -o libcommon_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mkdir_p.Tpo $(DEPDIR)/libcommon_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommon_a-mkdir_p.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c + +libcommon_a-mkdir_p.obj: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mkdir_p.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mkdir_p.Tpo -c -o libcommon_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mkdir_p.Tpo $(DEPDIR)/libcommon_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommon_a-mkdir_p.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` + +libcommon_a-exectool.o: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exectool.o -MD -MP -MF $(DEPDIR)/libcommon_a-exectool.Tpo -c -o libcommon_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exectool.Tpo $(DEPDIR)/libcommon_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommon_a-exectool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c + +libcommon_a-exectool.obj: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exectool.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exectool.Tpo -c -o libcommon_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exectool.Tpo $(DEPDIR)/libcommon_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommon_a-exectool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` + +libcommon_a-server-help.o: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-server-help.o -MD -MP -MF $(DEPDIR)/libcommon_a-server-help.Tpo -c -o libcommon_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-server-help.Tpo $(DEPDIR)/libcommon_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommon_a-server-help.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c + +libcommon_a-server-help.obj: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-server-help.obj -MD -MP -MF $(DEPDIR)/libcommon_a-server-help.Tpo -c -o libcommon_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-server-help.Tpo $(DEPDIR)/libcommon_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommon_a-server-help.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` + +libcommon_a-name-value.o: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-name-value.o -MD -MP -MF $(DEPDIR)/libcommon_a-name-value.Tpo -c -o libcommon_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-name-value.Tpo $(DEPDIR)/libcommon_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommon_a-name-value.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c + +libcommon_a-name-value.obj: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-name-value.obj -MD -MP -MF $(DEPDIR)/libcommon_a-name-value.Tpo -c -o libcommon_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-name-value.Tpo $(DEPDIR)/libcommon_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommon_a-name-value.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` + +libcommon_a-recsel.o: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-recsel.o -MD -MP -MF $(DEPDIR)/libcommon_a-recsel.Tpo -c -o libcommon_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-recsel.Tpo $(DEPDIR)/libcommon_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommon_a-recsel.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c + +libcommon_a-recsel.obj: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-recsel.obj -MD -MP -MF $(DEPDIR)/libcommon_a-recsel.Tpo -c -o libcommon_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-recsel.Tpo $(DEPDIR)/libcommon_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommon_a-recsel.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` + +libcommon_a-w32-reg.o: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-w32-reg.o -MD -MP -MF $(DEPDIR)/libcommon_a-w32-reg.Tpo -c -o libcommon_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-w32-reg.Tpo $(DEPDIR)/libcommon_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommon_a-w32-reg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c + +libcommon_a-w32-reg.obj: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-w32-reg.obj -MD -MP -MF $(DEPDIR)/libcommon_a-w32-reg.Tpo -c -o libcommon_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-w32-reg.Tpo $(DEPDIR)/libcommon_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommon_a-w32-reg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` + +libcommon_a-exechelp-w32ce.o: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32ce.o -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo -c -o libcommon_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommon_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommon_a-exechelp-w32ce.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c + +libcommon_a-exechelp-w32ce.obj: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32ce.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo -c -o libcommon_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommon_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommon_a-exechelp-w32ce.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` + +libcommon_a-exechelp-w32.o: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32.o -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32.Tpo -c -o libcommon_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32.Tpo $(DEPDIR)/libcommon_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommon_a-exechelp-w32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c + +libcommon_a-exechelp-w32.obj: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32.Tpo -c -o libcommon_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32.Tpo $(DEPDIR)/libcommon_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommon_a-exechelp-w32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` + +libcommon_a-exechelp-posix.o: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-posix.o -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-posix.Tpo -c -o libcommon_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-posix.Tpo $(DEPDIR)/libcommon_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommon_a-exechelp-posix.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c + +libcommon_a-exechelp-posix.obj: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-posix.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-posix.Tpo -c -o libcommon_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-posix.Tpo $(DEPDIR)/libcommon_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommon_a-exechelp-posix.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` + +libcommon_a-get-passphrase.o: get-passphrase.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-get-passphrase.o -MD -MP -MF $(DEPDIR)/libcommon_a-get-passphrase.Tpo -c -o libcommon_a-get-passphrase.o `test -f 'get-passphrase.c' || echo '$(srcdir)/'`get-passphrase.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-get-passphrase.Tpo $(DEPDIR)/libcommon_a-get-passphrase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get-passphrase.c' object='libcommon_a-get-passphrase.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-get-passphrase.o `test -f 'get-passphrase.c' || echo '$(srcdir)/'`get-passphrase.c + +libcommon_a-get-passphrase.obj: get-passphrase.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-get-passphrase.obj -MD -MP -MF $(DEPDIR)/libcommon_a-get-passphrase.Tpo -c -o libcommon_a-get-passphrase.obj `if test -f 'get-passphrase.c'; then $(CYGPATH_W) 'get-passphrase.c'; else $(CYGPATH_W) '$(srcdir)/get-passphrase.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-get-passphrase.Tpo $(DEPDIR)/libcommon_a-get-passphrase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get-passphrase.c' object='libcommon_a-get-passphrase.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-get-passphrase.obj `if test -f 'get-passphrase.c'; then $(CYGPATH_W) 'get-passphrase.c'; else $(CYGPATH_W) '$(srcdir)/get-passphrase.c'; fi` + +libcommonpth_a-i18n.o: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-i18n.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-i18n.Tpo -c -o libcommonpth_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-i18n.Tpo $(DEPDIR)/libcommonpth_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommonpth_a-i18n.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c + +libcommonpth_a-i18n.obj: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-i18n.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-i18n.Tpo -c -o libcommonpth_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-i18n.Tpo $(DEPDIR)/libcommonpth_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommonpth_a-i18n.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` + +libcommonpth_a-mapstrings.o: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mapstrings.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mapstrings.Tpo -c -o libcommonpth_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mapstrings.Tpo $(DEPDIR)/libcommonpth_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommonpth_a-mapstrings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c + +libcommonpth_a-mapstrings.obj: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mapstrings.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mapstrings.Tpo -c -o libcommonpth_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mapstrings.Tpo $(DEPDIR)/libcommonpth_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommonpth_a-mapstrings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` + +libcommonpth_a-stringhelp.o: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-stringhelp.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-stringhelp.Tpo -c -o libcommonpth_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-stringhelp.Tpo $(DEPDIR)/libcommonpth_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommonpth_a-stringhelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c + +libcommonpth_a-stringhelp.obj: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-stringhelp.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-stringhelp.Tpo -c -o libcommonpth_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-stringhelp.Tpo $(DEPDIR)/libcommonpth_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommonpth_a-stringhelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` + +libcommonpth_a-strlist.o: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-strlist.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-strlist.Tpo -c -o libcommonpth_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-strlist.Tpo $(DEPDIR)/libcommonpth_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommonpth_a-strlist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c + +libcommonpth_a-strlist.obj: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-strlist.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-strlist.Tpo -c -o libcommonpth_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-strlist.Tpo $(DEPDIR)/libcommonpth_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommonpth_a-strlist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` + +libcommonpth_a-utf8conv.o: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-utf8conv.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-utf8conv.Tpo -c -o libcommonpth_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-utf8conv.Tpo $(DEPDIR)/libcommonpth_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommonpth_a-utf8conv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c + +libcommonpth_a-utf8conv.obj: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-utf8conv.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-utf8conv.Tpo -c -o libcommonpth_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-utf8conv.Tpo $(DEPDIR)/libcommonpth_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommonpth_a-utf8conv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` + +libcommonpth_a-argparse.o: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-argparse.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-argparse.Tpo -c -o libcommonpth_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-argparse.Tpo $(DEPDIR)/libcommonpth_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommonpth_a-argparse.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c + +libcommonpth_a-argparse.obj: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-argparse.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-argparse.Tpo -c -o libcommonpth_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-argparse.Tpo $(DEPDIR)/libcommonpth_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommonpth_a-argparse.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` + +libcommonpth_a-logging.o: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-logging.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-logging.Tpo -c -o libcommonpth_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-logging.Tpo $(DEPDIR)/libcommonpth_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommonpth_a-logging.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c + +libcommonpth_a-logging.obj: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-logging.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-logging.Tpo -c -o libcommonpth_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-logging.Tpo $(DEPDIR)/libcommonpth_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommonpth_a-logging.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` + +libcommonpth_a-dotlock.o: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-dotlock.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-dotlock.Tpo -c -o libcommonpth_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-dotlock.Tpo $(DEPDIR)/libcommonpth_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommonpth_a-dotlock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c + +libcommonpth_a-dotlock.obj: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-dotlock.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-dotlock.Tpo -c -o libcommonpth_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-dotlock.Tpo $(DEPDIR)/libcommonpth_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommonpth_a-dotlock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` + +libcommonpth_a-mischelp.o: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mischelp.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mischelp.Tpo -c -o libcommonpth_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mischelp.Tpo $(DEPDIR)/libcommonpth_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommonpth_a-mischelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c + +libcommonpth_a-mischelp.obj: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mischelp.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mischelp.Tpo -c -o libcommonpth_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mischelp.Tpo $(DEPDIR)/libcommonpth_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommonpth_a-mischelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` + +libcommonpth_a-status.o: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-status.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-status.Tpo -c -o libcommonpth_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-status.Tpo $(DEPDIR)/libcommonpth_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommonpth_a-status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c + +libcommonpth_a-status.obj: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-status.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-status.Tpo -c -o libcommonpth_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-status.Tpo $(DEPDIR)/libcommonpth_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommonpth_a-status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` + +libcommonpth_a-tlv.o: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-tlv.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-tlv.Tpo -c -o libcommonpth_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-tlv.Tpo $(DEPDIR)/libcommonpth_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommonpth_a-tlv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c + +libcommonpth_a-tlv.obj: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-tlv.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-tlv.Tpo -c -o libcommonpth_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-tlv.Tpo $(DEPDIR)/libcommonpth_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommonpth_a-tlv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` + +libcommonpth_a-init.o: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-init.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-init.Tpo -c -o libcommonpth_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-init.Tpo $(DEPDIR)/libcommonpth_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommonpth_a-init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c + +libcommonpth_a-init.obj: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-init.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-init.Tpo -c -o libcommonpth_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-init.Tpo $(DEPDIR)/libcommonpth_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommonpth_a-init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` + +libcommonpth_a-sexputil.o: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sexputil.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-sexputil.Tpo -c -o libcommonpth_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sexputil.Tpo $(DEPDIR)/libcommonpth_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommonpth_a-sexputil.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c + +libcommonpth_a-sexputil.obj: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sexputil.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-sexputil.Tpo -c -o libcommonpth_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sexputil.Tpo $(DEPDIR)/libcommonpth_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommonpth_a-sexputil.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` + +libcommonpth_a-sysutils.o: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sysutils.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-sysutils.Tpo -c -o libcommonpth_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sysutils.Tpo $(DEPDIR)/libcommonpth_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommonpth_a-sysutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c + +libcommonpth_a-sysutils.obj: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sysutils.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-sysutils.Tpo -c -o libcommonpth_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sysutils.Tpo $(DEPDIR)/libcommonpth_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommonpth_a-sysutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` + +libcommonpth_a-homedir.o: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-homedir.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-homedir.Tpo -c -o libcommonpth_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-homedir.Tpo $(DEPDIR)/libcommonpth_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommonpth_a-homedir.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c + +libcommonpth_a-homedir.obj: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-homedir.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-homedir.Tpo -c -o libcommonpth_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-homedir.Tpo $(DEPDIR)/libcommonpth_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommonpth_a-homedir.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` + +libcommonpth_a-gettime.o: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-gettime.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-gettime.Tpo -c -o libcommonpth_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-gettime.Tpo $(DEPDIR)/libcommonpth_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommonpth_a-gettime.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c + +libcommonpth_a-gettime.obj: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-gettime.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-gettime.Tpo -c -o libcommonpth_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-gettime.Tpo $(DEPDIR)/libcommonpth_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommonpth_a-gettime.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` + +libcommonpth_a-yesno.o: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-yesno.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-yesno.Tpo -c -o libcommonpth_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-yesno.Tpo $(DEPDIR)/libcommonpth_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommonpth_a-yesno.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c + +libcommonpth_a-yesno.obj: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-yesno.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-yesno.Tpo -c -o libcommonpth_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-yesno.Tpo $(DEPDIR)/libcommonpth_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommonpth_a-yesno.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` + +libcommonpth_a-b64enc.o: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64enc.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64enc.Tpo -c -o libcommonpth_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64enc.Tpo $(DEPDIR)/libcommonpth_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommonpth_a-b64enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c + +libcommonpth_a-b64enc.obj: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64enc.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64enc.Tpo -c -o libcommonpth_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64enc.Tpo $(DEPDIR)/libcommonpth_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommonpth_a-b64enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` + +libcommonpth_a-b64dec.o: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64dec.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64dec.Tpo -c -o libcommonpth_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64dec.Tpo $(DEPDIR)/libcommonpth_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommonpth_a-b64dec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c + +libcommonpth_a-b64dec.obj: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64dec.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64dec.Tpo -c -o libcommonpth_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64dec.Tpo $(DEPDIR)/libcommonpth_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommonpth_a-b64dec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` + +libcommonpth_a-zb32.o: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-zb32.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-zb32.Tpo -c -o libcommonpth_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-zb32.Tpo $(DEPDIR)/libcommonpth_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommonpth_a-zb32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c + +libcommonpth_a-zb32.obj: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-zb32.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-zb32.Tpo -c -o libcommonpth_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-zb32.Tpo $(DEPDIR)/libcommonpth_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommonpth_a-zb32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` + +libcommonpth_a-convert.o: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-convert.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-convert.Tpo -c -o libcommonpth_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-convert.Tpo $(DEPDIR)/libcommonpth_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommonpth_a-convert.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c + +libcommonpth_a-convert.obj: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-convert.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-convert.Tpo -c -o libcommonpth_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-convert.Tpo $(DEPDIR)/libcommonpth_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommonpth_a-convert.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` + +libcommonpth_a-percent.o: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-percent.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-percent.Tpo -c -o libcommonpth_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-percent.Tpo $(DEPDIR)/libcommonpth_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommonpth_a-percent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c + +libcommonpth_a-percent.obj: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-percent.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-percent.Tpo -c -o libcommonpth_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-percent.Tpo $(DEPDIR)/libcommonpth_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommonpth_a-percent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` + +libcommonpth_a-mbox-util.o: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mbox-util.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mbox-util.Tpo -c -o libcommonpth_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mbox-util.Tpo $(DEPDIR)/libcommonpth_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommonpth_a-mbox-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c + +libcommonpth_a-mbox-util.obj: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mbox-util.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mbox-util.Tpo -c -o libcommonpth_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mbox-util.Tpo $(DEPDIR)/libcommonpth_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommonpth_a-mbox-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` + +libcommonpth_a-miscellaneous.o: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-miscellaneous.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo -c -o libcommonpth_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo $(DEPDIR)/libcommonpth_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommonpth_a-miscellaneous.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c + +libcommonpth_a-miscellaneous.obj: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-miscellaneous.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo -c -o libcommonpth_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo $(DEPDIR)/libcommonpth_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommonpth_a-miscellaneous.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` + +libcommonpth_a-xasprintf.o: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xasprintf.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-xasprintf.Tpo -c -o libcommonpth_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xasprintf.Tpo $(DEPDIR)/libcommonpth_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommonpth_a-xasprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c + +libcommonpth_a-xasprintf.obj: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xasprintf.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-xasprintf.Tpo -c -o libcommonpth_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xasprintf.Tpo $(DEPDIR)/libcommonpth_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommonpth_a-xasprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` + +libcommonpth_a-xreadline.o: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xreadline.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-xreadline.Tpo -c -o libcommonpth_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xreadline.Tpo $(DEPDIR)/libcommonpth_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommonpth_a-xreadline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c + +libcommonpth_a-xreadline.obj: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xreadline.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-xreadline.Tpo -c -o libcommonpth_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xreadline.Tpo $(DEPDIR)/libcommonpth_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommonpth_a-xreadline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` + +libcommonpth_a-membuf.o: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-membuf.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-membuf.Tpo -c -o libcommonpth_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-membuf.Tpo $(DEPDIR)/libcommonpth_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommonpth_a-membuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c + +libcommonpth_a-membuf.obj: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-membuf.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-membuf.Tpo -c -o libcommonpth_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-membuf.Tpo $(DEPDIR)/libcommonpth_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommonpth_a-membuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` + +libcommonpth_a-ccparray.o: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ccparray.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ccparray.Tpo -c -o libcommonpth_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ccparray.Tpo $(DEPDIR)/libcommonpth_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommonpth_a-ccparray.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c + +libcommonpth_a-ccparray.obj: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ccparray.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ccparray.Tpo -c -o libcommonpth_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ccparray.Tpo $(DEPDIR)/libcommonpth_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommonpth_a-ccparray.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` + +libcommonpth_a-iobuf.o: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-iobuf.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-iobuf.Tpo -c -o libcommonpth_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-iobuf.Tpo $(DEPDIR)/libcommonpth_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommonpth_a-iobuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c + +libcommonpth_a-iobuf.obj: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-iobuf.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-iobuf.Tpo -c -o libcommonpth_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-iobuf.Tpo $(DEPDIR)/libcommonpth_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommonpth_a-iobuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` + +libcommonpth_a-ttyio.o: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ttyio.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ttyio.Tpo -c -o libcommonpth_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ttyio.Tpo $(DEPDIR)/libcommonpth_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommonpth_a-ttyio.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c + +libcommonpth_a-ttyio.obj: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ttyio.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ttyio.Tpo -c -o libcommonpth_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ttyio.Tpo $(DEPDIR)/libcommonpth_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommonpth_a-ttyio.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` + +libcommonpth_a-asshelp.o: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp.Tpo -c -o libcommonpth_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp.Tpo $(DEPDIR)/libcommonpth_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommonpth_a-asshelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c + +libcommonpth_a-asshelp.obj: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp.Tpo -c -o libcommonpth_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp.Tpo $(DEPDIR)/libcommonpth_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommonpth_a-asshelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` + +libcommonpth_a-asshelp2.o: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp2.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp2.Tpo -c -o libcommonpth_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp2.Tpo $(DEPDIR)/libcommonpth_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommonpth_a-asshelp2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c + +libcommonpth_a-asshelp2.obj: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp2.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp2.Tpo -c -o libcommonpth_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp2.Tpo $(DEPDIR)/libcommonpth_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommonpth_a-asshelp2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` + +libcommonpth_a-signal.o: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-signal.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-signal.Tpo -c -o libcommonpth_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-signal.Tpo $(DEPDIR)/libcommonpth_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommonpth_a-signal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c + +libcommonpth_a-signal.obj: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-signal.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-signal.Tpo -c -o libcommonpth_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-signal.Tpo $(DEPDIR)/libcommonpth_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommonpth_a-signal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` + +libcommonpth_a-audit.o: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-audit.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-audit.Tpo -c -o libcommonpth_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-audit.Tpo $(DEPDIR)/libcommonpth_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommonpth_a-audit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c + +libcommonpth_a-audit.obj: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-audit.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-audit.Tpo -c -o libcommonpth_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-audit.Tpo $(DEPDIR)/libcommonpth_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommonpth_a-audit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` + +libcommonpth_a-localename.o: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-localename.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-localename.Tpo -c -o libcommonpth_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-localename.Tpo $(DEPDIR)/libcommonpth_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommonpth_a-localename.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c + +libcommonpth_a-localename.obj: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-localename.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-localename.Tpo -c -o libcommonpth_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-localename.Tpo $(DEPDIR)/libcommonpth_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommonpth_a-localename.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` + +libcommonpth_a-session-env.o: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-session-env.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-session-env.Tpo -c -o libcommonpth_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-session-env.Tpo $(DEPDIR)/libcommonpth_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommonpth_a-session-env.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c + +libcommonpth_a-session-env.obj: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-session-env.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-session-env.Tpo -c -o libcommonpth_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-session-env.Tpo $(DEPDIR)/libcommonpth_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommonpth_a-session-env.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` + +libcommonpth_a-userids.o: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-userids.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-userids.Tpo -c -o libcommonpth_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-userids.Tpo $(DEPDIR)/libcommonpth_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommonpth_a-userids.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c + +libcommonpth_a-userids.obj: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-userids.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-userids.Tpo -c -o libcommonpth_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-userids.Tpo $(DEPDIR)/libcommonpth_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommonpth_a-userids.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` + +libcommonpth_a-openpgp-oid.o: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-openpgp-oid.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo -c -o libcommonpth_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo $(DEPDIR)/libcommonpth_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommonpth_a-openpgp-oid.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c + +libcommonpth_a-openpgp-oid.obj: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-openpgp-oid.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo -c -o libcommonpth_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo $(DEPDIR)/libcommonpth_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommonpth_a-openpgp-oid.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` + +libcommonpth_a-ssh-utils.o: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ssh-utils.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo -c -o libcommonpth_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo $(DEPDIR)/libcommonpth_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommonpth_a-ssh-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c + +libcommonpth_a-ssh-utils.obj: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ssh-utils.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo -c -o libcommonpth_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo $(DEPDIR)/libcommonpth_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommonpth_a-ssh-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` + +libcommonpth_a-agent-opt.o: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-agent-opt.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-agent-opt.Tpo -c -o libcommonpth_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-agent-opt.Tpo $(DEPDIR)/libcommonpth_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommonpth_a-agent-opt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c + +libcommonpth_a-agent-opt.obj: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-agent-opt.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-agent-opt.Tpo -c -o libcommonpth_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-agent-opt.Tpo $(DEPDIR)/libcommonpth_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommonpth_a-agent-opt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` + +libcommonpth_a-helpfile.o: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-helpfile.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-helpfile.Tpo -c -o libcommonpth_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-helpfile.Tpo $(DEPDIR)/libcommonpth_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommonpth_a-helpfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c + +libcommonpth_a-helpfile.obj: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-helpfile.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-helpfile.Tpo -c -o libcommonpth_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-helpfile.Tpo $(DEPDIR)/libcommonpth_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommonpth_a-helpfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` + +libcommonpth_a-mkdir_p.o: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mkdir_p.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo -c -o libcommonpth_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo $(DEPDIR)/libcommonpth_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommonpth_a-mkdir_p.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c + +libcommonpth_a-mkdir_p.obj: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mkdir_p.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo -c -o libcommonpth_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo $(DEPDIR)/libcommonpth_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommonpth_a-mkdir_p.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` + +libcommonpth_a-exectool.o: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exectool.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exectool.Tpo -c -o libcommonpth_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exectool.Tpo $(DEPDIR)/libcommonpth_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommonpth_a-exectool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c + +libcommonpth_a-exectool.obj: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exectool.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exectool.Tpo -c -o libcommonpth_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exectool.Tpo $(DEPDIR)/libcommonpth_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommonpth_a-exectool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` + +libcommonpth_a-server-help.o: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-server-help.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-server-help.Tpo -c -o libcommonpth_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-server-help.Tpo $(DEPDIR)/libcommonpth_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommonpth_a-server-help.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c + +libcommonpth_a-server-help.obj: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-server-help.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-server-help.Tpo -c -o libcommonpth_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-server-help.Tpo $(DEPDIR)/libcommonpth_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommonpth_a-server-help.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` + +libcommonpth_a-name-value.o: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-name-value.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-name-value.Tpo -c -o libcommonpth_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-name-value.Tpo $(DEPDIR)/libcommonpth_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommonpth_a-name-value.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c + +libcommonpth_a-name-value.obj: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-name-value.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-name-value.Tpo -c -o libcommonpth_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-name-value.Tpo $(DEPDIR)/libcommonpth_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommonpth_a-name-value.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` + +libcommonpth_a-recsel.o: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-recsel.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-recsel.Tpo -c -o libcommonpth_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-recsel.Tpo $(DEPDIR)/libcommonpth_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommonpth_a-recsel.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c + +libcommonpth_a-recsel.obj: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-recsel.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-recsel.Tpo -c -o libcommonpth_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-recsel.Tpo $(DEPDIR)/libcommonpth_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommonpth_a-recsel.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` + +libcommonpth_a-w32-reg.o: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-w32-reg.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-w32-reg.Tpo -c -o libcommonpth_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-w32-reg.Tpo $(DEPDIR)/libcommonpth_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommonpth_a-w32-reg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c + +libcommonpth_a-w32-reg.obj: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-w32-reg.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-w32-reg.Tpo -c -o libcommonpth_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-w32-reg.Tpo $(DEPDIR)/libcommonpth_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommonpth_a-w32-reg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` + +libcommonpth_a-exechelp-w32ce.o: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32ce.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo -c -o libcommonpth_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommonpth_a-exechelp-w32ce.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c + +libcommonpth_a-exechelp-w32ce.obj: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32ce.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo -c -o libcommonpth_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommonpth_a-exechelp-w32ce.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` + +libcommonpth_a-exechelp-w32.o: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo -c -o libcommonpth_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommonpth_a-exechelp-w32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c + +libcommonpth_a-exechelp-w32.obj: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo -c -o libcommonpth_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommonpth_a-exechelp-w32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` + +libcommonpth_a-exechelp-posix.o: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-posix.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo -c -o libcommonpth_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo $(DEPDIR)/libcommonpth_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommonpth_a-exechelp-posix.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c + +libcommonpth_a-exechelp-posix.obj: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-posix.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo -c -o libcommonpth_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo $(DEPDIR)/libcommonpth_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommonpth_a-exechelp-posix.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` + +libcommonpth_a-call-gpg.o: call-gpg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-call-gpg.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-call-gpg.Tpo -c -o libcommonpth_a-call-gpg.o `test -f 'call-gpg.c' || echo '$(srcdir)/'`call-gpg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-call-gpg.Tpo $(DEPDIR)/libcommonpth_a-call-gpg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-gpg.c' object='libcommonpth_a-call-gpg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-call-gpg.o `test -f 'call-gpg.c' || echo '$(srcdir)/'`call-gpg.c + +libcommonpth_a-call-gpg.obj: call-gpg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-call-gpg.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-call-gpg.Tpo -c -o libcommonpth_a-call-gpg.obj `if test -f 'call-gpg.c'; then $(CYGPATH_W) 'call-gpg.c'; else $(CYGPATH_W) '$(srcdir)/call-gpg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-call-gpg.Tpo $(DEPDIR)/libcommonpth_a-call-gpg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-gpg.c' object='libcommonpth_a-call-gpg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-call-gpg.obj `if test -f 'call-gpg.c'; then $(CYGPATH_W) 'call-gpg.c'; else $(CYGPATH_W) '$(srcdir)/call-gpg.c'; fi` + +libsimple_pwquery_a-simple-pwquery.o: simple-pwquery.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-simple-pwquery.o -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo -c -o libsimple_pwquery_a-simple-pwquery.o `test -f 'simple-pwquery.c' || echo '$(srcdir)/'`simple-pwquery.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='simple-pwquery.c' object='libsimple_pwquery_a-simple-pwquery.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-simple-pwquery.o `test -f 'simple-pwquery.c' || echo '$(srcdir)/'`simple-pwquery.c + +libsimple_pwquery_a-simple-pwquery.obj: simple-pwquery.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-simple-pwquery.obj -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo -c -o libsimple_pwquery_a-simple-pwquery.obj `if test -f 'simple-pwquery.c'; then $(CYGPATH_W) 'simple-pwquery.c'; else $(CYGPATH_W) '$(srcdir)/simple-pwquery.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='simple-pwquery.c' object='libsimple_pwquery_a-simple-pwquery.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-simple-pwquery.obj `if test -f 'simple-pwquery.c'; then $(CYGPATH_W) 'simple-pwquery.c'; else $(CYGPATH_W) '$(srcdir)/simple-pwquery.c'; fi` + +libsimple_pwquery_a-asshelp.o: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-asshelp.o -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo -c -o libsimple_pwquery_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo $(DEPDIR)/libsimple_pwquery_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libsimple_pwquery_a-asshelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c + +libsimple_pwquery_a-asshelp.obj: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-asshelp.obj -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo -c -o libsimple_pwquery_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo $(DEPDIR)/libsimple_pwquery_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libsimple_pwquery_a-asshelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LIBRARIES) $(PROGRAMS) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check check-am install install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ + clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + + +@HAVE_W32_SYSTEM_TRUE@.rc.o: +@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" + +# Note: Due to the dependency on Makefile, the file will always be +# rebuilt, so we allow this only in maintainer mode. + +# Create the audit-events.h include file from audit.h +# Note: We create the target file in the source directory because it +# is a distributed built source. If we would not do that we may end +# up with two files and then it is not clear which version of the +# files will be picked up. +@MAINTAINER_MODE_TRUE@audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h +@MAINTAINER_MODE_TRUE@ $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ +@MAINTAINER_MODE_TRUE@ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ +@MAINTAINER_MODE_TRUE@ -v namespace=eventstr_ > $(srcdir)/audit-events.h + +# Create the status-codes.h include file from status.h +@MAINTAINER_MODE_TRUE@status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h +@MAINTAINER_MODE_TRUE@ $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ +@MAINTAINER_MODE_TRUE@ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ +@MAINTAINER_MODE_TRUE@ -v namespace=statusstr_ > $(srcdir)/status-codes.h + +# All programs should depend on the created libs. +$(PROGRAMS) : libcommon.a libcommonpth.a + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/common/README b/common/README new file mode 100644 index 0000000..73799a8 --- /dev/null +++ b/common/README @@ -0,0 +1 @@ +Common functionality used by all modules of GnuPG. diff --git a/common/agent-opt.c b/common/agent-opt.c new file mode 100644 index 0000000..b324482 --- /dev/null +++ b/common/agent-opt.c @@ -0,0 +1,71 @@ +/* agent-opt.c - Helper for certain agent options + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include + +#include "shareddefs.h" + + +/* Parse VALUE and return an integer representing a pinentry_mode_t. + (-1) is returned for an invalid VALUE. */ +int +parse_pinentry_mode (const char *value) +{ + int result; + + if (!strcmp (value, "ask") || !strcmp (value, "default")) + result = PINENTRY_MODE_ASK; + else if (!strcmp (value, "cancel")) + result = PINENTRY_MODE_CANCEL; + else if (!strcmp (value, "error")) + result = PINENTRY_MODE_ERROR; + else if (!strcmp (value, "loopback")) + result = PINENTRY_MODE_LOOPBACK; + else + result = -1; + + return result; +} + +/* Return the string representation for the pinentry MODE. Returns + "?" for an invalid mode. */ +const char * +str_pinentry_mode (pinentry_mode_t mode) +{ + switch (mode) + { + case PINENTRY_MODE_ASK: return "ask"; + case PINENTRY_MODE_CANCEL: return "cancel"; + case PINENTRY_MODE_ERROR: return "error"; + case PINENTRY_MODE_LOOPBACK: return "loopback"; + } + return "?"; +} diff --git a/common/argparse.c b/common/argparse.c new file mode 100644 index 0000000..dce725a --- /dev/null +++ b/common/argparse.c @@ -0,0 +1,1629 @@ +/* [argparse.c wk 17.06.97] Argument Parser for option handling + * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc. + * Copyright (C) 1997-2001, 2006-2008, 2013-2016 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +/* This file may be used as part of GnuPG or standalone. A GnuPG + build is detected by the presence of the macro GNUPG_MAJOR_VERSION. + Some feature are only availalbe in the GnuPG build mode. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef GNUPG_MAJOR_VERSION +# include "util.h" +# include "common-defs.h" +# include "i18n.h" +# include "mischelp.h" +# include "stringhelp.h" +# include "logging.h" +# include "utf8conv.h" +#endif /*GNUPG_MAJOR_VERSION*/ + +#include "argparse.h" + +/* GnuPG uses GPLv3+ but a standalone version of this defaults to + GPLv2+ because that is the license of this file. Change this if + you include it in a program which uses GPLv3. If you don't want to + set a a copyright string for your usage() you may also hardcode it + here. */ +#ifndef GNUPG_MAJOR_VERSION + +# define ARGPARSE_GPL_VERSION 2 +# define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME" + +#else /* Used by GnuPG */ + +# define ARGPARSE_GPL_VERSION 3 +# define ARGPARSE_CRIGHT_STR "Copyright (C) 2016 Free Software Foundation, Inc." + +#endif /*GNUPG_MAJOR_VERSION*/ + +/* Replacements for standalone builds. */ +#ifndef GNUPG_MAJOR_VERSION +# ifndef _ +# define _(a) (a) +# endif +# ifndef DIM +# define DIM(v) (sizeof(v)/sizeof((v)[0])) +# endif +# define xtrymalloc(a) malloc ((a)) +# define xtryrealloc(a,b) realloc ((a), (b)) +# define xtrystrdup(a) strdup ((a)) +# define xfree(a) free ((a)) +# define log_error my_log_error +# define log_bug my_log_bug +# define trim_spaces(a) my_trim_spaces ((a)) +# define map_static_macro_string(a) (a) +#endif /*!GNUPG_MAJOR_VERSION*/ + + +#define ARGPARSE_STR(v) #v +#define ARGPARSE_STR2(v) ARGPARSE_STR(v) + + +/* Replacements for standalone builds. */ +#ifndef GNUPG_MAJOR_VERSION +static void +my_log_error (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + fprintf (stderr, "%s: ", strusage (11)); + vfprintf (stderr, fmt, arg_ptr); + va_end (arg_ptr); +} + +static void +my_log_bug (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11)); + vfprintf (stderr, fmt, arg_ptr); + va_end (arg_ptr); + abort (); +} + +/* Return true if the native charset is utf-8. */ +static int +is_native_utf8 (void) +{ + return 1; +} + +static char * +my_trim_spaces (char *str) +{ + char *string, *p, *mark; + + string = str; + /* Find first non space character. */ + for (p=string; *p && isspace (*(unsigned char*)p) ; p++) + ; + /* Move characters. */ + for ((mark = NULL); (*string = *p); string++, p++) + if (isspace (*(unsigned char*)p)) + { + if (!mark) + mark = string; + } + else + mark = NULL; + if (mark) + *mark = '\0' ; /* Remove trailing spaces. */ + + return str ; +} + +#endif /*!GNUPG_MAJOR_VERSION*/ + + + +/********************************* + * @Summary arg_parse + * #include "argparse.h" + * + * typedef struct { + * char *argc; pointer to argc (value subject to change) + * char ***argv; pointer to argv (value subject to change) + * unsigned flags; Global flags (DO NOT CHANGE) + * int err; print error about last option + * 1 = warning, 2 = abort + * int r_opt; return option + * int r_type; type of return value (0 = no argument found) + * union { + * int ret_int; + * long ret_long + * ulong ret_ulong; + * char *ret_str; + * } r; Return values + * struct { + * int idx; + * const char *last; + * void *aliases; + * } internal; DO NOT CHANGE + * } ARGPARSE_ARGS; + * + * typedef struct { + * int short_opt; + * const char *long_opt; + * unsigned flags; + * } ARGPARSE_OPTS; + * + * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts ); + * + * @Description + * This is my replacement for getopt(). See the example for a typical usage. + * Global flags are: + * Bit 0 : Do not remove options form argv + * Bit 1 : Do not stop at last option but return other args + * with r_opt set to -1. + * Bit 2 : Assume options and real args are mixed. + * Bit 3 : Do not use -- to stop option processing. + * Bit 4 : Do not skip the first arg. + * Bit 5 : allow usage of long option with only one dash + * Bit 6 : ignore --version + * all other bits must be set to zero, this value is modified by the + * function, so assume this is write only. + * Local flags (for each option): + * Bit 2-0 : 0 = does not take an argument + * 1 = takes int argument + * 2 = takes string argument + * 3 = takes long argument + * 4 = takes ulong argument + * Bit 3 : argument is optional (r_type will the be set to 0) + * Bit 4 : allow 0x etc. prefixed values. + * Bit 6 : Ignore this option + * Bit 7 : This is a command and not an option + * You stop the option processing by setting opts to NULL, the function will + * then return 0. + * @Return Value + * Returns the args.r_opt or 0 if ready + * r_opt may be -2/-7 to indicate an unknown option/command. + * @See Also + * ArgExpand + * @Notes + * You do not need to process the options 'h', '--help' or '--version' + * because this function includes standard help processing; but if you + * specify '-h', '--help' or '--version' you have to do it yourself. + * The option '--' stops argument processing; if bit 1 is set the function + * continues to return normal arguments. + * To process float args or unsigned args you must use a string args and do + * the conversion yourself. + * @Example + * + * ARGPARSE_OPTS opts[] = { + * { 'v', "verbose", 0 }, + * { 'd', "debug", 0 }, + * { 'o', "output", 2 }, + * { 'c', "cross-ref", 2|8 }, + * { 'm', "my-option", 1|8 }, + * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE}, + * { 500, "have-no-short-option-for-this-long-option", 0 }, + * {0} }; + * ARGPARSE_ARGS pargs = { &argc, &argv, 0 } + * + * while( ArgParse( &pargs, &opts) ) { + * switch( pargs.r_opt ) { + * case 'v': opt.verbose++; break; + * case 'd': opt.debug++; break; + * case 'o': opt.outfile = pargs.r.ret_str; break; + * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; + * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; + * case 500: opt.a_long_one++; break + * default : pargs.err = 1; break; -- force warning output -- + * } + * } + * if( argc > 1 ) + * log_fatal( "Too many args"); + * + */ + +typedef struct alias_def_s *ALIAS_DEF; +struct alias_def_s { + ALIAS_DEF next; + char *name; /* malloced buffer with name, \0, value */ + const char *value; /* ptr into name */ +}; + + +/* Object to store the names for the --ignore-invalid-option option. + This is a simple linked list. */ +typedef struct iio_item_def_s *IIO_ITEM_DEF; +struct iio_item_def_s +{ + IIO_ITEM_DEF next; + char name[1]; /* String with the long option name. */ +}; + +static const char *(*strusage_handler)( int ) = NULL; +static int (*custom_outfnc) (int, const char *); + +static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s); +static void show_help(ARGPARSE_OPTS *opts, unsigned flags); +static void show_version(void); +static int writestrings (int is_error, const char *string, ...) +#if __GNUC__ >= 4 + __attribute__ ((sentinel(0))) +#endif + ; + + +void +argparse_register_outfnc (int (*fnc)(int, const char *)) +{ + custom_outfnc = fnc; +} + + +/* Write STRING and all following const char * arguments either to + stdout or, if IS_ERROR is set, to stderr. The list of strings must + be terminated by a NULL. */ +static int +writestrings (int is_error, const char *string, ...) +{ + va_list arg_ptr; + const char *s; + int count = 0; + + if (string) + { + s = string; + va_start (arg_ptr, string); + do + { + if (custom_outfnc) + custom_outfnc (is_error? 2:1, s); + else + fputs (s, is_error? stderr : stdout); + count += strlen (s); + } + while ((s = va_arg (arg_ptr, const char *))); + va_end (arg_ptr); + } + return count; +} + + +static void +flushstrings (int is_error) +{ + if (custom_outfnc) + custom_outfnc (is_error? 2:1, NULL); + else + fflush (is_error? stderr : stdout); +} + + +static void +initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) +{ + if( !(arg->flags & (1<<15)) ) + { + /* Initialize this instance. */ + arg->internal.idx = 0; + arg->internal.last = NULL; + arg->internal.inarg = 0; + arg->internal.stopped = 0; + arg->internal.aliases = NULL; + arg->internal.cur_alias = NULL; + arg->internal.iio_list = NULL; + arg->err = 0; + arg->flags |= 1<<15; /* Mark as initialized. */ + if ( *arg->argc < 0 ) + log_bug ("invalid argument for arg_parse\n"); + } + + + if (arg->err) + { + /* Last option was erroneous. */ + const char *s; + + if (filename) + { + if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) + s = _("argument not expected"); + else if ( arg->r_opt == ARGPARSE_READ_ERROR ) + s = _("read error"); + else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG ) + s = _("keyword too long"); + else if ( arg->r_opt == ARGPARSE_MISSING_ARG ) + s = _("missing argument"); + else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) + s = _("invalid argument"); + else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) + s = _("invalid command"); + else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS ) + s = _("invalid alias definition"); + else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) + s = _("out of core"); + else + s = _("invalid option"); + log_error ("%s:%u: %s\n", filename, *lineno, s); + } + else + { + s = arg->internal.last? arg->internal.last:"[??]"; + + if ( arg->r_opt == ARGPARSE_MISSING_ARG ) + log_error (_("missing argument for option \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) + log_error (_("invalid argument for option \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) + log_error (_("option \"%.50s\" does not expect an argument\n"), s); + else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) + log_error (_("invalid command \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) + log_error (_("option \"%.50s\" is ambiguous\n"), s); + else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND ) + log_error (_("command \"%.50s\" is ambiguous\n"),s ); + else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) + log_error ("%s\n", _("out of core\n")); + else + log_error (_("invalid option \"%.50s\"\n"), s); + } + if (arg->err != ARGPARSE_PRINT_WARNING) + exit (2); + arg->err = 0; + } + + /* Zero out the return value union. */ + arg->r.ret_str = NULL; + arg->r.ret_long = 0; +} + + +static void +store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) +{ + /* TODO: replace this dummy function with a rea one + * and fix the probelms IRIX has with (ALIAS_DEV)arg.. + * used as lvalue + */ + (void)arg; + (void)name; + (void)value; +#if 0 + ALIAS_DEF a = xmalloc( sizeof *a ); + a->name = name; + a->value = value; + a->next = (ALIAS_DEF)arg->internal.aliases; + (ALIAS_DEF)arg->internal.aliases = a; +#endif +} + + +/* Return true if KEYWORD is in the ignore-invalid-option list. */ +static int +ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) +{ + IIO_ITEM_DEF item = arg->internal.iio_list; + + for (; item; item = item->next) + if (!strcmp (item->name, keyword)) + return 1; + return 0; +} + + +/* Add the keywords up to the next LF to the list of to be ignored + options. After returning FP will either be at EOF or the next + character read wll be the first of a new line. The function + returns 0 on success or true on malloc failure. */ +static int +ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) +{ + IIO_ITEM_DEF item; + int c; + char name[100]; + int namelen = 0; + int ready = 0; + enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS; + + while (!ready) + { + c = getc (fp); + if (c == '\n') + ready = 1; + else if (c == EOF) + { + c = '\n'; + ready = 1; + } + again: + switch (state) + { + case skipWS: + if (!isascii (c) || !isspace(c)) + { + namelen = 0; + state = collectNAME; + goto again; + } + break; + + case collectNAME: + if (isspace (c)) + { + state = addNAME; + goto again; + } + else if (namelen < DIM(name)-1) + name[namelen++] = c; + else /* Too long. */ + state = skipNAME; + break; + + case skipNAME: + if (isspace (c)) + { + state = skipWS; + goto again; + } + break; + + case addNAME: + name[namelen] = 0; + if (!ignore_invalid_option_p (arg, name)) + { + item = xtrymalloc (sizeof *item + namelen); + if (!item) + return 1; + strcpy (item->name, name); + item->next = (IIO_ITEM_DEF)arg->internal.iio_list; + arg->internal.iio_list = item; + } + state = skipWS; + goto again; + } + } + return 0; +} + + +/* Clear the entire ignore-invalid-option list. */ +static void +ignore_invalid_option_clear (ARGPARSE_ARGS *arg) +{ + IIO_ITEM_DEF item, tmpitem; + + for (item = arg->internal.iio_list; item; item = tmpitem) + { + tmpitem = item->next; + xfree (item); + } + arg->internal.iio_list = NULL; +} + + + +/**************** + * Get options from a file. + * Lines starting with '#' are comment lines. + * Syntax is simply a keyword and the argument. + * Valid keywords are all keywords from the long_opt list without + * the leading dashes. The special keywords "help", "warranty" and "version" + * are not valid here. + * The special keyword "alias" may be used to store alias definitions, + * which are later expanded like long options. + * The option + * ignore-invalid-option OPTIONNAMEs + * is recognized and updates a list of option which should be ignored if they + * are not defined. + * Caller must free returned strings. + * If called with FP set to NULL command line args are parse instead. + * + * Q: Should we allow the syntax + * keyword = value + * and accept for boolean options a value of 1/0, yes/no or true/false? + * Note: Abbreviation of options is here not allowed. + */ +int +optfile_parse (FILE *fp, const char *filename, unsigned *lineno, + ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) +{ + int state, i, c; + int idx=0; + char keyword[100]; + char *buffer = NULL; + size_t buflen = 0; + int in_alias=0; + int unread_buf[3]; /* We use an int so that we can store EOF. */ + int unread_buf_count = 0; + + if (!fp) /* Divert to to arg_parse() in this case. */ + return arg_parse (arg, opts); + + initialize (arg, filename, lineno); + + /* If the LINENO is zero we assume that we are at the start of a + * file and we skip over a possible Byte Order Mark. */ + if (!*lineno) + { + unread_buf[0] = getc (fp); + unread_buf[1] = getc (fp); + unread_buf[2] = getc (fp); + if (unread_buf[0] != 0xef + || unread_buf[1] != 0xbb + || unread_buf[2] != 0xbf) + unread_buf_count = 3; + } + + /* Find the next keyword. */ + state = i = 0; + for (;;) + { + if (unread_buf_count) + c = unread_buf[3 - unread_buf_count--]; + else + c = getc (fp); + if (c == '\n' || c== EOF ) + { + if ( c != EOF ) + ++*lineno; + if (state == -1) + break; + else if (state == 2) + { + keyword[i] = 0; + for (i=0; opts[i].short_opt; i++ ) + { + if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) + break; + } + idx = i; + arg->r_opt = opts[idx].short_opt; + if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) + { + state = i = 0; + continue; + } + else if (!opts[idx].short_opt ) + { + if (!strcmp (keyword, "ignore-invalid-option")) + { + /* No argument - ignore this meta option. */ + state = i = 0; + continue; + } + else if (ignore_invalid_option_p (arg, keyword)) + { + /* This invalid option is in the iio list. */ + state = i = 0; + continue; + } + arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) + ? ARGPARSE_INVALID_COMMAND + : ARGPARSE_INVALID_OPTION); + } + else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) + arg->r_type = 0; /* Does not take an arg. */ + else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) ) + arg->r_type = 0; /* Arg is optional. */ + else + arg->r_opt = ARGPARSE_MISSING_ARG; + + break; + } + else if (state == 3) + { + /* No argument found. */ + if (in_alias) + arg->r_opt = ARGPARSE_MISSING_ARG; + else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) + arg->r_type = 0; /* Does not take an arg. */ + else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL)) + arg->r_type = 0; /* No optional argument. */ + else + arg->r_opt = ARGPARSE_MISSING_ARG; + + break; + } + else if (state == 4) + { + /* Has an argument. */ + if (in_alias) + { + if (!buffer) + arg->r_opt = ARGPARSE_UNEXPECTED_ARG; + else + { + char *p; + + buffer[i] = 0; + p = strpbrk (buffer, " \t"); + if (p) + { + *p++ = 0; + trim_spaces (p); + } + if (!p || !*p) + { + xfree (buffer); + arg->r_opt = ARGPARSE_INVALID_ALIAS; + } + else + { + store_alias (arg, buffer, p); + } + } + } + else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) + arg->r_opt = ARGPARSE_UNEXPECTED_ARG; + else + { + char *p; + + if (!buffer) + { + keyword[i] = 0; + buffer = xtrystrdup (keyword); + if (!buffer) + arg->r_opt = ARGPARSE_OUT_OF_CORE; + } + else + buffer[i] = 0; + + if (buffer) + { + trim_spaces (buffer); + p = buffer; + if (*p == '"') + { + /* Remove quotes. */ + p++; + if (*p && p[strlen(p)-1] == '\"' ) + p[strlen(p)-1] = 0; + } + if (!set_opt_arg (arg, opts[idx].flags, p)) + xfree (buffer); + else + gpgrt_annotate_leaked_object (buffer); + } + } + break; + } + else if (c == EOF) + { + ignore_invalid_option_clear (arg); + if (ferror (fp)) + arg->r_opt = ARGPARSE_READ_ERROR; + else + arg->r_opt = 0; /* EOF. */ + break; + } + state = 0; + i = 0; + } + else if (state == -1) + ; /* Skip. */ + else if (state == 0 && isascii (c) && isspace(c)) + ; /* Skip leading white space. */ + else if (state == 0 && c == '#' ) + state = 1; /* Start of a comment. */ + else if (state == 1) + ; /* Skip comments. */ + else if (state == 2 && isascii (c) && isspace(c)) + { + /* Check keyword. */ + keyword[i] = 0; + for (i=0; opts[i].short_opt; i++ ) + if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) + break; + idx = i; + arg->r_opt = opts[idx].short_opt; + if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) + { + state = 1; /* Process like a comment. */ + } + else if (!opts[idx].short_opt) + { + if (!strcmp (keyword, "alias")) + { + in_alias = 1; + state = 3; + } + else if (!strcmp (keyword, "ignore-invalid-option")) + { + if (ignore_invalid_option_add (arg, fp)) + { + arg->r_opt = ARGPARSE_OUT_OF_CORE; + break; + } + state = i = 0; + ++*lineno; + } + else if (ignore_invalid_option_p (arg, keyword)) + state = 1; /* Process like a comment. */ + else + { + arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) + ? ARGPARSE_INVALID_COMMAND + : ARGPARSE_INVALID_OPTION); + state = -1; /* Skip rest of line and leave. */ + } + } + else + state = 3; + } + else if (state == 3) + { + /* Skip leading spaces of the argument. */ + if (!isascii (c) || !isspace(c)) + { + i = 0; + keyword[i++] = c; + state = 4; + } + } + else if (state == 4) + { + /* Collect the argument. */ + if (buffer) + { + if (i < buflen-1) + buffer[i++] = c; + else + { + char *tmp; + size_t tmplen = buflen + 50; + + tmp = xtryrealloc (buffer, tmplen); + if (tmp) + { + buflen = tmplen; + buffer = tmp; + buffer[i++] = c; + } + else + { + xfree (buffer); + arg->r_opt = ARGPARSE_OUT_OF_CORE; + break; + } + } + } + else if (i < DIM(keyword)-1) + keyword[i++] = c; + else + { + size_t tmplen = DIM(keyword) + 50; + buffer = xtrymalloc (tmplen); + if (buffer) + { + buflen = tmplen; + memcpy(buffer, keyword, i); + buffer[i++] = c; + } + else + { + arg->r_opt = ARGPARSE_OUT_OF_CORE; + break; + } + } + } + else if (i >= DIM(keyword)-1) + { + arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG; + state = -1; /* Skip rest of line and leave. */ + } + else + { + keyword[i++] = c; + state = 2; + } + } + + return arg->r_opt; +} + + + +static int +find_long_option( ARGPARSE_ARGS *arg, + ARGPARSE_OPTS *opts, const char *keyword ) +{ + int i; + size_t n; + + (void)arg; + + /* Would be better if we can do a binary search, but it is not + possible to reorder our option table because we would mess + up our help strings - What we can do is: Build a nice option + lookup table when this function is first invoked */ + if( !*keyword ) + return -1; + for(i=0; opts[i].short_opt; i++ ) + if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) ) + return i; +#if 0 + { + ALIAS_DEF a; + /* see whether it is an alias */ + for( a = args->internal.aliases; a; a = a->next ) { + if( !strcmp( a->name, keyword) ) { + /* todo: must parse the alias here */ + args->internal.cur_alias = a; + return -3; /* alias available */ + } + } + } +#endif + /* not found, see whether it is an abbreviation */ + /* aliases may not be abbreviated */ + n = strlen( keyword ); + for(i=0; opts[i].short_opt; i++ ) { + if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) { + int j; + for(j=i+1; opts[j].short_opt; j++ ) { + if( opts[j].long_opt + && !strncmp( opts[j].long_opt, keyword, n ) ) + return -2; /* abbreviation is ambiguous */ + } + return i; + } + } + return -1; /* Not found. */ +} + +int +arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) +{ + int idx; + int argc; + char **argv; + char *s, *s2; + int i; + + initialize( arg, NULL, NULL ); + argc = *arg->argc; + argv = *arg->argv; + idx = arg->internal.idx; + + if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0)) + { + /* Skip the first argument. */ + argc--; argv++; idx++; + } + + next_one: + if (!argc) + { + /* No more args. */ + arg->r_opt = 0; + goto leave; /* Ready. */ + } + + s = *argv; + arg->internal.last = s; + + if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL)) + { + arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */ + arg->r_type = 2; + arg->r.ret_str = s; + argc--; argv++; idx++; /* set to next one */ + } + else if( arg->internal.stopped ) + { + arg->r_opt = 0; + goto leave; /* Ready. */ + } + else if ( *s == '-' && s[1] == '-' ) + { + /* Long option. */ + char *argpos; + + arg->internal.inarg = 0; + if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP)) + { + /* Stop option processing. */ + arg->internal.stopped = 1; + arg->flags |= ARGPARSE_FLAG_STOP_SEEN; + argc--; argv++; idx++; + goto next_one; + } + + argpos = strchr( s+2, '=' ); + if ( argpos ) + *argpos = 0; + i = find_long_option ( arg, opts, s+2 ); + if ( argpos ) + *argpos = '='; + + if ( i < 0 && !strcmp ( "help", s+2) ) + show_help (opts, arg->flags); + else if ( i < 0 && !strcmp ( "version", s+2) ) + { + if (!(arg->flags & ARGPARSE_FLAG_NOVERSION)) + { + show_version (); + exit(0); + } + } + else if ( i < 0 && !strcmp( "warranty", s+2)) + { + writestrings (0, strusage (16), "\n", NULL); + exit (0); + } + else if ( i < 0 && !strcmp( "dump-options", s+2) ) + { + for (i=0; opts[i].short_opt; i++ ) + { + if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE)) + writestrings (0, "--", opts[i].long_opt, "\n", NULL); + } + writestrings (0, "--dump-options\n--help\n--version\n--warranty\n", + NULL); + exit (0); + } + + if ( i == -2 ) + arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION; + else if ( i == -1 ) + { + arg->r_opt = ARGPARSE_INVALID_OPTION; + arg->r.ret_str = s+2; + } + else + arg->r_opt = opts[i].short_opt; + if ( i < 0 ) + ; + else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) + { + if ( argpos ) + { + s2 = argpos+1; + if ( !*s2 ) + s2 = NULL; + } + else + s2 = argv[1]; + if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */ + } + else if ( !s2 ) + { + arg->r_opt = ARGPARSE_MISSING_ARG; + } + else if ( !argpos && *s2 == '-' + && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + /* The argument is optional and the next seems to be an + option. We do not check this possible option but + assume no argument */ + arg->r_type = ARGPARSE_TYPE_NONE; + } + else + { + set_opt_arg (arg, opts[i].flags, s2); + if ( !argpos ) + { + argc--; argv++; idx++; /* Skip one. */ + } + } + } + else + { + /* Does not take an argument. */ + if ( argpos ) + arg->r_type = ARGPARSE_UNEXPECTED_ARG; + else + arg->r_type = 0; + } + argc--; argv++; idx++; /* Set to next one. */ + } + else if ( (*s == '-' && s[1]) || arg->internal.inarg ) + { + /* Short option. */ + int dash_kludge = 0; + + i = 0; + if ( !arg->internal.inarg ) + { + arg->internal.inarg++; + if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) ) + { + for (i=0; opts[i].short_opt; i++ ) + if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1)) + { + dash_kludge = 1; + break; + } + } + } + s += arg->internal.inarg; + + if (!dash_kludge ) + { + for (i=0; opts[i].short_opt; i++ ) + if ( opts[i].short_opt == *s ) + break; + } + + if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) + show_help (opts, arg->flags); + + arg->r_opt = opts[i].short_opt; + if (!opts[i].short_opt ) + { + arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)? + ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION; + arg->internal.inarg++; /* Point to the next arg. */ + arg->r.ret_str = s; + } + else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) + { + if ( s[1] && !dash_kludge ) + { + s2 = s+1; + set_opt_arg (arg, opts[i].flags, s2); + } + else + { + s2 = argv[1]; + if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + arg->r_type = ARGPARSE_TYPE_NONE; + } + else if ( !s2 ) + { + arg->r_opt = ARGPARSE_MISSING_ARG; + } + else if ( *s2 == '-' && s2[1] + && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + /* The argument is optional and the next seems to + be an option. We do not check this possible + option but assume no argument. */ + arg->r_type = ARGPARSE_TYPE_NONE; + } + else + { + set_opt_arg (arg, opts[i].flags, s2); + argc--; argv++; idx++; /* Skip one. */ + } + } + s = "x"; /* This is so that !s[1] yields false. */ + } + else + { + /* Does not take an argument. */ + arg->r_type = ARGPARSE_TYPE_NONE; + arg->internal.inarg++; /* Point to the next arg. */ + } + if ( !s[1] || dash_kludge ) + { + /* No more concatenated short options. */ + arg->internal.inarg = 0; + argc--; argv++; idx++; + } + } + else if ( arg->flags & ARGPARSE_FLAG_MIXED ) + { + arg->r_opt = ARGPARSE_IS_ARG; + arg->r_type = 2; + arg->r.ret_str = s; + argc--; argv++; idx++; /* Set to next one. */ + } + else + { + arg->internal.stopped = 1; /* Stop option processing. */ + goto next_one; + } + + leave: + *arg->argc = argc; + *arg->argv = argv; + arg->internal.idx = idx; + return arg->r_opt; +} + + +/* Returns: -1 on error, 0 for an integer type and 1 for a non integer + type argument. */ +static int +set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s) +{ + int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10; + long l; + + switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) ) + { + case ARGPARSE_TYPE_LONG: + case ARGPARSE_TYPE_INT: + errno = 0; + l = strtol (s, NULL, base); + if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) + { + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + if (arg->r_type == ARGPARSE_TYPE_LONG) + arg->r.ret_long = l; + else if ( (l < 0 && l < INT_MIN) || l > INT_MAX ) + { + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + else + arg->r.ret_int = (int)l; + return 0; + + case ARGPARSE_TYPE_ULONG: + while (isascii (*s) && isspace(*s)) + s++; + if (*s == '-') + { + arg->r.ret_ulong = 0; + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + errno = 0; + arg->r.ret_ulong = strtoul (s, NULL, base); + if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE) + { + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + return 0; + + case ARGPARSE_TYPE_STRING: + default: + arg->r.ret_str = s; + return 1; + } +} + + +static size_t +long_opt_strlen( ARGPARSE_OPTS *o ) +{ + size_t n = strlen (o->long_opt); + + if ( o->description && *o->description == '|' ) + { + const char *s; + int is_utf8 = is_native_utf8 (); + + s=o->description+1; + if ( *s != '=' ) + n++; + /* For a (mostly) correct length calculation we exclude + continuation bytes (10xxxxxx) if we are on a native utf8 + terminal. */ + for (; *s && *s != '|'; s++ ) + if ( is_utf8 && (*s&0xc0) != 0x80 ) + n++; + } + return n; +} + + +/**************** + * Print formatted help. The description string has some special + * meanings: + * - A description string which is "@" suppresses help output for + * this option + * - a description,ine which starts with a '@' and is followed by + * any other characters is printed as is; this may be used for examples + * ans such. + * - A description which starts with a '|' outputs the string between this + * bar and the next one as arguments of the long option. + */ +static void +show_help (ARGPARSE_OPTS *opts, unsigned int flags) +{ + const char *s; + char tmp[2]; + + show_version (); + writestrings (0, "\n", NULL); + s = strusage (42); + if (s && *s == '1') + { + s = strusage (40); + writestrings (1, s, NULL); + if (*s && s[strlen(s)] != '\n') + writestrings (1, "\n", NULL); + } + s = strusage(41); + writestrings (0, s, "\n", NULL); + if ( opts[0].description ) + { + /* Auto format the option description. */ + int i,j, indent; + + /* Get max. length of long options. */ + for (i=indent=0; opts[i].short_opt; i++ ) + { + if ( opts[i].long_opt ) + if ( !opts[i].description || *opts[i].description != '@' ) + if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 ) + indent = j; + } + + /* Example: " -v, --verbose Viele Sachen ausgeben" */ + indent += 10; + if ( *opts[0].description != '@' ) + writestrings (0, "Options:", "\n", NULL); + for (i=0; opts[i].short_opt; i++ ) + { + s = map_static_macro_string (_( opts[i].description )); + if ( s && *s== '@' && !s[1] ) /* Hide this line. */ + continue; + if ( s && *s == '@' ) /* Unindented comment only line. */ + { + for (s++; *s; s++ ) + { + if ( *s == '\n' ) + { + if( s[1] ) + writestrings (0, "\n", NULL); + } + else + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + } + writestrings (0, "\n", NULL); + continue; + } + + j = 3; + if ( opts[i].short_opt < 256 ) + { + tmp[0] = opts[i].short_opt; + tmp[1] = 0; + writestrings (0, " -", tmp, NULL ); + if ( !opts[i].long_opt ) + { + if (s && *s == '|' ) + { + writestrings (0, " ", NULL); j++; + for (s++ ; *s && *s != '|'; s++, j++ ) + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + if ( *s ) + s++; + } + } + } + else + writestrings (0, " ", NULL); + if ( opts[i].long_opt ) + { + tmp[0] = opts[i].short_opt < 256?',':' '; + tmp[1] = 0; + j += writestrings (0, tmp, " --", opts[i].long_opt, NULL); + if (s && *s == '|' ) + { + if ( *++s != '=' ) + { + writestrings (0, " ", NULL); + j++; + } + for ( ; *s && *s != '|'; s++, j++ ) + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + if ( *s ) + s++; + } + writestrings (0, " ", NULL); + j += 3; + } + for (;j < indent; j++ ) + writestrings (0, " ", NULL); + if ( s ) + { + if ( *s && j > indent ) + { + writestrings (0, "\n", NULL); + for (j=0;j < indent; j++ ) + writestrings (0, " ", NULL); + } + for (; *s; s++ ) + { + if ( *s == '\n' ) + { + if ( s[1] ) + { + writestrings (0, "\n", NULL); + for (j=0; j < indent; j++ ) + writestrings (0, " ", NULL); + } + } + else + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + } + } + writestrings (0, "\n", NULL); + } + if ( (flags & ARGPARSE_FLAG_ONEDASH) ) + writestrings (0, "\n(A single dash may be used " + "instead of the double ones)\n", NULL); + } + if ( (s=strusage(19)) ) + { + writestrings (0, "\n", NULL); + writestrings (0, s, NULL); + } + flushstrings (0); + exit(0); +} + +static void +show_version () +{ + const char *s; + int i; + + /* Version line. */ + writestrings (0, strusage (11), NULL); + if ((s=strusage (12))) + writestrings (0, " (", s, ")", NULL); + writestrings (0, " ", strusage (13), "\n", NULL); + /* Additional version lines. */ + for (i=20; i < 30; i++) + if ((s=strusage (i))) + writestrings (0, s, "\n", NULL); + /* Copyright string. */ + if ((s=strusage (14))) + writestrings (0, s, "\n", NULL); + /* Licence string. */ + if( (s=strusage (10)) ) + writestrings (0, s, "\n", NULL); + /* Copying conditions. */ + if ( (s=strusage(15)) ) + writestrings (0, s, NULL); + /* Thanks. */ + if ((s=strusage(18))) + writestrings (0, s, NULL); + /* Additional program info. */ + for (i=30; i < 40; i++ ) + if ( (s=strusage (i)) ) + writestrings (0, s, NULL); + flushstrings (0); +} + + +void +usage (int level) +{ + const char *p; + + if (!level) + { + writestrings (1, strusage(11), " ", strusage(13), "; ", + strusage (14), "\n", NULL); + flushstrings (1); + } + else if (level == 1) + { + p = strusage (40); + writestrings (1, p, NULL); + if (*p && p[strlen(p)] != '\n') + writestrings (1, "\n", NULL); + exit (2); + } + else if (level == 2) + { + p = strusage (42); + if (p && *p == '1') + { + p = strusage (40); + writestrings (1, p, NULL); + if (*p && p[strlen(p)] != '\n') + writestrings (1, "\n", NULL); + } + writestrings (0, strusage(41), "\n", NULL); + exit (0); + } +} + +/* Level + * 0: Print copyright string to stderr + * 1: Print a short usage hint to stderr and terminate + * 2: Print a long usage hint to stdout and terminate + * 10: Return license info string + * 11: Return the name of the program + * 12: Return optional name of package which includes this program. + * 13: version string + * 14: copyright string + * 15: Short copying conditions (with LFs) + * 16: Long copying conditions (with LFs) + * 17: Optional printable OS name + * 18: Optional thanks list (with LFs) + * 19: Bug report info + *20..29: Additional lib version strings. + *30..39: Additional program info (with LFs) + * 40: short usage note (with LF) + * 41: long usage note (with LF) + * 42: Flag string: + * First char is '1': + * The short usage notes needs to be printed + * before the long usage note. + */ +const char * +strusage( int level ) +{ + const char *p = strusage_handler? strusage_handler(level) : NULL; + + if ( p ) + return map_static_macro_string (p); + + switch ( level ) + { + + case 10: +#if ARGPARSE_GPL_VERSION == 3 + p = ("License GPLv3+: GNU GPL version 3 or later " + ""); +#else + p = ("License GPLv2+: GNU GPL version 2 or later " + ""); +#endif + break; + case 11: p = "foo"; break; + case 13: p = "0.0"; break; + case 14: p = ARGPARSE_CRIGHT_STR; break; + case 15: p = +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n"; + break; + case 16: p = +"This is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation; either version " +ARGPARSE_STR2(ARGPARSE_GPL_VERSION) +" of the License, or\n" +"(at your option) any later version.\n\n" +"It is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n\n" +"You should have received a copy of the GNU General Public License\n" +"along with this software. If not, see .\n"; + break; + case 40: /* short and long usage */ + case 41: p = ""; break; + } + + return p; +} + + +/* Set the usage handler. This function is basically a constructor. */ +void +set_strusage ( const char *(*f)( int ) ) +{ + strusage_handler = f; +} + + +#ifdef TEST +static struct { + int verbose; + int debug; + char *outfile; + char *crf; + int myopt; + int echo; + int a_long_one; +} opt; + +int +main(int argc, char **argv) +{ + ARGPARSE_OPTS opts[] = { + ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"), + ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, " + "was wir eingegeben haben")), + ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"), + ARGPARSE_s_s('o', "output", 0 ), + ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ), + /* Note that on a non-utf8 terminal the ß might garble the output. */ + ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"), + ARGPARSE_o_i('m', "my-option", 0), + ARGPARSE_s_n(500, "a-long-option", 0 ), + ARGPARSE_end() + }; + ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL + | ARGPARSE_FLAG_MIXED + | ARGPARSE_FLAG_ONEDASH) }; + int i; + + while (arg_parse (&pargs, opts)) + { + switch (pargs.r_opt) + { + case ARGPARSE_IS_ARG : + printf ("arg='%s'\n", pargs.r.ret_str); + break; + case 'v': opt.verbose++; break; + case 'e': opt.echo++; break; + case 'd': opt.debug++; break; + case 'o': opt.outfile = pargs.r.ret_str; break; + case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; + case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; + case 500: opt.a_long_one++; break; + default : pargs.err = ARGPARSE_PRINT_WARNING; break; + } + } + for (i=0; i < argc; i++ ) + printf ("%3d -> (%s)\n", i, argv[i] ); + puts ("Options:"); + if (opt.verbose) + printf (" verbose=%d\n", opt.verbose ); + if (opt.debug) + printf (" debug=%d\n", opt.debug ); + if (opt.outfile) + printf (" outfile='%s'\n", opt.outfile ); + if (opt.crf) + printf (" crffile='%s'\n", opt.crf ); + if (opt.myopt) + printf (" myopt=%d\n", opt.myopt ); + if (opt.a_long_one) + printf (" a-long-one=%d\n", opt.a_long_one ); + if (opt.echo) + printf (" echo=%d\n", opt.echo ); + + return 0; +} +#endif /*TEST*/ + +/**** bottom of file ****/ diff --git a/common/argparse.h b/common/argparse.h new file mode 100644 index 0000000..81e881d --- /dev/null +++ b/common/argparse.h @@ -0,0 +1,203 @@ +/* argparse.h - Argument parser for option handling. + * Copyright (C) 1998,1999,2000,2001,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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_ARGPARSE_H +#define GNUPG_COMMON_ARGPARSE_H + +#include + +typedef struct +{ + int *argc; /* Pointer to ARGC (value subject to change). */ + char ***argv; /* Pointer to ARGV (value subject to change). */ + unsigned int flags; /* Global flags. May be set prior to calling the + parser. The parser may change the value. */ + int err; /* Print error description for last option. + Either 0, ARGPARSE_PRINT_WARNING or + ARGPARSE_PRINT_ERROR. */ + + int r_opt; /* Returns option code. */ + int r_type; /* Returns type of option value. */ + union { + int ret_int; + long ret_long; + unsigned long ret_ulong; + char *ret_str; + } r; /* Return values */ + + struct { + int idx; + int inarg; + int stopped; + const char *last; + void *aliases; + const void *cur_alias; + void *iio_list; + } internal; /* Private - do not change. */ +} ARGPARSE_ARGS; + +typedef struct +{ + int short_opt; + const char *long_opt; + unsigned int flags; + const char *description; /* Optional option description. */ +} ARGPARSE_OPTS; + + +/* Global flags (ARGPARSE_ARGS). */ +#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ +#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return + remaining args with R_OPT set to -1. */ +#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ +#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ +#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ +#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ +#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ + +#define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */ + +/* Flags for each option (ARGPARSE_OPTS). The type code may be + ORed with the OPT flags. */ +#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ +#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ +#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ +#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ +#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ +#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ +#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ +#define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */ +#define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */ + +#define ARGPARSE_TYPE_MASK 7 /* Mask for the type values (internal). */ + +/* A set of macros to make option definitions easier to read. */ +#define ARGPARSE_x(s,l,t,f,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } + +#define ARGPARSE_s(s,l,t,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t, (d) } +#define ARGPARSE_s_n(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_NONE, (d) } +#define ARGPARSE_s_i(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_INT, (d) } +#define ARGPARSE_s_s(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_STRING, (d) } +#define ARGPARSE_s_l(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_LONG, (d) } +#define ARGPARSE_s_u(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_ULONG, (d) } + +#define ARGPARSE_o(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } + +#define ARGPARSE_p(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_op(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_c(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } + +#define ARGPARSE_ignore(s,l) \ + { (s), (l), (ARGPARSE_OPT_IGNORE), "@" } + +#define ARGPARSE_group(s,d) \ + { (s), NULL, 0, (d) } + +#define ARGPARSE_end() { 0, NULL, 0, NULL } + + +/* Other constants. */ +#define ARGPARSE_PRINT_WARNING 1 +#define ARGPARSE_PRINT_ERROR 2 + + +/* Error values. */ +#define ARGPARSE_IS_ARG (-1) +#define ARGPARSE_INVALID_OPTION (-2) +#define ARGPARSE_MISSING_ARG (-3) +#define ARGPARSE_KEYWORD_TOO_LONG (-4) +#define ARGPARSE_READ_ERROR (-5) +#define ARGPARSE_UNEXPECTED_ARG (-6) +#define ARGPARSE_INVALID_COMMAND (-7) +#define ARGPARSE_AMBIGUOUS_OPTION (-8) +#define ARGPARSE_AMBIGUOUS_COMMAND (-9) +#define ARGPARSE_INVALID_ALIAS (-10) +#define ARGPARSE_OUT_OF_CORE (-11) +#define ARGPARSE_INVALID_ARG (-12) + + +int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); +int optfile_parse (FILE *fp, const char *filename, unsigned *lineno, + ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); +void usage (int level); +const char *strusage (int level); +void set_strusage (const char *(*f)( int )); +void argparse_register_outfnc (int (*fnc)(int, const char *)); + +#endif /*GNUPG_COMMON_ARGPARSE_H*/ diff --git a/common/asshelp.c b/common/asshelp.c new file mode 100644 index 0000000..2cab310 --- /dev/null +++ b/common/asshelp.c @@ -0,0 +1,673 @@ +/* asshelp.c - Helper functions for Assuan + * Copyright (C) 2002, 2004, 2007, 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "i18n.h" +#include "util.h" +#include "exechelp.h" +#include "sysutils.h" +#include "status.h" +#include "membuf.h" +#include "asshelp.h" + +/* The type we use for lock_agent_spawning. */ +#ifdef HAVE_W32_SYSTEM +# define lock_spawn_t HANDLE +#else +# define lock_spawn_t dotlock_t +#endif + +/* The time we wait until the agent or the dirmngr are ready for + operation after we started them before giving up. */ +#ifdef HAVE_W32CE_SYSTEM +# define SECS_TO_WAIT_FOR_AGENT 30 +# define SECS_TO_WAIT_FOR_DIRMNGR 30 +#else +# define SECS_TO_WAIT_FOR_AGENT 5 +# define SECS_TO_WAIT_FOR_DIRMNGR 5 +#endif + +/* A bitfield that specifies the assuan categories to log. This is + identical to the default log handler of libassuan. We need to do + it ourselves because we use a custom log handler and want to use + the same assuan variables to select the categories to log. */ +static int log_cats; +#define TEST_LOG_CAT(x) (!! (log_cats & (1 << (x - 1)))) + +/* The assuan log monitor used to temporary inhibit log messages from + * assuan. */ +static int (*my_log_monitor) (assuan_context_t ctx, + unsigned int cat, + const char *msg); + + +static int +my_libassuan_log_handler (assuan_context_t ctx, void *hook, + unsigned int cat, const char *msg) +{ + unsigned int dbgval; + + if (! TEST_LOG_CAT (cat)) + return 0; + + dbgval = hook? *(unsigned int*)hook : 0; + if (!(dbgval & 1024)) + return 0; /* Assuan debugging is not enabled. */ + + if (ctx && my_log_monitor && !my_log_monitor (ctx, cat, msg)) + return 0; /* Temporary disabled. */ + + if (msg) + log_string (GPGRT_LOG_DEBUG, msg); + + return 1; +} + + +/* Setup libassuan to use our own logging functions. Should be used + early at startup. */ +void +setup_libassuan_logging (unsigned int *debug_var_address, + int (*log_monitor)(assuan_context_t ctx, + unsigned int cat, + const char *msg)) +{ + char *flagstr; + + flagstr = getenv ("ASSUAN_DEBUG"); + if (flagstr) + log_cats = atoi (flagstr); + else /* Default to log the control channel. */ + log_cats = (1 << (ASSUAN_LOG_CONTROL - 1)); + my_log_monitor = log_monitor; + assuan_set_log_cb (my_libassuan_log_handler, debug_var_address); +} + + +/* Change the Libassuan log categories to those given by NEWCATS. + NEWCATS is 0 the default category of ASSUAN_LOG_CONTROL is + selected. Note, that setup_libassuan_logging overrides the values + given here. */ +void +set_libassuan_log_cats (unsigned int newcats) +{ + if (newcats) + log_cats = newcats; + else /* Default to log the control channel. */ + log_cats = (1 << (ASSUAN_LOG_CONTROL - 1)); +} + + + +static gpg_error_t +send_one_option (assuan_context_t ctx, gpg_err_source_t errsource, + const char *name, const char *value, int use_putenv) +{ + gpg_error_t err; + char *optstr; + + (void)errsource; + + if (!value || !*value) + err = 0; /* Avoid sending empty strings. */ + else if (asprintf (&optstr, "OPTION %s%s=%s", + use_putenv? "putenv=":"", name, value) < 0) + err = gpg_error_from_syserror (); + else + { + err = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); + xfree (optstr); + } + + return err; +} + + +/* Send the assuan commands pertaining to the pinentry environment. The + OPT_* arguments are optional and may be used to override the + defaults taken from the current locale. */ +gpg_error_t +send_pinentry_environment (assuan_context_t ctx, + gpg_err_source_t errsource, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env) + +{ + gpg_error_t err = 0; +#if defined(HAVE_SETLOCALE) + char *old_lc = NULL; +#endif + char *dft_lc = NULL; + const char *dft_ttyname; + int iterator; + const char *name, *assname, *value; + int is_default; + + iterator = 0; + while ((name = session_env_list_stdenvnames (&iterator, &assname))) + { + value = session_env_getenv_or_default (session_env, name, NULL); + if (!value) + continue; + + if (assname) + err = send_one_option (ctx, errsource, assname, value, 0); + else + { + err = send_one_option (ctx, errsource, name, value, 1); + if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) + err = 0; /* Server too old; can't pass the new envvars. */ + } + if (err) + return err; + } + + + dft_ttyname = session_env_getenv_or_default (session_env, "GPG_TTY", + &is_default); + if (dft_ttyname && !is_default) + dft_ttyname = NULL; /* We need the default value. */ + + /* Send the value for LC_CTYPE. */ +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + old_lc = xtrystrdup (old_lc); + if (!old_lc) + return gpg_error_from_syserror (); + } + dft_lc = setlocale (LC_CTYPE, ""); +#endif + if (opt_lc_ctype || (dft_ttyname && dft_lc)) + { + err = send_one_option (ctx, errsource, "lc-ctype", + opt_lc_ctype ? opt_lc_ctype : dft_lc, 0); + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + xfree (old_lc); + } +#endif + if (err) + return err; + + /* Send the value for LC_MESSAGES. */ +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + old_lc = xtrystrdup (old_lc); + if (!old_lc) + return gpg_error_from_syserror (); + } + dft_lc = setlocale (LC_MESSAGES, ""); +#endif + if (opt_lc_messages || (dft_ttyname && dft_lc)) + { + err = send_one_option (ctx, errsource, "lc-messages", + opt_lc_messages ? opt_lc_messages : dft_lc, 0); + } +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + xfree (old_lc); + } +#endif + if (err) + return err; + + return 0; +} + + +/* Lock a spawning process. The caller needs to provide the address + of a variable to store the lock information and the name or the + process. */ +static gpg_error_t +lock_spawning (lock_spawn_t *lock, const char *homedir, const char *name, + int verbose) +{ + char *fname; + (void)verbose; + + *lock = NULL; + + fname = make_absfilename_try + (homedir, + !strcmp (name, "agent")? "gnupg_spawn_agent_sentinel": + !strcmp (name, "dirmngr")? "gnupg_spawn_dirmngr_sentinel": + /* */ "gnupg_spawn_unknown_sentinel", + NULL); + if (!fname) + return gpg_error_from_syserror (); + + *lock = dotlock_create (fname, 0); + xfree (fname); + if (!*lock) + return gpg_error_from_syserror (); + + /* FIXME: We should use a timeout of 5000 here - however + make_dotlock does not yet support values other than -1 and 0. */ + if (dotlock_take (*lock, -1)) + return gpg_error_from_syserror (); + + return 0; +} + + +/* Unlock the spawning process. */ +static void +unlock_spawning (lock_spawn_t *lock, const char *name) +{ + if (*lock) + { + (void)name; + dotlock_destroy (*lock); + *lock = NULL; + } +} + +/* Try to connect to the agent via socket or start it if it is not + running and AUTOSTART is set. Handle the server's initial + greeting. Returns a new assuan context at R_CTX or an error + code. */ +gpg_error_t +start_new_gpg_agent (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env, + int autostart, int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg) +{ + gpg_error_t err; + assuan_context_t ctx; + int did_success_msg = 0; + char *sockname; + const char *argv[6]; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("error allocating assuan context: %s\n", gpg_strerror (err)); + return err; + } + + sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + if (!sockname) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + assuan_release (ctx); + return err; + } + + err = assuan_socket_connect (ctx, sockname, 0, 0); + if (err && autostart) + { + char *abs_homedir; + lock_spawn_t lock; + char *program = NULL; + const char *program_arg = NULL; + char *p; + const char *s; + int i; + + /* With no success start a new server. */ + if (!agent_program || !*agent_program) + agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT); + else if ((s=strchr (agent_program, '|')) && s[1] == '-' && s[2]=='-') + { + /* Hack to insert an additional option on the command line. */ + program = xtrystrdup (agent_program); + if (!program) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + xfree (sockname); + assuan_release (ctx); + return tmperr; + } + p = strchr (program, '|'); + *p++ = 0; + program_arg = p; + } + + if (verbose) + log_info (_("no running gpg-agent - starting '%s'\n"), + agent_program); + + if (status_cb) + status_cb (status_cb_arg, STATUS_PROGRESS, + "starting_agent ? 0 0", NULL); + + /* We better pass an absolute home directory to the agent just + in case gpg-agent does not convert the passed name to an + absolute one (which it should do). */ + abs_homedir = make_absfilename_try (gnupg_homedir (), NULL); + if (!abs_homedir) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error building filename: %s\n",gpg_strerror (tmperr)); + xfree (sockname); + assuan_release (ctx); + xfree (program); + return tmperr; + } + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error flushing pending output: %s\n", + strerror (errno)); + xfree (sockname); + assuan_release (ctx); + xfree (abs_homedir); + xfree (program); + return tmperr; + } + + /* If the agent has been configured for use with a standard + socket, an environment variable is not required and thus + we we can savely start the agent here. */ + i = 0; + argv[i++] = "--homedir"; + argv[i++] = abs_homedir; + argv[i++] = "--use-standard-socket"; + if (program_arg) + argv[i++] = program_arg; + argv[i++] = "--daemon"; + argv[i++] = NULL; + + if (!(err = lock_spawning (&lock, gnupg_homedir (), "agent", verbose)) + && assuan_socket_connect (ctx, sockname, 0, 0)) + { + err = gnupg_spawn_process_detached (program? program : agent_program, + argv, NULL); + if (err) + log_error ("failed to start agent '%s': %s\n", + agent_program, gpg_strerror (err)); + else + { + for (i=0; i < SECS_TO_WAIT_FOR_AGENT; i++) + { + if (verbose) + log_info (_("waiting for the agent to come up ... (%ds)\n"), + SECS_TO_WAIT_FOR_AGENT - i); + gnupg_sleep (1); + err = assuan_socket_connect (ctx, sockname, 0, 0); + if (!err) + { + if (verbose) + { + log_info (_("connection to agent established\n")); + did_success_msg = 1; + } + break; + } + } + } + } + + unlock_spawning (&lock, "agent"); + xfree (abs_homedir); + xfree (program); + } + xfree (sockname); + if (err) + { + if (autostart || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) + log_error ("can't connect to the agent: %s\n", gpg_strerror (err)); + assuan_release (ctx); + return gpg_err_make (errsource, GPG_ERR_NO_AGENT); + } + + if (debug && !did_success_msg) + log_debug ("connection to agent established\n"); + + err = assuan_transact (ctx, "RESET", + NULL, NULL, NULL, NULL, NULL, NULL); + if (!err) + { + err = send_pinentry_environment (ctx, errsource, + opt_lc_ctype, opt_lc_messages, + session_env); + if (gpg_err_code (err) == GPG_ERR_FORBIDDEN + && gpg_err_source (err) == GPG_ERR_SOURCE_GPGAGENT) + { + /* Check whether we are in restricted mode. */ + if (!assuan_transact (ctx, "GETINFO restricted", + NULL, NULL, NULL, NULL, NULL, NULL)) + { + if (verbose) + log_info (_("connection to agent is in restricted mode\n")); + err = 0; + } + } + } + if (err) + { + assuan_release (ctx); + return err; + } + + *r_ctx = ctx; + return 0; +} + + +/* Try to connect to the dirmngr via a socket. On platforms + supporting it, start it up if needed and if AUTOSTART is true. + Returns a new assuan context at R_CTX or an error code. */ +gpg_error_t +start_new_dirmngr (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *dirmngr_program, + int autostart, + int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg) +{ + gpg_error_t err; + assuan_context_t ctx; + const char *sockname; + int did_success_msg = 0; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("error allocating assuan context: %s\n", gpg_strerror (err)); + return err; + } + + sockname = dirmngr_socket_name (); + err = assuan_socket_connect (ctx, sockname, 0, 0); + +#ifdef USE_DIRMNGR_AUTO_START + if (err && autostart) + { + lock_spawn_t lock; + const char *argv[4]; + char *abs_homedir; + + /* No connection: Try start a new Dirmngr. */ + if (!dirmngr_program || !*dirmngr_program) + dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR); + + if (verbose) + log_info (_("no running Dirmngr - starting '%s'\n"), + dirmngr_program); + + if (status_cb) + status_cb (status_cb_arg, STATUS_PROGRESS, + "starting_dirmngr ? 0 0", NULL); + + abs_homedir = make_absfilename (gnupg_homedir (), NULL); + if (!abs_homedir) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error building filename: %s\n",gpg_strerror (tmperr)); + assuan_release (ctx); + return tmperr; + } + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error flushing pending output: %s\n", + strerror (errno)); + assuan_release (ctx); + return tmperr; + } + + argv[0] = "--daemon"; + /* Try starting the daemon. Versions of dirmngr < 2.1.15 do + * this only if the home directory is given on the command line. */ + argv[1] = "--homedir"; + argv[2] = abs_homedir; + argv[3] = NULL; + + if (!(err = lock_spawning (&lock, gnupg_homedir (), "dirmngr", verbose)) + && assuan_socket_connect (ctx, sockname, 0, 0)) + { + err = gnupg_spawn_process_detached (dirmngr_program, argv, NULL); + if (err) + log_error ("failed to start the dirmngr '%s': %s\n", + dirmngr_program, gpg_strerror (err)); + else + { + int i; + + for (i=0; i < SECS_TO_WAIT_FOR_DIRMNGR; i++) + { + if (verbose) + log_info (_("waiting for the dirmngr " + "to come up ... (%ds)\n"), + SECS_TO_WAIT_FOR_DIRMNGR - i); + gnupg_sleep (1); + err = assuan_socket_connect (ctx, sockname, 0, 0); + if (!err) + { + if (verbose) + { + log_info (_("connection to the dirmngr" + " established\n")); + did_success_msg = 1; + } + break; + } + } + } + } + + unlock_spawning (&lock, "dirmngr"); + xfree (abs_homedir); + } +#else + (void)dirmngr_program; + (void)verbose; + (void)status_cb; + (void)status_cb_arg; +#endif /*USE_DIRMNGR_AUTO_START*/ + + if (err) + { + if (autostart || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) + log_error ("connecting dirmngr at '%s' failed: %s\n", + sockname, gpg_strerror (err)); + assuan_release (ctx); + return gpg_err_make (errsource, GPG_ERR_NO_DIRMNGR); + } + + if (debug && !did_success_msg) + log_debug ("connection to the dirmngr established\n"); + + *r_ctx = ctx; + return 0; +} + + +/* Return the version of a server using "GETINFO version". On success + 0 is returned and R_VERSION receives a malloced string with the + version which must be freed by the caller. On error NULL is stored + at R_VERSION and an error code returned. Mode is in general 0 but + certain values may be used to modify the used version command: + + MODE == 0 = Use "GETINFO version" + MODE == 2 - Use "SCD GETINFO version" + */ +gpg_error_t +get_assuan_server_version (assuan_context_t ctx, int mode, char **r_version) +{ + gpg_error_t err; + membuf_t data; + + init_membuf (&data, 64); + err = assuan_transact (ctx, + mode == 2? "SCD GETINFO version" + /**/ : "GETINFO version", + put_membuf_cb, &data, + NULL, NULL, NULL, NULL); + if (err) + { + xfree (get_membuf (&data, NULL)); + *r_version = NULL; + } + else + { + put_membuf (&data, "", 1); + *r_version = get_membuf (&data, NULL); + if (!*r_version) + err = gpg_error_from_syserror (); + } + return err; +} diff --git a/common/asshelp.h b/common/asshelp.h new file mode 100644 index 0000000..f169d87 --- /dev/null +++ b/common/asshelp.h @@ -0,0 +1,97 @@ +/* asshelp.h - Helper functions for Assuan + * Copyright (C) 2004, 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_ASSHELP_H +#define GNUPG_COMMON_ASSHELP_H + +#include +#include + +#include "session-env.h" +#include "util.h" + +/*-- asshelp.c --*/ + +void setup_libassuan_logging (unsigned int *debug_var_address, + int (*log_monitor)(assuan_context_t ctx, + unsigned int cat, + const char *msg)); +void set_libassuan_log_cats (unsigned int newcats); + + +gpg_error_t +send_pinentry_environment (assuan_context_t ctx, + gpg_err_source_t errsource, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env); + +/* This function is used by the call-agent.c modules to fire up a new + agent. */ +gpg_error_t +start_new_gpg_agent (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env, + int autostart, int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg); + +/* This function is used to connect to the dirmngr. On some platforms + the function is able starts a dirmngr process if needed. */ +gpg_error_t +start_new_dirmngr (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *dirmngr_program, + int autostart, int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg); + +/* Return the version of a server using "GETINFO version". */ +gpg_error_t get_assuan_server_version (assuan_context_t ctx, + int mode, char **r_version); + + +/*-- asshelp2.c --*/ + +/* Helper function to print an assuan status line using a printf + format string. */ +gpg_error_t print_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, + ...) GPGRT_ATTR_PRINTF(3,4); +gpg_error_t vprint_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, + va_list arg_ptr) GPGRT_ATTR_PRINTF(3,0); + + +#endif /*GNUPG_COMMON_ASSHELP_H*/ diff --git a/common/asshelp2.c b/common/asshelp2.c new file mode 100644 index 0000000..f85c1e6 --- /dev/null +++ b/common/asshelp2.c @@ -0,0 +1,73 @@ +/* asshelp2.c - More helper functions for Assuan + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "asshelp.h" + +/* Helper function to print an assuan status line using a printf + format string. */ +gpg_error_t +vprint_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, va_list arg_ptr) +{ + int rc; + char *buf; + + rc = gpgrt_vasprintf (&buf, format, arg_ptr); + if (rc < 0) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + rc = assuan_write_status (ctx, keyword, buf); + xfree (buf); + return rc; +} + + +/* Helper function to print an assuan status line using a printf + format string. */ +gpg_error_t +print_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, ...) +{ + va_list arg_ptr; + gpg_error_t err; + + va_start (arg_ptr, format); + err = vprint_assuan_status (ctx, keyword, format, arg_ptr); + va_end (arg_ptr); + return err; +} diff --git a/common/audit-events.h b/common/audit-events.h new file mode 100644 index 0000000..ae9fde2 --- /dev/null +++ b/common/audit-events.h @@ -0,0 +1,116 @@ +/* Output of mkstrtable.awk. DO NOT EDIT. */ + +/* audit.h - Definitions for the audit subsystem + * Copyright (C) 2007 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 3 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, see . + */ + +/* The purpose of this complex string table is to produce + optimal code with a minimum of relocations. */ + +static const char eventstr_msgstr[] = + "null event" "\0" + "setup ready" "\0" + "agent ready" "\0" + "dirmngr ready" "\0" + "gpg ready" "\0" + "gpgsm ready" "\0" + "g13 ready" "\0" + "got data" "\0" + "detached signature" "\0" + "cert only sig" "\0" + "data hash algo" "\0" + "attr hash algo" "\0" + "data cipher algo" "\0" + "bad data hash algo" "\0" + "bad data cipher algo" "\0" + "data hashing" "\0" + "read error" "\0" + "write error" "\0" + "usage error" "\0" + "save cert" "\0" + "new sig" "\0" + "sig name" "\0" + "sig status" "\0" + "new recp" "\0" + "recp name" "\0" + "recp result" "\0" + "decryption result" "\0" + "validate chain" "\0" + "chain begin" "\0" + "chain cert" "\0" + "chain rootcert" "\0" + "chain end" "\0" + "chain status" "\0" + "root trusted" "\0" + "crl check" "\0" + "got recipients" "\0" + "session key" "\0" + "encrypted to" "\0" + "encryption done" "\0" + "signed by" "\0" + "signing done"; + +static const int eventstr_msgidx[] = + { + 0, + 11, + 23, + 35, + 49, + 59, + 71, + 81, + 90, + 109, + 123, + 138, + 153, + 170, + 189, + 210, + 223, + 234, + 246, + 258, + 268, + 276, + 285, + 296, + 305, + 315, + 327, + 345, + 360, + 372, + 383, + 398, + 408, + 421, + 434, + 444, + 459, + 471, + 484, + 500, + 510, + + }; + +#define eventstr_msgidxof(code) (0 ? -1 \ + : ((code >= 0) && (code <= 40)) ? (code - 0) \ + : -1) diff --git a/common/audit.c b/common/audit.c new file mode 100644 index 0000000..7d545a3 --- /dev/null +++ b/common/audit.c @@ -0,0 +1,1323 @@ +/* audit.c - GnuPG's audit subsystem + * Copyright (C) 2007, 2009 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 3 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, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "i18n.h" +#include "audit.h" +#include "audit-events.h" + +/* A list to maintain a list of helptags. */ +struct helptag_s +{ + struct helptag_s *next; + const char *name; +}; +typedef struct helptag_s *helptag_t; + + +/* One log entry. */ +struct log_item_s +{ + audit_event_t event; /* The event. */ + gpg_error_t err; /* The logged error code. */ + int intvalue; /* A logged integer value. */ + char *string; /* A malloced string or NULL. */ + ksba_cert_t cert; /* A certifciate or NULL. */ + int have_err:1; + int have_intvalue:1; +}; +typedef struct log_item_s *log_item_t; + + + +/* The main audit object. */ +struct audit_ctx_s +{ + const char *failure; /* If set a description of the internal failure. */ + audit_type_t type; + + log_item_t log; /* The table with the log entries. */ + size_t logsize; /* The allocated size for LOG. */ + size_t logused; /* The used size of LOG. */ + + estream_t outstream; /* The current output stream. */ + int use_html; /* The output shall be HTML formatted. */ + int indentlevel; /* Current level of indentation. */ + helptag_t helptags; /* List of help keys. */ +}; + + + + +static void writeout_para (audit_ctx_t ctx, + const char *format, ...) GPGRT_ATTR_PRINTF(2,3); +static void writeout_li (audit_ctx_t ctx, const char *oktext, + const char *format, ...) GPGRT_ATTR_PRINTF(3,4); +static void writeout_rem (audit_ctx_t ctx, + const char *format, ...) GPGRT_ATTR_PRINTF(2,3); + + +/* Add NAME to the list of help tags. NAME needs to be a const string + an this function merly stores this pointer. */ +static void +add_helptag (audit_ctx_t ctx, const char *name) +{ + helptag_t item; + + for (item=ctx->helptags; item; item = item->next) + if (!strcmp (item->name, name)) + return; /* Already in the list. */ + item = xtrycalloc (1, sizeof *item); + if (!item) + return; /* Don't care about memory problems. */ + item->name = name; + item->next = ctx->helptags; + ctx->helptags = item; +} + + +/* Remove all help tags from the context. */ +static void +clear_helptags (audit_ctx_t ctx) +{ + while (ctx->helptags) + { + helptag_t tmp = ctx->helptags->next; + xfree (ctx->helptags); + ctx->helptags = tmp; + } +} + + + +static const char * +event2str (audit_event_t event) +{ + /* We need the cast so that compiler does not complain about an + always true comparison (>= 0) for an unsigned value. */ + int idx = eventstr_msgidxof ((int)event); + if (idx == -1) + return "Unknown event"; + else + return eventstr_msgstr + eventstr_msgidx[idx]; +} + + + +/* Create a new audit context. In case of an error NULL is returned + and errno set appropriately. */ +audit_ctx_t +audit_new (void) +{ + audit_ctx_t ctx; + + ctx = xtrycalloc (1, sizeof *ctx); + + return ctx; +} + + +/* Release an audit context. Passing NULL for CTX is allowed and does + nothing. */ +void +audit_release (audit_ctx_t ctx) +{ + int idx; + if (!ctx) + return; + if (ctx->log) + { + for (idx=0; idx < ctx->logused; idx++) + { + if (ctx->log[idx].string) + xfree (ctx->log[idx].string); + if (ctx->log[idx].cert) + ksba_cert_release (ctx->log[idx].cert); + } + xfree (ctx->log); + } + clear_helptags (ctx); + xfree (ctx); +} + + +/* Set the type for the audit operation. If CTX is NULL, this is a + dummy function. */ +void +audit_set_type (audit_ctx_t ctx, audit_type_t type) +{ + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + + if (ctx->type && ctx->type != type) + { + ctx->failure = "conflict in type initialization"; + return; + } + ctx->type = type; +} + + +/* Create a new log item and put it into the table. Return that log + item on success; return NULL on memory failure and mark that in + CTX. */ +static log_item_t +create_log_item (audit_ctx_t ctx) +{ + log_item_t item, table; + size_t size; + + if (!ctx->log) + { + size = 10; + table = xtrymalloc (size * sizeof *table); + if (!table) + { + ctx->failure = "Out of memory in create_log_item"; + return NULL; + } + ctx->log = table; + ctx->logsize = size; + item = ctx->log + 0; + ctx->logused = 1; + } + else if (ctx->logused >= ctx->logsize) + { + size = ctx->logsize + 10; + table = xtryrealloc (ctx->log, size * sizeof *table); + if (!table) + { + ctx->failure = "Out of memory while reallocating in create_log_item"; + return NULL; + } + ctx->log = table; + ctx->logsize = size; + item = ctx->log + ctx->logused++; + } + else + item = ctx->log + ctx->logused++; + + item->event = AUDIT_NULL_EVENT; + item->err = 0; + item->have_err = 0; + item->intvalue = 0; + item->have_intvalue = 0; + item->string = NULL; + item->cert = NULL; + + return item; + +} + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. */ +void +audit_log (audit_ctx_t ctx, audit_event_t event) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; +} + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also adds the result of the operation + to the log. */ +void +audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_ok"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; + item->err = err; + item->have_err = 1; +} + + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also add the integer VALUE to the log. */ +void +audit_log_i (audit_ctx_t ctx, audit_event_t event, int value) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_i"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; + item->intvalue = value; + item->have_intvalue = 1; +} + + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also add the integer VALUE to the log. */ +void +audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value) +{ + log_item_t item; + char *tmp; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_s"; + return; + } + tmp = xtrystrdup (value? value : ""); + if (!tmp) + { + ctx->failure = "Out of memory in audit_event"; + return; + } + if (!(item = create_log_item (ctx))) + { + xfree (tmp); + return; + } + item->event = event; + item->string = tmp; +} + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also adds the certificate CERT and the + result of an operation to the log. */ +void +audit_log_cert (audit_ctx_t ctx, audit_event_t event, + ksba_cert_t cert, gpg_error_t err) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_cert"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; + item->err = err; + item->have_err = 1; + if (cert) + { + ksba_cert_ref (cert); + item->cert = cert; + } +} + + +/* Write TEXT to the outstream. */ +static void +writeout (audit_ctx_t ctx, const char *text) +{ + if (ctx->use_html) + { + for (; *text; text++) + { + if (*text == '<') + es_fputs ("<", ctx->outstream); + else if (*text == '&') + es_fputs ("&", ctx->outstream); + else + es_putc (*text, ctx->outstream); + } + } + else + es_fputs (text, ctx->outstream); +} + + +/* Write TEXT to the outstream using a variable argument list. */ +static void +writeout_v (audit_ctx_t ctx, const char *format, va_list arg_ptr) +{ + char *buf; + + gpgrt_vasprintf (&buf, format, arg_ptr); + if (buf) + { + writeout (ctx, buf); + xfree (buf); + } + else + writeout (ctx, "[!!Out of core!!]"); +} + + +/* Write TEXT as a paragraph. */ +static void +writeout_para (audit_ctx_t ctx, const char *format, ...) +{ + va_list arg_ptr; + + if (ctx->use_html) + es_fputs ("

", ctx->outstream); + va_start (arg_ptr, format) ; + writeout_v (ctx, format, arg_ptr); + va_end (arg_ptr); + if (ctx->use_html) + es_fputs ("

\n", ctx->outstream); + else + es_fputc ('\n', ctx->outstream); +} + + +static void +enter_li (audit_ctx_t ctx) +{ + if (ctx->use_html) + { + if (!ctx->indentlevel) + { + es_fputs ("\n" + " \n" + " \n" + " \n" + " \n", + ctx->outstream); + } + } + ctx->indentlevel++; +} + + +static void +leave_li (audit_ctx_t ctx) +{ + ctx->indentlevel--; + if (ctx->use_html) + { + if (!ctx->indentlevel) + es_fputs ("
\n", ctx->outstream); + } +} + + +/* Write TEXT as a list element. If OKTEXT is not NULL, append it to + the last line. */ +static void +writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...) +{ + va_list arg_ptr; + const char *color = NULL; + + if (ctx->use_html && format && oktext) + { + if (!strcmp (oktext, "Yes") + || !strcmp (oktext, "good") ) + color = "green"; + else if (!strcmp (oktext, "No") + || !strcmp (oktext, "bad") ) + color = "red"; + } + + if (format && oktext) + { + const char *s = NULL; + + if (!strcmp (oktext, "Yes")) + oktext = _("Yes"); + else if (!strcmp (oktext, "No")) + oktext = _("No"); + else if (!strcmp (oktext, "good")) + { + /* TRANSLATORS: Copy the prefix between the vertical bars + verbatim. It will not be printed. */ + oktext = _("|audit-log-result|Good"); + } + else if (!strcmp (oktext, "bad")) + oktext = _("|audit-log-result|Bad"); + else if (!strcmp (oktext, "unsupported")) + oktext = _("|audit-log-result|Not supported"); + else if (!strcmp (oktext, "no-cert")) + oktext = _("|audit-log-result|No certificate"); + else if (!strcmp (oktext, "disabled")) + oktext = _("|audit-log-result|Not enabled"); + else if (!strcmp (oktext, "error")) + oktext = _("|audit-log-result|Error"); + else if (!strcmp (oktext, "not-used")) + oktext = _("|audit-log-result|Not used"); + else if (!strcmp (oktext, "okay")) + oktext = _("|audit-log-result|Okay"); + else if (!strcmp (oktext, "skipped")) + oktext = _("|audit-log-result|Skipped"); + else if (!strcmp (oktext, "some")) + oktext = _("|audit-log-result|Some"); + else + s = ""; + + /* If we have set a prefix, skip it. */ + if (!s && *oktext == '|' && (s=strchr (oktext+1,'|'))) + oktext = s+1; + } + + if (ctx->use_html) + { + int i; + + es_fputs ("
", ctx->outstream); + if (color) + es_fprintf (ctx->outstream, "*", color); + else + es_fputs ("*", ctx->outstream); + for (i=1; i < ctx->indentlevel; i++) + es_fputs ("  ", ctx->outstream); + es_fputs ("", ctx->outstream); + } + else + es_fprintf (ctx->outstream, "* %*s", (ctx->indentlevel-1)*2, ""); + if (format) + { + va_start (arg_ptr, format) ; + writeout_v (ctx, format, arg_ptr); + va_end (arg_ptr); + } + if (ctx->use_html) + es_fputs ("
", ctx->outstream); + if (format && oktext) + { + if (ctx->use_html) + { + es_fputs ("", ctx->outstream); + if (color) + es_fprintf (ctx->outstream, "", color); + } + else + writeout (ctx, ": "); + writeout (ctx, oktext); + if (color) + es_fputs ("", ctx->outstream); + } + + if (ctx->use_html) + es_fputs ("\n", ctx->outstream); + else + es_fputc ('\n', ctx->outstream); +} + + +/* Write a remark line. */ +static void +writeout_rem (audit_ctx_t ctx, const char *format, ...) +{ + va_list arg_ptr; + + if (ctx->use_html) + { + int i; + + es_fputs ("
*", ctx->outstream); + for (i=1; i < ctx->indentlevel; i++) + es_fputs ("  ", ctx->outstream); + es_fputs ("    (", ctx->outstream); + + } + else + es_fprintf (ctx->outstream, "* %*s (", (ctx->indentlevel-1)*2, ""); + if (format) + { + va_start (arg_ptr, format) ; + writeout_v (ctx, format, arg_ptr); + va_end (arg_ptr); + } + if (ctx->use_html) + es_fputs (")
\n", ctx->outstream); + else + es_fputs (")\n", ctx->outstream); +} + + +/* Return the first log item for EVENT. If STOPEVENT is not 0 never + look behind that event in the log. If STARTITEM is not NULL start + search _after_that item. */ +static log_item_t +find_next_log_item (audit_ctx_t ctx, log_item_t startitem, + audit_event_t event, audit_event_t stopevent) +{ + int idx; + + for (idx=0; idx < ctx->logused; idx++) + { + if (startitem) + { + if (ctx->log + idx == startitem) + startitem = NULL; + } + else if (stopevent && ctx->log[idx].event == stopevent) + break; + else if (ctx->log[idx].event == event) + return ctx->log + idx; + } + return NULL; +} + + +static log_item_t +find_log_item (audit_ctx_t ctx, audit_event_t event, audit_event_t stopevent) +{ + return find_next_log_item (ctx, NULL, event, stopevent); +} + + +/* Helper to a format a serial number. */ +static char * +format_serial (ksba_const_sexp_t sn) +{ + const char *p = (const char *)sn; + unsigned long n; + char *endp; + + if (!p) + return NULL; + if (*p != '(') + BUG (); /* Not a valid S-expression. */ + n = strtoul (p+1, &endp, 10); + p = endp; + if (*p != ':') + BUG (); /* Not a valid S-expression. */ + return bin2hex (p+1, n, NULL); +} + + +/* Return a malloced string with the serial number and the issuer DN + of the certificate. */ +static char * +get_cert_name (ksba_cert_t cert) +{ + char *result; + ksba_sexp_t sn; + char *issuer, *p; + + if (!cert) + return xtrystrdup ("[no certificate]"); + + issuer = ksba_cert_get_issuer (cert, 0); + sn = ksba_cert_get_serial (cert); + if (issuer && sn) + { + p = format_serial (sn); + if (!p) + result = xtrystrdup ("[invalid S/N]"); + else + { + result = xtrymalloc (strlen (p) + strlen (issuer) + 2 + 1); + if (result) + { + *result = '#'; + strcpy (stpcpy (stpcpy (result+1, p),"/"), issuer); + } + xfree (p); + } + } + else + result = xtrystrdup ("[missing S/N or issuer]"); + ksba_free (sn); + xfree (issuer); + return result; +} + +/* Return a malloced string with the serial number and the issuer DN + of the certificate. */ +static char * +get_cert_subject (ksba_cert_t cert, int idx) +{ + char *result; + char *subject; + + if (!cert) + return xtrystrdup ("[no certificate]"); + + subject = ksba_cert_get_subject (cert, idx); + if (subject) + { + result = xtrymalloc (strlen (subject) + 1 + 1); + if (result) + { + *result = '/'; + strcpy (result+1, subject); + } + } + else + result = NULL; + xfree (subject); + return result; +} + + +/* List the given certificiate. If CERT is NULL, this is a NOP. */ +static void +list_cert (audit_ctx_t ctx, ksba_cert_t cert, int with_subj) +{ + char *name; + int idx; + + name = get_cert_name (cert); + writeout_rem (ctx, "%s", name); + xfree (name); + if (with_subj) + { + enter_li (ctx); + for (idx=0; (name = get_cert_subject (cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } +} + + +/* List the chain of certificates from STARTITEM up to STOPEVENT. The + certifcates are written out as comments. */ +static void +list_certchain (audit_ctx_t ctx, log_item_t startitem, audit_event_t stopevent) +{ + log_item_t item; + + startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent); + writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available")); + if (!startitem) + return; + + item = find_next_log_item (ctx, startitem, + AUDIT_CHAIN_ROOTCERT, AUDIT_CHAIN_END); + if (!item) + writeout_rem (ctx, "%s", _("root certificate missing")); + else + { + list_cert (ctx, item->cert, 0); + } + item = startitem; + while ( ((item = find_next_log_item (ctx, item, + AUDIT_CHAIN_CERT, AUDIT_CHAIN_END)))) + { + list_cert (ctx, item->cert, 1); + } +} + + + +/* Process an encrypt operation's log. */ +static void +proc_type_encrypt (audit_ctx_t ctx) +{ + log_item_t loopitem, item; + int recp_no, idx; + char numbuf[35]; + int algo; + char *name; + + item = find_log_item (ctx, AUDIT_ENCRYPTION_DONE, 0); + writeout_li (ctx, item?"Yes":"No", "%s", _("Data encryption succeeded")); + + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + + item = find_log_item (ctx, AUDIT_SESSION_KEY, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Session key created")); + if (item) + { + algo = gcry_cipher_map_name (item->string); + if (algo) + writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo)); + else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2")) + writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2"); + else if (item->string) + writeout_rem (ctx, _("unsupported algorithm: %s"), item->string); + else + writeout_rem (ctx, _("seems to be not encrypted")); + } + + item = find_log_item (ctx, AUDIT_GOT_RECIPIENTS, 0); + snprintf (numbuf, sizeof numbuf, "%d", + item && item->have_intvalue? item->intvalue : 0); + writeout_li (ctx, numbuf, "%s", _("Number of recipients")); + + /* Loop over all recipients. */ + loopitem = NULL; + recp_no = 0; + while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_ENCRYPTED_TO, 0))) + { + recp_no++; + writeout_li (ctx, NULL, _("Recipient %d"), recp_no); + if (loopitem->cert) + { + name = get_cert_name (loopitem->cert); + writeout_rem (ctx, "%s", name); + xfree (name); + enter_li (ctx); + for (idx=0; (name = get_cert_subject (loopitem->cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + } + + leave_li (ctx); +} + + + +/* Process a sign operation's log. */ +static void +proc_type_sign (audit_ctx_t ctx) +{ + log_item_t item, loopitem; + int signer, idx; + const char *result; + ksba_cert_t cert; + char *name; + int lastalgo; + + item = find_log_item (ctx, AUDIT_SIGNING_DONE, 0); + writeout_li (ctx, item?"Yes":"No", "%s", _("Data signing succeeded")); + + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + /* Write remarks with the data hash algorithms. We use a very + simple scheme to avoid some duplicates. */ + loopitem = NULL; + lastalgo = 0; + while ((loopitem = find_next_log_item + (ctx, loopitem, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG))) + { + if (loopitem->intvalue && loopitem->intvalue != lastalgo) + writeout_rem (ctx, _("data hash algorithm: %s"), + gcry_md_algo_name (loopitem->intvalue)); + lastalgo = loopitem->intvalue; + } + + /* Loop over all signer. */ + loopitem = NULL; + signer = 0; + while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0))) + { + signer++; + + item = find_next_log_item (ctx, loopitem, AUDIT_SIGNED_BY, AUDIT_NEW_SIG); + if (!item) + result = "error"; + else if (!item->err) + result = "okay"; + else if (gpg_err_code (item->err) == GPG_ERR_CANCELED) + result = "skipped"; + else + result = gpg_strerror (item->err); + cert = item? item->cert : NULL; + + writeout_li (ctx, result, _("Signer %d"), signer); + item = find_next_log_item (ctx, loopitem, + AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, _("attr hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + + if (cert) + { + name = get_cert_name (cert); + writeout_rem (ctx, "%s", name); + xfree (name); + enter_li (ctx); + for (idx=0; (name = get_cert_subject (cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + } + + leave_li (ctx); +} + + + +/* Process a decrypt operation's log. */ +static void +proc_type_decrypt (audit_ctx_t ctx) +{ + log_item_t loopitem, item; + int algo, recpno; + char *name; + char numbuf[35]; + int idx; + + item = find_log_item (ctx, AUDIT_DECRYPTION_RESULT, 0); + writeout_li (ctx, item && !item->err?"Yes":"No", + "%s", _("Data decryption succeeded")); + + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + + item = find_log_item (ctx, AUDIT_DATA_CIPHER_ALGO, 0); + algo = item? item->intvalue : 0; + writeout_li (ctx, algo?"Yes":"No", "%s", _("Encryption algorithm supported")); + if (algo) + writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo)); + + item = find_log_item (ctx, AUDIT_BAD_DATA_CIPHER_ALGO, 0); + if (item && item->string) + { + algo = gcry_cipher_map_name (item->string); + if (algo) + writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo)); + else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2")) + writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2"); + else if (item->string) + writeout_rem (ctx, _("unsupported algorithm: %s"), item->string); + else + writeout_rem (ctx, _("seems to be not encrypted")); + } + + + for (recpno = 0, item = NULL; + (item = find_next_log_item (ctx, item, AUDIT_NEW_RECP, 0)); recpno++) + ; + snprintf (numbuf, sizeof numbuf, "%d", recpno); + writeout_li (ctx, numbuf, "%s", _("Number of recipients")); + + /* Loop over all recipients. */ + loopitem = NULL; + while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_RECP, 0))) + { + const char *result; + + recpno = loopitem->have_intvalue? loopitem->intvalue : -1; + + item = find_next_log_item (ctx, loopitem, + AUDIT_RECP_RESULT, AUDIT_NEW_RECP); + if (!item) + result = "not-used"; + else if (!item->err) + result = "okay"; + else if (gpg_err_code (item->err) == GPG_ERR_CANCELED) + result = "skipped"; + else + result = gpg_strerror (item->err); + + item = find_next_log_item (ctx, loopitem, + AUDIT_RECP_NAME, AUDIT_NEW_RECP); + writeout_li (ctx, result, _("Recipient %d"), recpno); + if (item && item->string) + writeout_rem (ctx, "%s", item->string); + + /* If we have a certificate write out more infos. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_SAVE_CERT, AUDIT_NEW_RECP); + if (item && item->cert) + { + enter_li (ctx); + for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + } + + leave_li (ctx); +} + + + +/* Process a verification operation's log. */ +static void +proc_type_verify (audit_ctx_t ctx) +{ + log_item_t loopitem, item; + int signo, count, idx, n_good, n_bad; + char numbuf[35]; + const char *result; + + /* If there is at least one signature status we claim that the + verification succeeded. This does not mean that the data has + verified okay. */ + item = find_log_item (ctx, AUDIT_SIG_STATUS, 0); + writeout_li (ctx, item?"Yes":"No", "%s", _("Data verification succeeded")); + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + if (!item) + goto leave; + + item = find_log_item (ctx, AUDIT_NEW_SIG, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Signature available")); + if (!item) + goto leave; + + /* Print info about the used data hashing algorithms. */ + for (idx=0, n_good=n_bad=0; idx < ctx->logused; idx++) + { + item = ctx->log + idx; + if (item->event == AUDIT_NEW_SIG) + break; + else if (item->event == AUDIT_DATA_HASH_ALGO) + n_good++; + else if (item->event == AUDIT_BAD_DATA_HASH_ALGO) + n_bad++; + } + item = find_log_item (ctx, AUDIT_DATA_HASHING, AUDIT_NEW_SIG); + if (!item || item->err || !n_good) + result = "No"; + else if (n_good && !n_bad) + result = "Yes"; + else + result = "Some"; + writeout_li (ctx, result, "%s", _("Parsing data succeeded")); + if (n_good || n_bad) + { + for (idx=0; idx < ctx->logused; idx++) + { + item = ctx->log + idx; + if (item->event == AUDIT_NEW_SIG) + break; + else if (item->event == AUDIT_DATA_HASH_ALGO) + writeout_rem (ctx, _("data hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + else if (item->event == AUDIT_BAD_DATA_HASH_ALGO) + writeout_rem (ctx, _("bad data hash algorithm: %s"), + item->string? item->string:"?"); + } + } + + + /* Loop over all signatures. */ + loopitem = find_log_item (ctx, AUDIT_NEW_SIG, 0); + assert (loopitem); + do + { + signo = loopitem->have_intvalue? loopitem->intvalue : -1; + + item = find_next_log_item (ctx, loopitem, + AUDIT_SIG_STATUS, AUDIT_NEW_SIG); + writeout_li (ctx, item? item->string:"?", _("Signature %d"), signo); + item = find_next_log_item (ctx, loopitem, + AUDIT_SIG_NAME, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, "%s", item->string); + + item = find_next_log_item (ctx, loopitem, + AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, _("data hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + item = find_next_log_item (ctx, loopitem, + AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, _("attr hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + + enter_li (ctx); + + /* List the certificate chain. */ + list_certchain (ctx, loopitem, AUDIT_NEW_SIG); + + /* Show the result of the chain validation. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG); + if (item && item->have_err) + { + writeout_li (ctx, item->err? "No":"Yes", + _("Certificate chain valid")); + if (item->err) + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + } + + /* Show whether the root certificate is fine. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_ROOT_TRUSTED, AUDIT_CHAIN_STATUS); + if (item) + { + writeout_li (ctx, item->err?"No":"Yes", "%s", + _("Root certificate trustworthy")); + if (item->err) + { + add_helptag (ctx, "gpgsm.root-cert-not-trusted"); + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + list_cert (ctx, item->cert, 0); + } + } + + /* Show result of the CRL/OCSP check. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_CRL_CHECK, AUDIT_NEW_SIG); + if (item) + { + const char *ok; + switch (gpg_err_code (item->err)) + { + case 0: ok = "good"; break; + case GPG_ERR_CERT_REVOKED: ok = "bad"; break; + case GPG_ERR_NOT_ENABLED: ok = "disabled"; break; + case GPG_ERR_NO_CRL_KNOWN: + ok = _("no CRL found for certificate"); + break; + case GPG_ERR_CRL_TOO_OLD: + ok = _("the available CRL is too old"); + break; + default: ok = gpg_strerror (item->err); break; + } + + writeout_li (ctx, ok, "%s", _("CRL/OCSP check of certificates")); + if (item->err + && gpg_err_code (item->err) != GPG_ERR_CERT_REVOKED + && gpg_err_code (item->err) != GPG_ERR_NOT_ENABLED) + add_helptag (ctx, "gpgsm.crl-problem"); + } + + leave_li (ctx); + } + while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0))); + + + leave: + /* Always list the certificates stored in the signature. */ + item = NULL; + count = 0; + while ( ((item = find_next_log_item (ctx, item, + AUDIT_SAVE_CERT, AUDIT_NEW_SIG)))) + count++; + snprintf (numbuf, sizeof numbuf, "%d", count); + writeout_li (ctx, numbuf, _("Included certificates")); + item = NULL; + while ( ((item = find_next_log_item (ctx, item, + AUDIT_SAVE_CERT, AUDIT_NEW_SIG)))) + { + char *name = get_cert_name (item->cert); + writeout_rem (ctx, "%s", name); + xfree (name); + enter_li (ctx); + for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + leave_li (ctx); +} + + + + +/* Print the formatted audit result. THIS IS WORK IN PROGRESS. */ +void +audit_print_result (audit_ctx_t ctx, estream_t out, int use_html) +{ + int idx; + size_t n; + log_item_t item; + helptag_t helptag; + const char *s; + int show_raw = 0; + char *orig_codeset; + + if (!ctx) + return; + + orig_codeset = i18n_switchto_utf8 (); + + /* We use an environment variable to include some debug info in the + log. */ + if ((s = getenv ("gnupg_debug_audit"))) + show_raw = 1; + + assert (!ctx->outstream); + ctx->outstream = out; + ctx->use_html = use_html; + ctx->indentlevel = 0; + clear_helptags (ctx); + + if (use_html) + es_fputs ("
\n", ctx->outstream); + + if (!ctx->log || !ctx->logused) + { + writeout_para (ctx, _("No audit log entries.")); + goto leave; + } + + if (show_raw) + { + int maxlen; + + for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++) + { + n = strlen (eventstr_msgstr + eventstr_msgidx[idx]); + if (n > maxlen) + maxlen = n; + } + + if (use_html) + es_fputs ("
\n", out);
+      for (idx=0; idx < ctx->logused; idx++)
+        {
+          es_fprintf (out, "log: %-*s",
+                      maxlen, event2str (ctx->log[idx].event));
+          if (ctx->log[idx].have_intvalue)
+            es_fprintf (out, " i=%d", ctx->log[idx].intvalue);
+          if (ctx->log[idx].string)
+            {
+              es_fputs (" s='", out);
+              writeout (ctx, ctx->log[idx].string);
+              es_fputs ("'", out);
+            }
+          if (ctx->log[idx].cert)
+            es_fprintf (out, " has_cert");
+          if (ctx->log[idx].have_err)
+            {
+              es_fputs (" err='", out);
+              writeout (ctx, gpg_strerror (ctx->log[idx].err));
+              es_fputs ("'", out);
+            }
+          es_fputs ("\n", out);
+        }
+      if (use_html)
+        es_fputs ("
\n", out); + else + es_fputs ("\n", out); + } + + enter_li (ctx); + switch (ctx->type) + { + case AUDIT_TYPE_NONE: + writeout_li (ctx, NULL, _("Unknown operation")); + break; + case AUDIT_TYPE_ENCRYPT: + proc_type_encrypt (ctx); + break; + case AUDIT_TYPE_SIGN: + proc_type_sign (ctx); + break; + case AUDIT_TYPE_DECRYPT: + proc_type_decrypt (ctx); + break; + case AUDIT_TYPE_VERIFY: + proc_type_verify (ctx); + break; + } + item = find_log_item (ctx, AUDIT_AGENT_READY, 0); + if (item && item->have_err) + { + writeout_li (ctx, item->err? "No":"Yes", "%s", _("Gpg-Agent usable")); + if (item->err) + { + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + add_helptag (ctx, "gnupg.agent-problem"); + } + } + item = find_log_item (ctx, AUDIT_DIRMNGR_READY, 0); + if (item && item->have_err) + { + writeout_li (ctx, item->err? "No":"Yes", "%s", _("Dirmngr usable")); + if (item->err) + { + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + add_helptag (ctx, "gnupg.dirmngr-problem"); + } + } + leave_li (ctx); + + + /* Show the help from the collected help tags. */ + if (ctx->helptags) + { + if (use_html) + { + es_fputs ("
\n", ctx->outstream); + if (ctx->helptags->next) + es_fputs ("
    \n", ctx->outstream); + } + else + es_fputs ("\n\n", ctx->outstream); + } + for (helptag = ctx->helptags; helptag; helptag = helptag->next) + { + char *text; + + if (use_html && ctx->helptags->next) + es_fputs ("
  • \n", ctx->outstream); + + text = gnupg_get_help_string (helptag->name, 0); + if (text) + { + writeout_para (ctx, "%s", text); + xfree (text); + } + else + writeout_para (ctx, _("No help available for '%s'."), helptag->name); + if (use_html && ctx->helptags->next) + es_fputs ("
  • \n", ctx->outstream); + if (helptag->next) + es_fputs ("\n", ctx->outstream); + } + if (use_html && ctx->helptags && ctx->helptags->next) + es_fputs ("
\n", ctx->outstream); + + leave: + if (use_html) + es_fputs ("
\n", ctx->outstream); + ctx->outstream = NULL; + ctx->use_html = 0; + clear_helptags (ctx); + i18n_switchback (orig_codeset); +} diff --git a/common/audit.h b/common/audit.h new file mode 100644 index 0000000..4ef2645 --- /dev/null +++ b/common/audit.h @@ -0,0 +1,225 @@ +/* audit.h - Definitions for the audit subsystem + * Copyright (C) 2007 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 3 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, see . + */ + +#ifndef GNUPG_COMMON_AUDIT_H +#define GNUPG_COMMON_AUDIT_H + +#include + +struct audit_ctx_s; +typedef struct audit_ctx_s *audit_ctx_t; + +/* Constants for the audit type. */ +typedef enum + { + AUDIT_TYPE_NONE = 0, /* No type set. */ + AUDIT_TYPE_ENCRYPT, /* Data encryption. */ + AUDIT_TYPE_SIGN, /* Signature creation. */ + AUDIT_TYPE_DECRYPT, /* Data decryption. */ + AUDIT_TYPE_VERIFY /* Signature verification. */ + } +audit_type_t; + +/* The events we support. */ +typedef enum + { + AUDIT_NULL_EVENT = 0, + /* No such event. Its value shall be 0 and no other values shall + be assigned to the other enum symbols. This is required so + that the exaudit.awk script comes up with correct values + without running cc. */ + + AUDIT_SETUP_READY, + /* All preparations done so that the actual processing can start + now. This indicates that all parameters are okay and we can + start to process the actual data. */ + + AUDIT_AGENT_READY, /* err */ + /* Indicates whether the gpg-agent is available. For some + operations the agent is not required and thus no such event + will be logged. */ + + AUDIT_DIRMNGR_READY, /* err */ + /* Indicates whether the Dirmngr is available. For some + operations the Dirmngr is not required and thus no such event + will be logged. */ + + AUDIT_GPG_READY, /* err */ + /* Indicates whether the Gpg engine is available. */ + + AUDIT_GPGSM_READY, /* err */ + /* Indicates whether the Gpgsm engine is available. */ + + AUDIT_G13_READY, /* err */ + /* Indicates whether the G13 engine is available. */ + + AUDIT_GOT_DATA, + /* Data to be processed has been seen. */ + + AUDIT_DETACHED_SIGNATURE, + /* The signature is a detached one. */ + + AUDIT_CERT_ONLY_SIG, + /* A certifciate only signature has been detected. */ + + AUDIT_DATA_HASH_ALGO, /* int */ + /* The hash algo given as argument is used for the data. This + event will be repeated for all hash algorithms used with the + data. */ + + AUDIT_ATTR_HASH_ALGO, /* int */ + /* The hash algo given as argument is used to hash the message + digest and other signed attributes of this signature. */ + + AUDIT_DATA_CIPHER_ALGO, /* int */ + /* The cipher algo given as argument is used for this data. */ + + AUDIT_BAD_DATA_HASH_ALGO, /* string */ + /* The hash algo as specified by the signature can't be used. + STRING is the description of this algorithm which usually is an + OID string. STRING may be NULL. */ + + AUDIT_BAD_DATA_CIPHER_ALGO, /* string */ + /* The symmetric cipher algorithm is not supported. STRING is the + description of this algorithm which usually is an OID string. + STRING may be NULL. */ + + AUDIT_DATA_HASHING, /* ok_err */ + /* Logs the result of the data hashing. */ + + AUDIT_READ_ERROR, /* ok_err */ + /* A generic read error occurred. */ + + AUDIT_WRITE_ERROR, /* ok_err */ + /* A generic write error occurred. */ + + AUDIT_USAGE_ERROR, + /* The program was used in an inappropriate way; For example by + passing a data object while the signature does not expect one + or vice versa. */ + + AUDIT_SAVE_CERT, /* cert, ok_err */ + /* Save the certificate received in a message. */ + + AUDIT_NEW_SIG, /* int */ + /* Start the verification of a new signature for the last data + object. The argument is the signature number as used + internally by the program. */ + + AUDIT_SIG_NAME, /* string */ + /* The name of a signer. This is the name or other identification + data as known from the signature and not the name from the + certificate used for verification. An example for STRING when + using CMS is: "#1234/CN=Prostetnic Vogon Jeltz". */ + + AUDIT_SIG_STATUS, /* string */ + /* The signature status of the current signer. This is the last + audit information for one signature. STRING gives the status: + + "error" - there was a problem checking this or any signature. + "unsupported" - the signature type is not supported. + "no-cert" - The certificate of the signer was not found (the + S/N+issuer of the signer is already in the log). + "bad" - bad signature + "good" - good signature + */ + + AUDIT_NEW_RECP, /* int */ + /* A new recipient has been seen during decryption. The argument + is the recipient number as used internally by the program. */ + + AUDIT_RECP_NAME, /* string */ + /* The name of a recipient. This is the name or other identification + data as known from the decryption and not the name from the + certificate used for decryption. An example for STRING when + using CMS is: "#1234/CN=Prostetnic Vogon Jeltz". */ + + AUDIT_RECP_RESULT, /* ok_err */ + /* The status of the session key decryption. This is only written + for recipients tried. */ + + AUDIT_DECRYPTION_RESULT, /* ok_err */ + /* The status of the entire decryption. The decryption was + successful if the error code is 0. */ + + AUDIT_VALIDATE_CHAIN, + /* Start the validation of a certificate chain. */ + + AUDIT_CHAIN_BEGIN, + AUDIT_CHAIN_CERT, /* cert */ + AUDIT_CHAIN_ROOTCERT,/* cert */ + AUDIT_CHAIN_END, + /* These 4 events are used to log the certificates making up a + certificate chain. ROOTCERT is used for the trustanchor and + CERT for all other certificates. */ + + AUDIT_CHAIN_STATUS, /* err */ + /* Tells the final status of the chain validation. */ + + AUDIT_ROOT_TRUSTED, /* cert, err */ + /* Tells whether the root certificate is trusted. This event is + emitted during chain validation. */ + + AUDIT_CRL_CHECK, /* err */ + /* Tells the status of a CRL or OCSP check. */ + + AUDIT_GOT_RECIPIENTS, /* int */ + /* Records the number of recipients to be used for encryption. + This includes the recipients set by --encrypt-to but records 0 + if no real recipient has been given. */ + + AUDIT_SESSION_KEY, /* string */ + /* Mark the creation or availibility of the session key. The + parameter is the algorithm ID. */ + + AUDIT_ENCRYPTED_TO, /* cert, err */ + /* Records the certificate used for encryption and whether the + session key could be encrypted to it (err==0). */ + + AUDIT_ENCRYPTION_DONE, + /* Encryption succeeded. */ + + AUDIT_SIGNED_BY, /* cert, err */ + /* Records the certificate used for signed and whether the signure + could be created (if err==0). */ + + AUDIT_SIGNING_DONE, + /* Signing succeeded. */ + + + AUDIT_LAST_EVENT /* Marker for parsing this list. */ + } +audit_event_t; + + +audit_ctx_t audit_new (void); +void audit_release (audit_ctx_t ctx); +void audit_set_type (audit_ctx_t ctx, audit_type_t type); +void audit_log (audit_ctx_t ctx, audit_event_t event); +void audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err); +void audit_log_i (audit_ctx_t ctx, audit_event_t event, int value); +void audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value); +void audit_log_cert (audit_ctx_t ctx, audit_event_t event, + ksba_cert_t cert, gpg_error_t err); + +void audit_print_result (audit_ctx_t ctx, estream_t stream, int use_html); + + + +#endif /*GNUPG_COMMON_AUDIT_H*/ diff --git a/common/b64dec.c b/common/b64dec.c new file mode 100644 index 0000000..74cf933 --- /dev/null +++ b/common/b64dec.c @@ -0,0 +1,253 @@ +/* b64dec.c - Simple Base64 decoder. + * Copyright (C) 2008, 2011 Free Software Foundation, Inc. + * Copyright (C) 2008, 2011, 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file 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 file 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 Lesser General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "i18n.h" +#include "util.h" + + +/* The reverse base-64 list used for base-64 decoding. */ +static unsigned char const asctobin[128] = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff + }; + +enum decoder_states + { + s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin, + s_b64_0, s_b64_1, s_b64_2, s_b64_3, + s_waitendtitle, s_waitend + }; + + + +/* Initialize the context for the base64 decoder. If TITLE is NULL a + plain base64 decoding is done. If it is the empty string the + decoder will skip everything until a "-----BEGIN " line has been + seen, decoding ends at a "----END " line. */ +gpg_error_t +b64dec_start (struct b64state *state, const char *title) +{ + memset (state, 0, sizeof *state); + if (title) + { + state->title = xtrystrdup (title); + if (!state->title) + state->lasterr = gpg_error_from_syserror (); + else + state->idx = s_init; + } + else + state->idx = s_b64_0; + return state->lasterr; +} + + +/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the + new length of the buffer at R_NBYTES. */ +gpg_error_t +b64dec_proc (struct b64state *state, void *buffer, size_t length, + size_t *r_nbytes) +{ + enum decoder_states ds = state->idx; + unsigned char val = state->radbuf[0]; + int pos = state->quad_count; + char *d, *s; + + if (state->lasterr) + return state->lasterr; + + if (state->stop_seen) + { + *r_nbytes = 0; + state->lasterr = gpg_error (GPG_ERR_EOF); + xfree (state->title); + state->title = NULL; + return state->lasterr; + } + + for (s=d=buffer; length && !state->stop_seen; length--, s++) + { + again: + switch (ds) + { + case s_idle: + if (*s == '\n') + { + ds = s_lfseen; + pos = 0; + } + break; + case s_init: + ds = s_lfseen; + case s_lfseen: + if (*s != "-----BEGIN "[pos]) + { + ds = s_idle; + goto again; + } + else if (pos == 10) + { + pos = 0; + ds = s_beginseen; + } + else + pos++; + break; + case s_beginseen: + if (*s != "PGP "[pos]) + ds = s_begin; /* Not a PGP armor. */ + else if (pos == 3) + ds = s_waitheader; + else + pos++; + break; + case s_waitheader: + if (*s == '\n') + ds = s_waitblank; + break; + case s_waitblank: + if (*s == '\n') + ds = s_b64_0; /* blank line found. */ + else if (*s == ' ' || *s == '\r' || *s == '\t') + ; /* Ignore spaces. */ + else + { + /* Armor header line. Note that we don't care that our + * FSM accepts a header prefixed with spaces. */ + ds = s_waitheader; /* Wait for next header. */ + } + break; + case s_begin: + if (*s == '\n') + ds = s_b64_0; + break; + case s_b64_0: + case s_b64_1: + case s_b64_2: + case s_b64_3: + { + int c; + + if (*s == '-' && state->title) + { + /* Not a valid Base64 character: assume end + header. */ + ds = s_waitend; + } + else if (*s == '=') + { + /* Pad character: stop */ + if (ds == s_b64_1) + *d++ = val; + ds = state->title? s_waitendtitle : s_waitend; + } + else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t') + ; /* Skip white spaces. */ + else if ( (*s & 0x80) + || (c = asctobin[*(unsigned char *)s]) == 255) + { + /* Skip invalid encodings. */ + state->invalid_encoding = 1; + } + else if (ds == s_b64_0) + { + val = c << 2; + ds = s_b64_1; + } + else if (ds == s_b64_1) + { + val |= (c>>4)&3; + *d++ = val; + val = (c<<4)&0xf0; + ds = s_b64_2; + } + else if (ds == s_b64_2) + { + val |= (c>>2)&15; + *d++ = val; + val = (c<<6)&0xc0; + ds = s_b64_3; + } + else + { + val |= c&0x3f; + *d++ = val; + ds = s_b64_0; + } + } + break; + case s_waitendtitle: + if (*s == '-') + ds = s_waitend; + break; + case s_waitend: + if ( *s == '\n') + state->stop_seen = 1; + break; + default: + BUG(); + } + } + + + state->idx = ds; + state->radbuf[0] = val; + state->quad_count = pos; + *r_nbytes = (d -(char*) buffer); + return 0; +} + + +/* This function needs to be called before releasing the decoder + state. It may return an error code in case an encoding error has + been found during decoding. */ +gpg_error_t +b64dec_finish (struct b64state *state) +{ + xfree (state->title); + state->title = NULL; + + if (state->lasterr) + return state->lasterr; + + return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0; +} diff --git a/common/b64enc.c b/common/b64enc.c new file mode 100644 index 0000000..d633048 --- /dev/null +++ b/common/b64enc.c @@ -0,0 +1,422 @@ +/* b64enc.c - Simple Base64 encoder. + * Copyright (C) 2001, 2003, 2004, 2008, 2010, + * 2011 Free Software Foundation, Inc. + * Copyright (C) 2001, 2003, 2004, 2008, 2010, + * 2011 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file 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 file 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 Lesser General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "i18n.h" +#include "util.h" + +#define B64ENC_DID_HEADER 1 +#define B64ENC_DID_TRAILER 2 +#define B64ENC_NO_LINEFEEDS 16 +#define B64ENC_USE_PGPCRC 32 + +/* The base-64 character list */ +static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/* Stuff required to create the OpenPGP CRC. This crc_table has been + created using this code: + + #include + #include + + #define CRCPOLY 0x864CFB + + int + main (void) + { + int i, j; + uint32_t t; + uint32_t crc_table[256]; + + crc_table[0] = 0; + for (i=j=0; j < 128; j++ ) + { + t = crc_table[j]; + if ( (t & 0x00800000) ) + { + t <<= 1; + crc_table[i++] = t ^ CRCPOLY; + crc_table[i++] = t; + } + else + { + t <<= 1; + crc_table[i++] = t; + crc_table[i++] = t ^ CRCPOLY; + } + } + + puts ("static const u32 crc_table[256] = {"); + for (i=j=0; i < 256; i++) + { + printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]); + if (i != 255) + { + putchar (','); + if ( ++j > 5) + { + j = 0; + putchar ('\n'); + } + } + } + puts ("\n};"); + return 0; + } +*/ +#define CRCINIT 0xB704CE +static const u32 crc_table[256] = { + 0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a, + 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf, + 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272, + 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e, + 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa, + 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f, + 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b, + 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7, + 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a, + 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af, + 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29, + 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5, + 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1, + 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad, + 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099, + 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375, + 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821, + 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4, + 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049, + 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5, + 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791, + 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52, + 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66, + 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a, + 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337, + 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2, + 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6, + 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a, + 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e, + 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132, + 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506, + 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea, + 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c, + 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9, + 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604, + 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8, + 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc, + 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69, + 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d, + 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1, + 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c, + 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9, + 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538 +}; + + +static gpg_error_t +enc_start (struct b64state *state, FILE *fp, estream_t stream, + const char *title) +{ + memset (state, 0, sizeof *state); + state->fp = fp; + state->stream = stream; + state->lasterr = 0; + if (title && !*title) + state->flags |= B64ENC_NO_LINEFEEDS; + else if (title) + { + if (!strncmp (title, "PGP ", 4)) + { + state->flags |= B64ENC_USE_PGPCRC; + state->crc = CRCINIT; + } + state->title = xtrystrdup (title); + if (!state->title) + state->lasterr = gpg_error_from_syserror (); + } + return state->lasterr; +} + + +/* Prepare for base-64 writing to the stream FP. If TITLE is not NULL + and not an empty string, this string will be used as the title for + the armor lines, with TITLE being an empty string, we don't write + the header lines and furthermore even don't write any linefeeds. + If TITLE starts with "PGP " the OpenPGP CRC checksum will be + written as well. With TITLE being NULL, we merely don't write + header but make sure that lines are not too long. Note, that we + don't write any output unless at least one byte get written using + b64enc_write. */ +gpg_error_t +b64enc_start (struct b64state *state, FILE *fp, const char *title) +{ + return enc_start (state, fp, NULL, title); +} + +/* Same as b64enc_start but takes an estream. */ +gpg_error_t +b64enc_start_es (struct b64state *state, estream_t fp, const char *title) +{ + return enc_start (state, NULL, fp, title); +} + + +static int +my_fputs (const char *string, struct b64state *state) +{ + if (state->stream) + return es_fputs (string, state->stream); + else + return fputs (string, state->fp); +} + + +/* Write NBYTES from BUFFER to the Base 64 stream identified by + STATE. With BUFFER and NBYTES being 0, merely do a fflush on the + stream. */ +gpg_error_t +b64enc_write (struct b64state *state, const void *buffer, size_t nbytes) +{ + unsigned char radbuf[4]; + int idx, quad_count; + const unsigned char *p; + + if (state->lasterr) + return state->lasterr; + + if (!nbytes) + { + if (buffer) + if (state->stream? es_fflush (state->stream) : fflush (state->fp)) + goto write_error; + return 0; + } + + if (!(state->flags & B64ENC_DID_HEADER)) + { + if (state->title) + { + if ( my_fputs ("-----BEGIN ", state) == EOF + || my_fputs (state->title, state) == EOF + || my_fputs ("-----\n", state) == EOF) + goto write_error; + if ( (state->flags & B64ENC_USE_PGPCRC) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + + state->flags |= B64ENC_DID_HEADER; + } + + idx = state->idx; + quad_count = state->quad_count; + assert (idx < 4); + memcpy (radbuf, state->radbuf, idx); + + if ( (state->flags & B64ENC_USE_PGPCRC) ) + { + size_t n; + u32 crc = state->crc; + + for (p=buffer, n=nbytes; n; p++, n-- ) + crc = ((u32)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p]; + state->crc = (crc & 0x00ffffff); + } + + for (p=buffer; nbytes; p++, nbytes--) + { + radbuf[idx++] = *p; + if (idx > 2) + { + char tmp[4]; + + tmp[0] = bintoasc[(*radbuf >> 2) & 077]; + tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; + tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + tmp[3] = bintoasc[radbuf[2]&077]; + if (state->stream) + { + for (idx=0; idx < 4; idx++) + es_putc (tmp[idx], state->stream); + idx = 0; + if (es_ferror (state->stream)) + goto write_error; + } + else + { + for (idx=0; idx < 4; idx++) + putc (tmp[idx], state->fp); + idx = 0; + if (ferror (state->fp)) + goto write_error; + } + if (++quad_count >= (64/4)) + { + quad_count = 0; + if (!(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + } + } + memcpy (state->radbuf, radbuf, idx); + state->idx = idx; + state->quad_count = quad_count; + return 0; + + write_error: + state->lasterr = gpg_error_from_syserror (); + if (state->title) + { + xfree (state->title); + state->title = NULL; + } + return state->lasterr; +} + + +gpg_error_t +b64enc_finish (struct b64state *state) +{ + gpg_error_t err = 0; + unsigned char radbuf[4]; + int idx, quad_count; + char tmp[4]; + + if (state->lasterr) + return state->lasterr; + + if (!(state->flags & B64ENC_DID_HEADER)) + goto cleanup; + + /* Flush the base64 encoding */ + idx = state->idx; + quad_count = state->quad_count; + assert (idx < 4); + memcpy (radbuf, state->radbuf, idx); + + if (idx) + { + tmp[0] = bintoasc[(*radbuf>>2)&077]; + if (idx == 1) + { + tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077]; + tmp[2] = '='; + tmp[3] = '='; + } + else + { + tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; + tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077]; + tmp[3] = '='; + } + if (state->stream) + { + for (idx=0; idx < 4; idx++) + es_putc (tmp[idx], state->stream); + if (es_ferror (state->stream)) + goto write_error; + } + else + { + for (idx=0; idx < 4; idx++) + putc (tmp[idx], state->fp); + if (ferror (state->fp)) + goto write_error; + } + + if (++quad_count >= (64/4)) + { + quad_count = 0; + if (!(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + } + + /* Finish the last line and write the trailer. */ + if (quad_count + && !(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + + if ( (state->flags & B64ENC_USE_PGPCRC) ) + { + /* Write the CRC. */ + my_fputs ("=", state); + radbuf[0] = state->crc >>16; + radbuf[1] = state->crc >> 8; + radbuf[2] = state->crc; + tmp[0] = bintoasc[(*radbuf>>2)&077]; + tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; + tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + tmp[3] = bintoasc[radbuf[2]&077]; + if (state->stream) + { + for (idx=0; idx < 4; idx++) + es_putc (tmp[idx], state->stream); + if (es_ferror (state->stream)) + goto write_error; + } + else + { + for (idx=0; idx < 4; idx++) + putc (tmp[idx], state->fp); + if (ferror (state->fp)) + goto write_error; + } + if (!(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + + if (state->title) + { + if ( my_fputs ("-----END ", state) == EOF + || my_fputs (state->title, state) == EOF + || my_fputs ("-----\n", state) == EOF) + goto write_error; + } + + goto cleanup; + + write_error: + err = gpg_error_from_syserror (); + + cleanup: + if (state->title) + { + xfree (state->title); + state->title = NULL; + } + state->fp = NULL; + state->stream = NULL; + state->lasterr = err; + return err; +} diff --git a/common/call-gpg.c b/common/call-gpg.c new file mode 100644 index 0000000..d42325a --- /dev/null +++ b/common/call-gpg.c @@ -0,0 +1,753 @@ +/* call-gpg.c - Communication with the GPG + * Copyright (C) 2009 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "call-gpg.h" +#include "exechelp.h" +#include "i18n.h" +#include "logging.h" +#include "membuf.h" +#include "strlist.h" +#include "util.h" + + +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static GPGRT_INLINE gpg_error_t +my_error_from_errno (int e) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_errno (e)); +} + + +/* Fire up a new GPG. Handle the server's initial greeting. Returns + 0 on success and stores the assuan context at R_CTX. */ +static gpg_error_t +start_gpg (ctrl_t ctrl, const char *gpg_program, strlist_t gpg_arguments, + int input_fd, int output_fd, assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + const char *pgmname; + const char **argv; + assuan_fd_t no_close_list[5]; + int i; + char line[ASSUAN_LINELENGTH]; + + (void)ctrl; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (err)); + return err; + } + + /* The first time we are used, intialize the gpg_program variable. */ + if ( !gpg_program || !*gpg_program ) + gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG); + + /* Compute argv[0]. */ + if ( !(pgmname = strrchr (gpg_program, '/'))) + pgmname = gpg_program; + else + pgmname++; + + if (fflush (NULL)) + { + err = my_error_from_syserror (); + log_error ("error flushing pending output: %s\n", gpg_strerror (err)); + return err; + } + + argv = xtrycalloc (strlist_length (gpg_arguments) + 3, sizeof *argv); + if (argv == NULL) + { + err = my_error_from_syserror (); + return err; + } + i = 0; + argv[i++] = pgmname; + argv[i++] = "--server"; + for (; gpg_arguments; gpg_arguments = gpg_arguments->next) + argv[i++] = gpg_arguments->d; + argv[i++] = NULL; + + i = 0; + if (log_get_fd () != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); + no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr)); + if (input_fd != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (input_fd); + if (output_fd != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (output_fd); + no_close_list[i] = ASSUAN_INVALID_FD; + + /* Connect to GPG and perform initial handshaking. */ + err = assuan_pipe_connect (ctx, gpg_program, argv, no_close_list, + NULL, NULL, 0); + if (err) + { + assuan_release (ctx); + log_error ("can't connect to GPG: %s\n", gpg_strerror (err)); + return gpg_error (GPG_ERR_NO_ENGINE); + } + + if (input_fd != -1) + { + snprintf (line, sizeof line, "INPUT FD=%d", input_fd); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + assuan_release (ctx); + log_error ("error sending INPUT command: %s\n", gpg_strerror (err)); + return err; + } + } + + if (output_fd != -1) + { + snprintf (line, sizeof line, "OUTPUT FD=%d", output_fd); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + assuan_release (ctx); + log_error ("error sending OUTPUT command: %s\n", gpg_strerror (err)); + return err; + } + } + + *r_ctx = ctx; + return 0; +} + + +/* Release the assuan context created by start_gpg. */ +static void +release_gpg (assuan_context_t ctx) +{ + assuan_release (ctx); +} + + + +/* The data passed to the writer_thread. */ +struct writer_thread_parms +{ + int fd; + const void *data; + size_t datalen; + estream_t stream; + gpg_error_t *err_addr; +}; + + +/* The thread started by start_writer. */ +static void * +writer_thread_main (void *arg) +{ + gpg_error_t err = 0; + struct writer_thread_parms *parm = arg; + char _buffer[4096]; + char *buffer; + size_t length; + + if (parm->stream) + { + buffer = _buffer; + err = es_read (parm->stream, buffer, sizeof _buffer, &length); + if (err) + { + log_error ("reading stream failed: %s\n", gpg_strerror (err)); + goto leave; + } + } + else + { + buffer = (char *) parm->data; + length = parm->datalen; + } + + while (length) + { + ssize_t nwritten; + + nwritten = npth_write (parm->fd, buffer, length < 4096? length:4096); + if (nwritten < 0) + { + if (errno == EINTR) + continue; + err = my_error_from_syserror (); + break; /* Write error. */ + } + length -= nwritten; + + if (parm->stream) + { + if (length == 0) + { + err = es_read (parm->stream, buffer, sizeof _buffer, &length); + if (err) + { + log_error ("reading stream failed: %s\n", + gpg_strerror (err)); + break; + } + if (length == 0) + /* We're done. */ + break; + } + } + else + buffer += nwritten; + } + + leave: + *parm->err_addr = err; + if (close (parm->fd)) + log_error ("closing writer fd %d failed: %s\n", parm->fd, strerror (errno)); + xfree (parm); + return NULL; +} + + +/* Fire up a thread to send (DATA,DATALEN) to the file descriptor FD. + On success the thread receives the ownership over FD. The thread + ID is stored at R_TID. WRITER_ERR is the address of an gpg_error_t + variable to receive a possible write error after the thread has + finished. */ +static gpg_error_t +start_writer (int fd, const void *data, size_t datalen, estream_t stream, + npth_t *r_thread, gpg_error_t *err_addr) +{ + gpg_error_t err; + struct writer_thread_parms *parm; + npth_attr_t tattr; + npth_t thread; + int ret; + + memset (r_thread, '\0', sizeof (*r_thread)); + *err_addr = 0; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return my_error_from_syserror (); + parm->fd = fd; + parm->data = data; + parm->datalen = datalen; + parm->stream = stream; + parm->err_addr = err_addr; + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + + ret = npth_create (&thread, &tattr, writer_thread_main, parm); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("error spawning writer thread: %s\n", gpg_strerror (err)); + } + else + { + npth_setname_np (thread, "fd-writer"); + err = 0; + *r_thread = thread; + } + npth_attr_destroy (&tattr); + + return err; +} + + + +/* The data passed to the reader_thread. */ +struct reader_thread_parms +{ + int fd; + membuf_t *mb; + estream_t stream; + gpg_error_t *err_addr; +}; + + +/* The thread started by start_reader. */ +static void * +reader_thread_main (void *arg) +{ + gpg_error_t err = 0; + struct reader_thread_parms *parm = arg; + char buffer[4096]; + int nread; + + while ( (nread = npth_read (parm->fd, buffer, sizeof buffer)) ) + { + if (nread < 0) + { + if (errno == EINTR) + continue; + err = my_error_from_syserror (); + break; /* Read error. */ + } + + if (parm->stream) + { + const char *p = buffer; + size_t nwritten; + while (nread) + { + err = es_write (parm->stream, p, nread, &nwritten); + if (err) + { + log_error ("writing stream failed: %s\n", + gpg_strerror (err)); + goto leave; + } + nread -= nwritten; + p += nwritten; + } + } + else + put_membuf (parm->mb, buffer, nread); + } + + leave: + *parm->err_addr = err; + if (close (parm->fd)) + log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno)); + xfree (parm); + return NULL; +} + + +/* Fire up a thread to receive data from the file descriptor FD. On + success the thread receives the ownership over FD. The thread ID + is stored at R_TID. After the thread has finished an error from + the thread will be stored at ERR_ADDR. */ +static gpg_error_t +start_reader (int fd, membuf_t *mb, estream_t stream, + npth_t *r_thread, gpg_error_t *err_addr) +{ + gpg_error_t err; + struct reader_thread_parms *parm; + npth_attr_t tattr; + npth_t thread; + int ret; + + memset (r_thread, '\0', sizeof (*r_thread)); + *err_addr = 0; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return my_error_from_syserror (); + parm->fd = fd; + parm->mb = mb; + parm->stream = stream; + parm->err_addr = err_addr; + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + + ret = npth_create (&thread, &tattr, reader_thread_main, parm); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("error spawning reader thread: %s\n", gpg_strerror (err)); + } + else + { + npth_setname_np (thread, "fd-reader"); + err = 0; + *r_thread = thread; + } + npth_attr_destroy (&tattr); + + return err; +} + + + + +/* Call GPG to encrypt a block of data. + + + */ +static gpg_error_t +_gpg_encrypt (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *plain, size_t plainlen, + estream_t plain_stream, + strlist_t keys, + membuf_t *reader_mb, + estream_t cipher_stream) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + int outbound_fds[2] = { -1, -1 }; + int inbound_fds[2] = { -1, -1 }; + npth_t writer_thread = (npth_t)0; + npth_t reader_thread = (npth_t)0; + gpg_error_t writer_err, reader_err; + char line[ASSUAN_LINELENGTH]; + strlist_t sl; + int ret; + + /* Make sure that either the stream interface xor the buffer + interface is used. */ + assert ((plain == NULL) != (plain_stream == NULL)); + assert ((reader_mb == NULL) != (cipher_stream == NULL)); + + /* Create two pipes. */ + err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0); + if (!err) + err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0); + if (err) + { + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Start GPG and send the INPUT and OUTPUT commands. */ + err = start_gpg (ctrl, gpg_program, gpg_arguments, + outbound_fds[0], inbound_fds[1], &ctx); + if (err) + goto leave; + close (outbound_fds[0]); outbound_fds[0] = -1; + close (inbound_fds[1]); inbound_fds[1] = -1; + + /* Start a writer thread to feed the INPUT command of the server. */ + err = start_writer (outbound_fds[1], plain, plainlen, plain_stream, + &writer_thread, &writer_err); + if (err) + return err; + outbound_fds[1] = -1; /* The thread owns the FD now. */ + + /* Start a reader thread to eat from the OUTPUT command of the + server. */ + err = start_reader (inbound_fds[0], reader_mb, cipher_stream, + &reader_thread, &reader_err); + if (err) + return err; + outbound_fds[0] = -1; /* The thread owns the FD now. */ + + /* Run the encryption. */ + for (sl = keys; sl; sl = sl->next) + { + snprintf (line, sizeof line, "RECIPIENT -- %s", sl->d); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's RECIPIENT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + } + + err = assuan_transact (ctx, "ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's ENCRYPT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + + /* Wait for reader and return the data. */ + ret = npth_join (reader_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + /* FIXME: Not really valid, as npth_t is an opaque type. */ + memset (&reader_thread, '\0', sizeof (reader_thread)); + if (reader_err) + { + err = reader_err; + log_error ("read error in reader thread: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Wait for the writer to catch a writer error. */ + ret = npth_join (writer_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + memset (&writer_thread, '\0', sizeof (writer_thread)); + if (writer_err) + { + err = writer_err; + log_error ("write error in writer thread: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + /* FIXME: Not valid, as npth_t is an opaque type. */ + if (reader_thread) + npth_detach (reader_thread); + if (writer_thread) + npth_detach (writer_thread); + if (outbound_fds[0] != -1) + close (outbound_fds[0]); + if (outbound_fds[1] != -1) + close (outbound_fds[1]); + if (inbound_fds[0] != -1) + close (inbound_fds[0]); + if (inbound_fds[1] != -1) + close (inbound_fds[1]); + release_gpg (ctx); + return err; +} + +gpg_error_t +gpg_encrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *plain, size_t plainlen, + strlist_t keys, + void **r_ciph, size_t *r_ciphlen) +{ + gpg_error_t err; + membuf_t reader_mb; + + *r_ciph = NULL; + *r_ciphlen = 0; + + /* Init the memory buffer to receive the encrypted stuff. */ + init_membuf (&reader_mb, 4096); + + err = _gpg_encrypt (ctrl, gpg_program, gpg_arguments, + plain, plainlen, NULL, + keys, + &reader_mb, NULL); + + if (! err) + { + /* Return the data. */ + *r_ciph = get_membuf (&reader_mb, r_ciphlen); + if (!*r_ciph) + { + err = my_error_from_syserror (); + log_error ("error while storing the data in the reader thread: %s\n", + gpg_strerror (err)); + } + } + + xfree (get_membuf (&reader_mb, NULL)); + return err; +} + +gpg_error_t +gpg_encrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t plain_stream, + strlist_t keys, + estream_t cipher_stream) +{ + return _gpg_encrypt (ctrl, gpg_program, gpg_arguments, + NULL, 0, plain_stream, + keys, + NULL, cipher_stream); +} + +/* Call GPG to decrypt a block of data. + + + */ +static gpg_error_t +_gpg_decrypt (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *ciph, size_t ciphlen, + estream_t cipher_stream, + membuf_t *reader_mb, + estream_t plain_stream) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + int outbound_fds[2] = { -1, -1 }; + int inbound_fds[2] = { -1, -1 }; + npth_t writer_thread = (npth_t)0; + npth_t reader_thread = (npth_t)0; + gpg_error_t writer_err, reader_err; + int ret; + + /* Make sure that either the stream interface xor the buffer + interface is used. */ + assert ((ciph == NULL) != (cipher_stream == NULL)); + assert ((reader_mb == NULL) != (plain_stream == NULL)); + + /* Create two pipes. */ + err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0); + if (!err) + err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0); + if (err) + { + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Start GPG and send the INPUT and OUTPUT commands. */ + err = start_gpg (ctrl, gpg_program, gpg_arguments, + outbound_fds[0], inbound_fds[1], &ctx); + if (err) + goto leave; + close (outbound_fds[0]); outbound_fds[0] = -1; + close (inbound_fds[1]); inbound_fds[1] = -1; + + /* Start a writer thread to feed the INPUT command of the server. */ + err = start_writer (outbound_fds[1], ciph, ciphlen, cipher_stream, + &writer_thread, &writer_err); + if (err) + return err; + outbound_fds[1] = -1; /* The thread owns the FD now. */ + + /* Start a reader thread to eat from the OUTPUT command of the + server. */ + err = start_reader (inbound_fds[0], reader_mb, plain_stream, + &reader_thread, &reader_err); + if (err) + return err; + outbound_fds[0] = -1; /* The thread owns the FD now. */ + + /* Run the decryption. */ + err = assuan_transact (ctx, "DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's DECRYPT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + + /* Wait for reader and return the data. */ + ret = npth_join (reader_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + memset (&reader_thread, '\0', sizeof (reader_thread)); + if (reader_err) + { + err = reader_err; + log_error ("read error in reader thread: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Wait for the writer to catch a writer error. */ + ret = npth_join (writer_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + memset (&writer_thread, '\0', sizeof (writer_thread)); + if (writer_err) + { + err = writer_err; + log_error ("write error in writer thread: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + if (reader_thread) + npth_detach (reader_thread); + if (writer_thread) + npth_detach (writer_thread); + if (outbound_fds[0] != -1) + close (outbound_fds[0]); + if (outbound_fds[1] != -1) + close (outbound_fds[1]); + if (inbound_fds[0] != -1) + close (inbound_fds[0]); + if (inbound_fds[1] != -1) + close (inbound_fds[1]); + release_gpg (ctx); + return err; +} + +gpg_error_t +gpg_decrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *ciph, size_t ciphlen, + void **r_plain, size_t *r_plainlen) +{ + gpg_error_t err; + membuf_t reader_mb; + + *r_plain = NULL; + *r_plainlen = 0; + + /* Init the memory buffer to receive the encrypted stuff. */ + init_membuf_secure (&reader_mb, 1024); + + err = _gpg_decrypt (ctrl, gpg_program, gpg_arguments, + ciph, ciphlen, NULL, + &reader_mb, NULL); + + if (! err) + { + /* Return the data. */ + *r_plain = get_membuf (&reader_mb, r_plainlen); + if (!*r_plain) + { + err = my_error_from_syserror (); + log_error ("error while storing the data in the reader thread: %s\n", + gpg_strerror (err)); + } + } + + xfree (get_membuf (&reader_mb, NULL)); + return err; +} + +gpg_error_t +gpg_decrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t cipher_stream, + estream_t plain_stream) +{ + return _gpg_decrypt (ctrl, gpg_program, gpg_arguments, + NULL, 0, cipher_stream, + NULL, plain_stream); +} diff --git a/common/call-gpg.h b/common/call-gpg.h new file mode 100644 index 0000000..fd7d2e6 --- /dev/null +++ b/common/call-gpg.h @@ -0,0 +1,54 @@ +/* call-gpg.h - Defs for the communication with GPG + * Copyright (C) 2009 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 3 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, see . + */ + +#ifndef GNUPG_COMMON_CALL_GPG_H +#define GNUPG_COMMON_CALL_GPG_H + +#include + +#include "fwddecl.h" +#include "strlist.h" + +gpg_error_t gpg_encrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *plain, size_t plainlen, + strlist_t keys, + void **r_ciph, size_t *r_ciphlen); + +gpg_error_t gpg_encrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t plain_stream, + strlist_t keys, + estream_t cipher_stream); + +gpg_error_t gpg_decrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *ciph, size_t ciphlen, + void **r_plain, size_t *r_plainlen); + +gpg_error_t gpg_decrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t cipher_stream, + estream_t plain_stream); + +#endif /*GNUPG_COMMON_CALL_GPG_H*/ diff --git a/common/ccparray.c b/common/ccparray.c new file mode 100644 index 0000000..ff3eb40 --- /dev/null +++ b/common/ccparray.c @@ -0,0 +1,148 @@ +/* ccparray.c - A simple dynamic array for character pointer. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "ccparray.h" + + +/* A simple implementation of a dynamic array of const char pointers. + * The example code: + * + * ccparray_t ccp; + * const char **argv; + * int i; + * + * ccparray_init (&ccp, 0); + * ccparray_put (&ccp, "First arg"); + * ccparray_put (&ccp, "Second arg"); + * ccparray_put (&ccp, NULL); + * ccparray_put (&ccp, "Fourth arg"); + * argv = ccparray_get (&ccp, NULL); + * if (!argv) + * die ("error building array: %s\n", strerror (errno)); + * for (i=0; argv[i]; i++) + * printf ("[%d] = '%s'\n", i, argv[i]); + * xfree (argv); + * + * will result in this output: + * + * [0] = 'First arg' + * [1] = 'Second arg' + * + * Note that allocation errors are detected but only returned with the + * final ccparray_get(); this helps not to clutter the code with out + * of core checks. + */ + +void +ccparray_init (ccparray_t *cpa, unsigned int initialsize) +{ + if (!initialsize) + cpa->size = 16; + else if (initialsize < (1<<16)) + cpa->size = initialsize; + else + cpa->size = (1<<16); + + cpa->count = 0; + cpa->out_of_core = 0; + cpa->array = xtrycalloc (cpa->size, sizeof *cpa->array); + if (!cpa->array) + cpa->out_of_core = errno; +} + + +void +ccparray_put (ccparray_t *cpa, const char *value) +{ + if (cpa->out_of_core) + return; + + if (cpa->count + 1 >= cpa->size) + { + const char **newarray; + size_t n, newsize; + + if (cpa->size < 8) + newsize = 16; + else if (cpa->size < 4096) + newsize = 2 * cpa->size; + else if (cpa->size < (1<<16)) + newsize = cpa->size + 2048; + else + { + cpa->out_of_core = ENOMEM; + return; + } + + newarray = xtrycalloc (newsize, sizeof *newarray); + if (!newarray) + { + cpa->out_of_core = errno ? errno : ENOMEM; + return; + } + for (n=0; n < cpa->size; n++) + newarray[n] = cpa->array[n]; + xfree (cpa->array); + cpa->array = newarray; + cpa->size = newsize; + + } + cpa->array[cpa->count++] = value; +} + + +const char ** +ccparray_get (ccparray_t *cpa, size_t *r_count) +{ + const char **result; + + if (cpa->out_of_core) + { + if (cpa->array) + { + xfree (cpa->array); + cpa->array = NULL; + } + gpg_err_set_errno (cpa->out_of_core); + return NULL; + } + + result= cpa->array; + if (r_count) + *r_count = cpa->count; + cpa->array = NULL; + cpa->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ + return result; +} diff --git a/common/ccparray.h b/common/ccparray.h new file mode 100644 index 0000000..1ecf95b --- /dev/null +++ b/common/ccparray.h @@ -0,0 +1,51 @@ +/* ccparray.c - A simple dynamic array for character pointer. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_CCPARRAY_H +#define GNUPG_COMMON_CCPARRAY_H + +/* The definition of the structure is private, we only need it here, + * so it can be allocated on the stack. */ +struct _ccparray_private_s +{ + unsigned int count; + unsigned int size; + int out_of_core; + const char **array; +}; + +typedef struct _ccparray_private_s ccparray_t; + + +void ccparray_init (ccparray_t *cpa, unsigned int initialsize); +void ccparray_put (ccparray_t *cpa, const char *value); +const char **ccparray_get (ccparray_t *cpa, size_t *r_nelems); + + +#endif /*GNUPG_COMMON_CCPARRAY_H*/ diff --git a/common/common-defs.h b/common/common-defs.h new file mode 100644 index 0000000..b1928e6 --- /dev/null +++ b/common/common-defs.h @@ -0,0 +1,54 @@ +/* common-defs.h - Private declarations for common/ + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_COMMON_DEFS_H +#define GNUPG_COMMON_COMMON_DEFS_H + + +/* Dummy replacement for getenv. */ +#ifndef HAVE_GETENV +#define getenv(a) (NULL) +#endif + +#ifdef HAVE_W32CE_SYSTEM +#define getpid() GetCurrentProcessId () +#endif + + +/*-- ttyio.c --*/ +void tty_private_set_rl_hooks (void (*init_stream) (FILE *), + void (*set_completer) (rl_completion_func_t*), + void (*inhibit_completion) (int), + void (*cleanup_after_signal) (void), + char *(*readline_fun) (const char*), + void (*add_history_fun) (const char*)); + + + +#endif /*GNUPG_COMMON_COMMON_DEFS_H*/ diff --git a/common/convert.c b/common/convert.c new file mode 100644 index 0000000..6d03adc --- /dev/null +++ b/common/convert.c @@ -0,0 +1,266 @@ +/* convert.c - Hex conversion functions. + * Copyright (C) 2006, 2008 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" + + +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + + +/* Convert STRING consisting of hex characters into its binary + representation and store that at BUFFER. BUFFER needs to be of + LENGTH bytes. The function checks that the STRING will convert + exactly to LENGTH bytes. The string is delimited by either end of + string or a white space character. The function returns -1 on + error or the length of the parsed string. */ +int +hex2bin (const char *string, void *buffer, size_t length) +{ + int i; + const char *s = string; + + for (i=0; i < length; ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return -1; /* Invalid hex digits. */ + ((unsigned char*)buffer)[i++] = xtoi_2 (s); + s += 2; + } + if (*s && (!isascii (*s) || !isspace (*s)) ) + return -1; /* Not followed by Nul or white space. */ + if (i != length) + return -1; /* Not of expected length. */ + if (*s) + s++; /* Skip the delimiter. */ + return s - string; +} + + +/* Convert STRING consisting of hex characters into its binary representation + and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The + function check that the STRING will convert exactly to LENGTH + bytes. Colons between the hex digits are allowed, if one colon + has been given a colon is expected very 2 characters. The string + is delimited by either end of string or a white space character. + The function returns -1 on error or the length of the parsed + string. */ +int +hexcolon2bin (const char *string, void *buffer, size_t length) +{ + int i; + const char *s = string; + int need_colon = 0; + + for (i=0; i < length; ) + { + if (i==1 && *s == ':') /* Skip colons between hex digits. */ + { + need_colon = 1; + s++; + } + else if (need_colon && *s == ':') + s++; + else if (need_colon) + return -1; /* Colon expected. */ + if (!hexdigitp (s) || !hexdigitp (s+1)) + return -1; /* Invalid hex digits. */ + ((unsigned char*)buffer)[i++] = xtoi_2 (s); + s += 2; + } + if (*s == ':') + return -1; /* Trailing colons are not allowed. */ + if (*s && (!isascii (*s) || !isspace (*s)) ) + return -1; /* Not followed by Nul or white space. */ + if (i != length) + return -1; /* Not of expected length. */ + if (*s) + s++; /* Skip the delimiter. */ + return s - string; +} + + + +static char * +do_bin2hex (const void *buffer, size_t length, char *stringbuf, int with_colon) +{ + const unsigned char *s; + char *p; + + if (!stringbuf) + { + /* Not really correct for with_colon but we don't care about the + one wasted byte. */ + size_t n = with_colon? 3:2; + size_t nbytes = n * length + 1; + if (length && (nbytes-1) / n != length) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + stringbuf = xtrymalloc (nbytes); + if (!stringbuf) + return NULL; + } + + for (s = buffer, p = stringbuf; length; length--, s++) + { + if (with_colon && s != buffer) + *p++ = ':'; + *p++ = tohex ((*s>>4)&15); + *p++ = tohex (*s&15); + } + *p = 0; + + return stringbuf; +} + + +/* Convert LENGTH bytes of data in BUFFER into hex encoding and store + that at the provided STRINGBUF. STRINGBUF must be allocated of at + least (2*LENGTH+1) bytes or be NULL so that the function mallocs an + appropriate buffer. Returns STRINGBUF or NULL on error (which may + only occur if STRINGBUF has been NULL and the internal malloc + failed). */ +char * +bin2hex (const void *buffer, size_t length, char *stringbuf) +{ + return do_bin2hex (buffer, length, stringbuf, 0); +} + +/* Convert LENGTH bytes of data in BUFFER into hex encoding and store + that at the provided STRINGBUF. STRINGBUF must be allocated of at + least (3*LENGTH+1) bytes or be NULL so that the function mallocs an + appropriate buffer. Returns STRINGBUF or NULL on error (which may + only occur if STRINGBUF has been NULL and the internal malloc + failed). */ +char * +bin2hexcolon (const void *buffer, size_t length, char *stringbuf) +{ + return do_bin2hex (buffer, length, stringbuf, 1); +} + + + +/* Convert HEXSTRING consisting of hex characters into string and + store that at BUFFER. HEXSTRING is either delimited by end of + string or a white space character. The function makes sure that + the resulting string in BUFFER is terminated by a Nul byte. Note + that the returned string may include embedded Nul bytes; the extra + Nul byte at the end is used to make sure tha the result can always + be used as a C-string. + + BUFSIZE is the available length of BUFFER; if the converted result + plus a possible required extra Nul character does not fit into this + buffer, the function returns NULL and won't change the existing + content of BUFFER. In-place conversion is possible as long as + BUFFER points to HEXSTRING. + + If BUFFER is NULL and BUFSIZE is 0 the function scans HEXSTRING but + does not store anything. This may be used to find the end of + HEXSTRING. + + On success the function returns a pointer to the next character + after HEXSTRING (which is either end-of-string or a the next white + space). If BUFLEN is not NULL the number of valid vytes in BUFFER + is stored there (an extra Nul byte is not counted); this will even + be done if BUFFER has been passed as NULL. */ +const char * +hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen) +{ + const char *s = hexstring; + int idx, count; + int need_nul = 0; + + if (buflen) + *buflen = 0; + + for (s=hexstring, count=0; hexdigitp (s) && hexdigitp (s+1); s += 2, count++) + ; + if (*s && (!isascii (*s) || !isspace (*s)) ) + { + gpg_err_set_errno (EINVAL); + return NULL; /* Not followed by Nul or white space. */ + } + /* We need to append a nul character. However we don't want that if + the hexstring already ends with "00". */ + need_nul = ((s == hexstring) || !(s[-2] == '0' && s[-1] == '0')); + if (need_nul) + count++; + + if (buffer) + { + if (count > bufsize) + { + gpg_err_set_errno (EINVAL); + return NULL; /* Too long. */ + } + + for (s=hexstring, idx=0; hexdigitp (s) && hexdigitp (s+1); s += 2) + ((unsigned char*)buffer)[idx++] = xtoi_2 (s); + if (need_nul) + buffer[idx] = 0; + } + + if (buflen) + *buflen = count - need_nul; + return s; +} + + +/* Same as hex2str but this function allocated a new string. Returns + NULL on error. If R_COUNT is not NULL, the number of scanned bytes + will be stored there. ERRNO is set on error. */ +char * +hex2str_alloc (const char *hexstring, size_t *r_count) +{ + const char *tail; + size_t nbytes; + char *result; + + tail = hex2str (hexstring, NULL, 0, &nbytes); + if (!tail) + { + if (r_count) + *r_count = 0; + return NULL; + } + if (r_count) + *r_count = tail - hexstring; + result = xtrymalloc (nbytes+1); + if (!result) + return NULL; + if (!hex2str (hexstring, result, nbytes+1, NULL)) + BUG (); + return result; +} diff --git a/common/dotlock.c b/common/dotlock.c new file mode 100644 index 0000000..7ebd523 --- /dev/null +++ b/common/dotlock.c @@ -0,0 +1,1379 @@ +/* dotlock.c - dotfile locking + * Copyright (C) 1998, 2000, 2001, 2003, 2004, + * 2005, 2006, 2008, 2010, 2011 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU Lesser General License or the GNU + * General Public License. If you wish to allow use of your version of + * this file only under the terms of the GNU Lesser General License or + * the GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Overview: + ========= + + This module implements advisory file locking in a portable way. + Due to the problems with POSIX fcntl locking a separate lock file + is used. It would be possible to use fcntl locking on this lock + file and thus avoid the weird auto unlock bug of POSIX while still + having an unproved better performance of fcntl locking. However + there are still problems left, thus we resort to use a hardlink + which has the well defined property that a link call will fail if + the target file already exists. + + Given that hardlinks are also available on NTFS file systems since + Windows XP; it will be possible to enhance this module to use + hardlinks even on Windows and thus allow Windows and Posix clients + to use locking on the same directory. This is not yet implemented; + instead we use a lockfile on Windows along with W32 style file + locking. + + On FAT file systems hardlinks are not supported. Thus this method + does not work. Our solution is to use a O_EXCL locking instead. + Querying the type of the file system is not easy to do in a + portable way (e.g. Linux has a statfs, BSDs have a the same call + but using different structures and constants). What we do instead + is to check at runtime whether link(2) works for a specific lock + file. + + + How to use: + =========== + + At program initialization time, the module should be explicitly + initialized: + + dotlock_create (NULL, 0); + + This installs an atexit handler and may also initialize mutex etc. + It is optional for non-threaded applications. Only the first call + has an effect. This needs to be done before any extra threads are + started. + + To create a lock file (which prepares it but does not take the + lock) you do: + + dotlock_t h + + h = dotlock_create (fname, 0); + if (!h) + error ("error creating lock file: %s\n", strerror (errno)); + + It is important to handle the error. For example on a read-only + file system a lock can't be created (but is usually not needed). + FNAME is the file you want to lock; the actual lockfile is that + name with the suffix ".lock" appended. On success a handle to be + used with the other functions is returned or NULL on error. Note + that the handle shall only be used by one thread at a time. This + function creates a unique file temporary file (".#lk*") in the same + directory as FNAME and returns a handle for further operations. + The module keeps track of theses unique files so that they will be + unlinked using the atexit handler. If you don't need the lock file + anymore, you may also explicitly remove it with a call to: + + dotlock_destroy (h); + + To actually lock the file, you use: + + if (dotlock_take (h, -1)) + error ("error taking lock: %s\n", strerror (errno)); + + This function will wait until the lock is acquired. If an + unexpected error occurs if will return non-zero and set ERRNO. If + you pass (0) instead of (-1) the function does not wait in case the + file is already locked but returns -1 and sets ERRNO to EACCES. + Any other positive value for the second parameter is considered a + timeout valuie in milliseconds. + + To release the lock you call: + + if (dotlock_release (h)) + error ("error releasing lock: %s\n", strerror (errno)); + + or, if the lock file is not anymore needed, you may just call + dotlock_destroy. However dotlock_release does some extra checks + before releasing the lock and prints diagnostics to help detecting + bugs. + + If you want to explicitly destroy all lock files you may call + + dotlock_remove_lockfiles (); + + which is the core of the installed atexit handler. In case your + application wants to disable locking completely it may call + + disable_locking () + + before any locks are created. + + There are two convenience functions to store an integer (e.g. a + file descriptor) value with the handle: + + void dotlock_set_fd (dotlock_t h, int fd); + int dotlock_get_fd (dotlock_t h); + + If nothing has been stored dotlock_get_fd returns -1. + + + + How to build: + ============= + + This module was originally developed for GnuPG but later changed to + allow its use without any GnuPG dependency. If you want to use it + with you application you may simply use it and it should figure out + most things automagically. + + You may use the common config.h file to pass macros, but take care + to pass -DHAVE_CONFIG_H to the compiler. Macros used by this + module are: + + DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use. + + DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions. + + DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the + string to which this macro evaluates. + + GNUPG_MAJOR_VERSION - Defined when used by GnuPG. + + HAVE_DOSISH_SYSTEM - Defined for Windows etc. Will be + automatically defined if a the target is + Windows. + + HAVE_POSIX_SYSTEM - Internally defined to !HAVE_DOSISH_SYSTEM. + + HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h + is not used defaults to defined. + + DIRSEP_C - Separation character for file name parts. + Usually not redefined. + + EXTSEP_S - Separation string for file name suffixes. + Usually not redefined. + + HAVE_W32CE_SYSTEM - Currently only used by GnuPG. + + Note that there is a test program t-dotlock which has compile + instructions at its end. At least for SMBFS and CIFS it is + important that 64 bit versions of stat are used; most programming + environments do this these days, just in case you want to compile + it on the command line, remember to pass -D_FILE_OFFSET_BITS=64 + + + Bugs: + ===== + + On Windows this module is not yet thread-safe. + + + Miscellaneous notes: + ==================== + + On hardlinks: + - Hardlinks are supported under Windows with NTFS since XP/Server2003. + - In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks. + - NFS supports hard links. But there are solvable problems. + - FAT does not support links + + On the file locking API: + - CIFS on Linux 2.6.33 supports several locking methods. + SMBFS seems not to support locking. No closer checks done. + - NFS supports Posix locks. flock is emulated in the server. + However there are a couple of problems; see below. + - FAT does not support locks. + - An advantage of fcntl locking is that R/W locks can be + implemented which is not easy with a straight lock file. + + On O_EXCL: + - Does not work reliable on NFS + - Should work on CIFS and SMBFS but how can we delete lockfiles? + + On NFS problems: + - Locks vanish if the server crashes and reboots. + - Client crashes keep the lock in the server until the client + re-connects. + - Communication problems may return unreliable error codes. The + MUA Postfix's workaround is to compare the link count after + seeing an error for link. However that gives a race. If using a + unique file to link to a lockfile and using stat to check the + link count instead of looking at the error return of link(2) is + the best solution. + - O_EXCL seems to have a race and may re-create a file anyway. + +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Some quick replacements for stuff we usually expect to be defined + in config.h. Define HAVE_POSIX_SYSTEM for better readability. */ +#if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32) +# define HAVE_DOSISH_SYSTEM 1 +#endif +#if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM) +# define HAVE_POSIX_SYSTEM 1 +#endif + +/* With no config.h assume that we have sitgnal.h. */ +#if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM) +# define HAVE_SIGNAL_H 1 +#endif + +/* Standard headers. */ +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_DOSISH_SYSTEM +# define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */ +# include +#else +# include +# include +# include +#endif +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#ifdef DOTLOCK_USE_PTHREAD +# include +#endif + +#ifdef DOTLOCK_GLIB_LOGGING +# include +#endif + +#ifdef GNUPG_MAJOR_VERSION +# include "util.h" +# include "common-defs.h" +# include "stringhelp.h" /* For stpcpy and w32_strerror. */ +#endif +#ifdef HAVE_W32CE_SYSTEM +# include "utf8conv.h" /* WindowsCE requires filename conversion. */ +#endif + +#include "dotlock.h" + + +/* Define constants for file name construction. */ +#if !defined(DIRSEP_C) && !defined(EXTSEP_S) +# ifdef HAVE_DOSISH_SYSTEM +# define DIRSEP_C '\\' +# define EXTSEP_S "." +#else +# define DIRSEP_C '/' +# define EXTSEP_S "." +# endif +#endif + +/* In GnuPG we use wrappers around the malloc fucntions. If they are + not defined we assume that this code is used outside of GnuPG and + fall back to the regular malloc functions. */ +#ifndef xtrymalloc +# define xtrymalloc(a) malloc ((a)) +# define xtrycalloc(a,b) calloc ((a), (b)) +# define xfree(a) free ((a)) +#endif + +/* Wrapper to set ERRNO (required for W32CE). */ +#ifdef GPG_ERROR_VERSION +# define my_set_errno(e) gpg_err_set_errno ((e)) +#else +# define my_set_errno(e) do { errno = (e); } while (0) +#endif + +/* Gettext macro replacement. */ +#ifndef _ +# define _(a) (a) +#endif + +#ifdef GNUPG_MAJOR_VERSION +# define my_info_0(a) log_info ((a)) +# define my_info_1(a,b) log_info ((a), (b)) +# define my_info_2(a,b,c) log_info ((a), (b), (c)) +# define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d)) +# define my_error_0(a) log_error ((a)) +# define my_error_1(a,b) log_error ((a), (b)) +# define my_error_2(a,b,c) log_error ((a), (b), (c)) +# define my_debug_1(a,b) log_debug ((a), (b)) +# define my_fatal_0(a) log_fatal ((a)) +#elif defined (DOTLOCK_GLIB_LOGGING) +# define my_info_0(a) g_message ((a)) +# define my_info_1(a,b) g_message ((a), (b)) +# define my_info_2(a,b,c) g_message ((a), (b), (c)) +# define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d)) +# define my_error_0(a) g_warning ((a)) +# define my_error_1(a,b) g_warning ((a), (b)) +# define my_error_2(a,b,c) g_warning ((a), (b), (c)) +# define my_debug_1(a,b) g_debug ((a), (b)) +# define my_fatal_0(a) g_error ((a)) +#else +# define my_info_0(a) fprintf (stderr, (a)) +# define my_info_1(a,b) fprintf (stderr, (a), (b)) +# define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c)) +# define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d)) +# define my_error_0(a) fprintf (stderr, (a)) +# define my_error_1(a,b) fprintf (stderr, (a), (b)) +# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c)) +# define my_debug_1(a,b) fprintf (stderr, (a), (b)) +# define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \ + abort (); } while (0) +#endif + + + + + +/* The object describing a lock. */ +struct dotlock_handle +{ + struct dotlock_handle *next; + char *lockname; /* Name of the actual lockfile. */ + unsigned int locked:1; /* Lock status. */ + unsigned int disable:1; /* If true, locking is disabled. */ + unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */ + + int extra_fd; /* A place for the caller to store an FD. */ + +#ifdef HAVE_DOSISH_SYSTEM + HANDLE lockhd; /* The W32 handle of the lock file. */ +#else /*!HAVE_DOSISH_SYSTEM */ + char *tname; /* Name of the lockfile template. */ + size_t nodename_off; /* Offset in TNAME of the nodename part. */ + size_t nodename_len; /* Length of the nodename part. */ +#endif /*!HAVE_DOSISH_SYSTEM */ +}; + + +/* A list of of all lock handles. The volatile attribute might help + if used in an atexit handler. Note that [UN]LOCK_all_lockfiles + must not change ERRNO. */ +static volatile dotlock_t all_lockfiles; +#ifdef DOTLOCK_USE_PTHREAD +static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER; +# define LOCK_all_lockfiles() do { \ + if (pthread_mutex_lock (&all_lockfiles_mutex)) \ + my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \ + } while (0) +# define UNLOCK_all_lockfiles() do { \ + if (pthread_mutex_unlock (&all_lockfiles_mutex)) \ + my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \ + } while (0) +#else /*!DOTLOCK_USE_PTHREAD*/ +# define LOCK_all_lockfiles() do { } while (0) +# define UNLOCK_all_lockfiles() do { } while (0) +#endif /*!DOTLOCK_USE_PTHREAD*/ + +/* If this has the value true all locking is disabled. */ +static int never_lock; + + + + +#ifdef HAVE_DOSISH_SYSTEM +static int +map_w32_to_errno (DWORD w32_err) +{ + switch (w32_err) + { + case 0: + return 0; + + case ERROR_FILE_NOT_FOUND: + return ENOENT; + + case ERROR_PATH_NOT_FOUND: + return ENOENT; + + case ERROR_ACCESS_DENIED: + return EPERM; + + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_BLOCK: + return EINVAL; + + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + + case ERROR_NO_DATA: + case ERROR_BROKEN_PIPE: + return EPIPE; + + default: + return EIO; + } +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Entirely disable all locking. This function should be called + before any locking is done. It may be called right at startup of + the process as it only sets a global value. */ +void +dotlock_disable (void) +{ + never_lock = 1; +} + + +#ifdef HAVE_POSIX_SYSTEM +static int +maybe_deadlock (dotlock_t h) +{ + dotlock_t r; + int res = 0; + + LOCK_all_lockfiles (); + for (r=all_lockfiles; r; r = r->next) + { + if ( r != h && r->locked ) + { + res = 1; + break; + } + } + UNLOCK_all_lockfiles (); + return res; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +/* Read the lock file and return the pid, returns -1 on error. True + will be stored in the integer at address SAME_NODE if the lock file + has been created on the same node. */ +#ifdef HAVE_POSIX_SYSTEM +static int +read_lockfile (dotlock_t h, int *same_node ) +{ + char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node + names are usually shorter. */ + int fd; + int pid = -1; + char *buffer, *p; + size_t expected_len; + int res, nread; + + *same_node = 0; + expected_len = 10 + 1 + h->nodename_len + 1; + if ( expected_len >= sizeof buffer_space) + { + buffer = xtrymalloc (expected_len); + if (!buffer) + return -1; + } + else + buffer = buffer_space; + + if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) + { + int e = errno; + my_info_2 ("error opening lockfile '%s': %s\n", + h->lockname, strerror(errno) ); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (e); /* Need to return ERRNO here. */ + return -1; + } + + p = buffer; + nread = 0; + do + { + res = read (fd, p, expected_len - nread); + if (res == -1 && errno == EINTR) + continue; + if (res < 0) + { + int e = errno; + my_info_1 ("error reading lockfile '%s'\n", h->lockname ); + close (fd); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (e); + return -1; + } + p += res; + nread += res; + } + while (res && nread != expected_len); + close(fd); + + if (nread < 11) + { + my_info_1 ("invalid size of lockfile '%s'\n", h->lockname); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (EINVAL); + return -1; + } + + if (buffer[10] != '\n' + || (buffer[10] = 0, pid = atoi (buffer)) == -1 + || !pid ) + { + my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (EINVAL); + return -1; + } + + if (nread == expected_len + && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) + && buffer[11+h->nodename_len] == '\n') + *same_node = 1; + + if (buffer != buffer_space) + xfree (buffer); + return pid; +} +#endif /*HAVE_POSIX_SYSTEM */ + + +/* Check whether the file system which stores TNAME supports + hardlinks. Instead of using the non-portable statsfs call which + differs between various Unix versions, we do a runtime test. + Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown + (test error). */ +#ifdef HAVE_POSIX_SYSTEM +static int +use_hardlinks_p (const char *tname) +{ + char *lname; + struct stat sb; + unsigned int nlink; + int res; + + if (stat (tname, &sb)) + return -1; + nlink = (unsigned int)sb.st_nlink; + + lname = xtrymalloc (strlen (tname) + 1 + 1); + if (!lname) + return -1; + strcpy (lname, tname); + strcat (lname, "x"); + + /* We ignore the return value of link() because it is unreliable. */ + (void) link (tname, lname); + + if (stat (tname, &sb)) + res = -1; /* Ooops. */ + else if (sb.st_nlink == nlink + 1) + res = 0; /* Yeah, hardlinks are supported. */ + else + res = 1; /* No hardlink support. */ + + unlink (lname); + xfree (lname); + return res; +} +#endif /*HAVE_POSIX_SYSTEM */ + + + +#ifdef HAVE_POSIX_SYSTEM +/* Locking core for Unix. It used a temporary file and the link + system call to make locking an atomic operation. */ +static dotlock_t +dotlock_create_unix (dotlock_t h, const char *file_to_lock) +{ + int fd = -1; + char pidstr[16]; + const char *nodename; + const char *dirpart; + int dirpartlen; + struct utsname utsbuf; + size_t tnamelen; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); + + /* Create a temporary file. */ + if ( uname ( &utsbuf ) ) + nodename = "unknown"; + else + nodename = utsbuf.nodename; + + if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) ) + { + dirpart = EXTSEP_S; + dirpartlen = 1; + } + else + { + dirpartlen = dirpart - file_to_lock; + dirpart = file_to_lock; + } + + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + + tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1; + h->tname = xtrymalloc (tnamelen + 1); + if (!h->tname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + xfree (h); + return NULL; + } + h->nodename_len = strlen (nodename); + + snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); + h->nodename_off = strlen (h->tname); + snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, + "%s.%d", nodename, (int)getpid ()); + + do + { + my_set_errno (0); + fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } + while (fd == -1 && errno == EINTR); + + if ( fd == -1 ) + { + int saveerrno = errno; + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("failed to create temporary file '%s': %s\n"), + h->tname, strerror (errno)); + xfree (h->tname); + xfree (h); + my_set_errno (saveerrno); + return NULL; + } + if ( write (fd, pidstr, 11 ) != 11 ) + goto write_failed; + if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) ) + goto write_failed; + if ( write (fd, "\n", 1 ) != 1 ) + goto write_failed; + if ( close (fd) ) + { + if ( errno == EINTR ) + fd = -1; + goto write_failed; + } + fd = -1; + + /* Check whether we support hard links. */ + switch (use_hardlinks_p (h->tname)) + { + case 0: /* Yes. */ + break; + case 1: /* No. */ + unlink (h->tname); + h->use_o_excl = 1; + break; + default: + { + int saveerrno = errno; + my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n" + , h->tname, strerror (saveerrno)); + my_set_errno (saveerrno); + } + goto write_failed; + } + + h->lockname = xtrymalloc (strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + int saveerrno = errno; + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + unlink (h->tname); + xfree (h->tname); + xfree (h); + my_set_errno (saveerrno); + return NULL; + } + strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); + UNLOCK_all_lockfiles (); + if (h->use_o_excl) + my_debug_1 ("locking for '%s' done via O_EXCL\n", h->lockname); + + return h; + + write_failed: + { + int saveerrno = errno; + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno)); + if ( fd != -1 ) + close (fd); + unlink (h->tname); + xfree (h->tname); + xfree (h); + my_set_errno (saveerrno); + } + return NULL; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Locking core for Windows. This version does not need a temporary + file but uses the plain lock file along with record locking. We + create this file here so that we later only need to do the file + locking. For error reporting it is useful to keep the name of the + file in the handle. */ +static dotlock_t +dotlock_create_w32 (dotlock_t h, const char *file_to_lock) +{ + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + + h->lockname = xtrymalloc ( strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + xfree (h); + return NULL; + } + strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock"); + + /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE + along with FILE_SHARE_DELETE but that does not work due to a race + condition: Despite the OPEN_ALWAYS flag CreateFile may return an + error and we can't reliable create/open the lock file unless we + would wait here until it works - however there are other valid + reasons why a lock file can't be created and thus the process + would not stop as expected but spin until Windows crashes. Our + solution is to keep the lock file open; that does not harm. */ + { +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wname = utf8_to_wchar (h->lockname); + + if (wname) + h->lockhd = CreateFile (wname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); + else + h->lockhd = INVALID_HANDLE_VALUE; + xfree (wname); +#else + h->lockhd = CreateFile (h->lockname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); +#endif + } + if (h->lockhd == INVALID_HANDLE_VALUE) + { + int saveerrno = map_w32_to_errno (GetLastError ()); + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1)); + xfree (h->lockname); + xfree (h); + my_set_errno (saveerrno); + return NULL; + } + return h; +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Create a lockfile for a file name FILE_TO_LOCK and returns an + object of type dotlock_t which may be used later to actually acquire + the lock. A cleanup routine gets installed to cleanup left over + locks or other files used internally by the lock mechanism. + + Calling this function with NULL does only install the atexit + handler and may thus be used to assure that the cleanup is called + after all other atexit handlers. + + This function creates a lock file in the same directory as + FILE_TO_LOCK using that name and a suffix of ".lock". Note that on + POSIX systems a temporary file ".#lk..pid[.threadid] is + used. + + FLAGS must be 0. + + The function returns an new handle which needs to be released using + destroy_dotlock but gets also released at the termination of the + process. On error NULL is returned. + */ + +dotlock_t +dotlock_create (const char *file_to_lock, unsigned int flags) +{ + static int initialized; + dotlock_t h; + + if ( !initialized ) + { + atexit (dotlock_remove_lockfiles); + initialized = 1; + } + + if ( !file_to_lock ) + return NULL; /* Only initialization was requested. */ + + if (flags) + { + my_set_errno (EINVAL); + return NULL; + } + + h = xtrycalloc (1, sizeof *h); + if (!h) + return NULL; + h->extra_fd = -1; + + if (never_lock) + { + h->disable = 1; + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + UNLOCK_all_lockfiles (); + return h; + } + +#ifdef HAVE_DOSISH_SYSTEM + return dotlock_create_w32 (h, file_to_lock); +#else /*!HAVE_DOSISH_SYSTEM */ + return dotlock_create_unix (h, file_to_lock); +#endif /*!HAVE_DOSISH_SYSTEM*/ +} + + + +/* Convenience function to store a file descriptor (or any any other + integer value) in the context of handle H. */ +void +dotlock_set_fd (dotlock_t h, int fd) +{ + h->extra_fd = fd; +} + +/* Convenience function to retrieve a file descriptor (or any any other + integer value) stored in the context of handle H. */ +int +dotlock_get_fd (dotlock_t h) +{ + return h->extra_fd; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of destroy_dotlock. */ +static void +dotlock_destroy_unix (dotlock_t h) +{ + if (h->locked && h->lockname) + unlink (h->lockname); + if (h->tname && !h->use_o_excl) + unlink (h->tname); + xfree (h->tname); +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of destroy_dotlock. */ +static void +dotlock_destroy_w32 (dotlock_t h) +{ + if (h->locked) + { + OVERLAPPED ovl; + + memset (&ovl, 0, sizeof ovl); + UnlockFileEx (h->lockhd, 0, 1, 0, &ovl); + } + CloseHandle (h->lockhd); +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Destroy the lock handle H and release the lock. */ +void +dotlock_destroy (dotlock_t h) +{ + dotlock_t hprev, htmp; + + if ( !h ) + return; + + /* First remove the handle from our global list of all locks. */ + LOCK_all_lockfiles (); + for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next) + if (htmp == h) + { + if (hprev) + hprev->next = htmp->next; + else + all_lockfiles = htmp->next; + h->next = NULL; + break; + } + UNLOCK_all_lockfiles (); + + /* Then destroy the lock. */ + if (!h->disable) + { +#ifdef HAVE_DOSISH_SYSTEM + dotlock_destroy_w32 (h); +#else /* !HAVE_DOSISH_SYSTEM */ + dotlock_destroy_unix (h); +#endif /* HAVE_DOSISH_SYSTEM */ + xfree (h->lockname); + } + xfree(h); +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of make_dotlock. Returns 0 on success and -1 on + error. */ +static int +dotlock_take_unix (dotlock_t h, long timeout) +{ + int wtime = 0; + int sumtime = 0; + int pid; + int lastpid = -1; + int ownerchanged; + const char *maybe_dead=""; + int same_node; + int saveerrno; + + again: + if (h->use_o_excl) + { + /* No hardlink support - use open(O_EXCL). */ + int fd; + + do + { + my_set_errno (0); + fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } + while (fd == -1 && errno == EINTR); + + if (fd == -1 && errno == EEXIST) + ; /* Lock held by another process. */ + else if (fd == -1) + { + saveerrno = errno; + my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n", + h->lockname, strerror (saveerrno)); + my_set_errno (saveerrno); + return -1; + } + else + { + char pidstr[16]; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid()); + if (write (fd, pidstr, 11 ) == 11 + && write (fd, h->tname + h->nodename_off,h->nodename_len) + == h->nodename_len + && write (fd, "\n", 1) == 1 + && !close (fd)) + { + h->locked = 1; + return 0; + } + /* Write error. */ + saveerrno = errno; + my_error_2 ("lock not made: writing to '%s' failed: %s\n", + h->lockname, strerror (errno)); + close (fd); + unlink (h->lockname); + my_set_errno (saveerrno); + return -1; + } + } + else /* Standard method: Use hardlinks. */ + { + struct stat sb; + + /* We ignore the return value of link() because it is unreliable. */ + (void) link (h->tname, h->lockname); + + if (stat (h->tname, &sb)) + { + saveerrno = errno; + my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n", + strerror (errno)); + /* In theory this might be a severe error: It is possible + that link succeeded but stat failed due to changed + permissions. We can't do anything about it, though. */ + my_set_errno (saveerrno); + return -1; + } + + if (sb.st_nlink == 2) + { + h->locked = 1; + return 0; /* Okay. */ + } + } + + /* Check for stale lock files. */ + if ( (pid = read_lockfile (h, &same_node)) == -1 ) + { + if ( errno != ENOENT ) + { + saveerrno = errno; + my_info_0 ("cannot read lockfile\n"); + my_set_errno (saveerrno); + return -1; + } + my_info_0 ("lockfile disappeared\n"); + goto again; + } + else if ( pid == getpid() && same_node ) + { + my_info_0 ("Oops: lock already held by us\n"); + h->locked = 1; + return 0; /* okay */ + } + else if ( same_node && kill (pid, 0) && errno == ESRCH ) + { + /* Note: It is unlikley that we get a race here unless a pid is + reused too fast or a new process with the same pid as the one + of the stale file tries to lock right at the same time as we. */ + my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); + unlink (h->lockname); + goto again; + } + + if (lastpid == -1) + lastpid = pid; + ownerchanged = (pid != lastpid); + + if (timeout) + { + struct timeval tv; + + /* Wait until lock has been released. We use increasing retry + intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s + but reset it if the lock owner meanwhile changed. */ + if (!wtime || ownerchanged) + wtime = 50; + else if (wtime < 800) + wtime *= 2; + else if (wtime == 800) + wtime = 2000; + else if (wtime < 8000) + wtime *= 2; + + if (timeout > 0) + { + if (wtime > timeout) + wtime = timeout; + timeout -= wtime; + } + + sumtime += wtime; + if (sumtime >= 1500) + { + sumtime = 0; + my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), + pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); + } + + + tv.tv_sec = wtime / 1000; + tv.tv_usec = (wtime % 1000) * 1000; + select (0, NULL, NULL, NULL, &tv); + goto again; + } + + my_set_errno (EACCES); + return -1; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of make_dotlock. Returns 0 on success and -1 on + error. */ +static int +dotlock_take_w32 (dotlock_t h, long timeout) +{ + int wtime = 0; + int w32err; + OVERLAPPED ovl; + + again: + /* Lock one byte at offset 0. The offset is given by OVL. */ + memset (&ovl, 0, sizeof ovl); + if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK + | LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl)) + { + h->locked = 1; + return 0; /* okay */ + } + + w32err = GetLastError (); + if (w32err != ERROR_LOCK_VIOLATION) + { + my_error_2 (_("lock '%s' not made: %s\n"), + h->lockname, w32_strerror (w32err)); + my_set_errno (map_w32_to_errno (w32err)); + return -1; + } + + if (timeout) + { + /* Wait until lock has been released. We use retry intervals of + 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */ + if (!wtime) + wtime = 50; + else if (wtime < 800) + wtime *= 2; + else if (wtime == 800) + wtime = 2000; + else if (wtime < 8000) + wtime *= 2; + + if (timeout > 0) + { + if (wtime > timeout) + wtime = timeout; + timeout -= wtime; + } + + if (wtime >= 800) + my_info_1 (_("waiting for lock %s...\n"), h->lockname); + + Sleep (wtime); + goto again; + } + + my_set_errno (EACCES); + return -1; +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Take a lock on H. A value of 0 for TIMEOUT returns immediately if + the lock can't be taked, -1 waits forever (hopefully not), other + values wait for TIMEOUT milliseconds. Returns: 0 on success */ +int +dotlock_take (dotlock_t h, long timeout) +{ + int ret; + + if ( h->disable ) + return 0; /* Locks are completely disabled. Return success. */ + + if ( h->locked ) + { + my_debug_1 ("Oops, '%s' is already locked\n", h->lockname); + return 0; + } + +#ifdef HAVE_DOSISH_SYSTEM + ret = dotlock_take_w32 (h, timeout); +#else /*!HAVE_DOSISH_SYSTEM*/ + ret = dotlock_take_unix (h, timeout); +#endif /*!HAVE_DOSISH_SYSTEM*/ + + return ret; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of release_dotlock. */ +static int +dotlock_release_unix (dotlock_t h) +{ + int pid, same_node; + int saveerrno; + + pid = read_lockfile (h, &same_node); + if ( pid == -1 ) + { + saveerrno = errno; + my_error_0 ("release_dotlock: lockfile error\n"); + my_set_errno (saveerrno); + return -1; + } + if ( pid != getpid() || !same_node ) + { + my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid); + my_set_errno (EACCES); + return -1; + } + + if ( unlink( h->lockname ) ) + { + saveerrno = errno; + my_error_1 ("release_dotlock: error removing lockfile '%s'\n", + h->lockname); + my_set_errno (saveerrno); + return -1; + } + /* Fixme: As an extra check we could check whether the link count is + now really at 1. */ + return 0; +} +#endif /*HAVE_POSIX_SYSTEM */ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of release_dotlock. */ +static int +dotlock_release_w32 (dotlock_t h) +{ + OVERLAPPED ovl; + + memset (&ovl, 0, sizeof ovl); + if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl)) + { + int saveerrno = map_w32_to_errno (GetLastError ()); + my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n", + h->lockname, w32_strerror (-1)); + my_set_errno (saveerrno); + return -1; + } + + return 0; +} +#endif /*HAVE_DOSISH_SYSTEM */ + + +/* Release a lock. Returns 0 on success. */ +int +dotlock_release (dotlock_t h) +{ + int ret; + + /* To avoid atexit race conditions we first check whether there are + any locks left. It might happen that another atexit handler + tries to release the lock while the atexit handler of this module + already ran and thus H is undefined. */ + LOCK_all_lockfiles (); + ret = !all_lockfiles; + UNLOCK_all_lockfiles (); + if (ret) + return 0; + + if ( h->disable ) + return 0; + + if ( !h->locked ) + { + my_debug_1 ("Oops, '%s' is not locked\n", h->lockname); + return 0; + } + +#ifdef HAVE_DOSISH_SYSTEM + ret = dotlock_release_w32 (h); +#else + ret = dotlock_release_unix (h); +#endif + + if (!ret) + h->locked = 0; + return ret; +} + + + +/* Remove all lockfiles. This is called by the atexit handler + installed by this module but may also be called by other + termination handlers. */ +void +dotlock_remove_lockfiles (void) +{ + dotlock_t h, h2; + + /* First set the lockfiles list to NULL so that for example + dotlock_release is aware that this function is currently + running. */ + LOCK_all_lockfiles (); + h = all_lockfiles; + all_lockfiles = NULL; + UNLOCK_all_lockfiles (); + + while ( h ) + { + h2 = h->next; + dotlock_destroy (h); + h = h2; + } +} diff --git a/common/dotlock.h b/common/dotlock.h new file mode 100644 index 0000000..78a7e73 --- /dev/null +++ b/common/dotlock.h @@ -0,0 +1,112 @@ +/* dotlock.h - dotfile locking declarations + * Copyright (C) 2000, 2001, 2006, 2011 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU Lesser General License or the GNU + * General Public License. If you wish to allow use of your version of + * this file only under the terms of the GNU Lesser General License or + * the GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GNUPG_COMMON_DOTLOCK_H +#define GNUPG_COMMON_DOTLOCK_H + +/* See dotlock.c for a description. */ + +#ifdef DOTLOCK_EXT_SYM_PREFIX +# ifndef _DOTLOCK_PREFIX +# define _DOTLOCK_PREFIX1(x,y) x ## y +# define _DOTLOCK_PREFIX2(x,y) _DOTLOCK_PREFIX1(x,y) +# define _DOTLOCK_PREFIX(x) _DOTLOCK_PREFIX2(DOTLOCK_EXT_SYM_PREFIX,x) +# endif /*_DOTLOCK_PREFIX*/ +# define dotlock_disable _DOTLOCK_PREFIX(dotlock_disable) +# define dotlock_create _DOTLOCK_PREFIX(dotlock_create) +# define dotlock_set_fd _DOTLOCK_PREFIX(dotlock_set_fd) +# define dotlock_get_fd _DOTLOCK_PREFIX(dotlock_get_fd) +# define dotlock_destroy _DOTLOCK_PREFIX(dotlock_destroy) +# define dotlock_take _DOTLOCK_PREFIX(dotlock_take) +# define dotlock_release _DOTLOCK_PREFIX(dotlock_release) +# define dotlock_remove_lockfiles _DOTLOCK_PREFIX(dotlock_remove_lockfiles) +#endif /*DOTLOCK_EXT_SYM_PREFIX*/ + +#ifdef __cplusplus +extern "C" +{ +#if 0 +} +#endif +#endif + + +struct dotlock_handle; +typedef struct dotlock_handle *dotlock_t; + +void dotlock_disable (void); +dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags); +void dotlock_set_fd (dotlock_t h, int fd); +int dotlock_get_fd (dotlock_t h); +void dotlock_destroy (dotlock_t h); +int dotlock_take (dotlock_t h, long timeout); +int dotlock_release (dotlock_t h); +void dotlock_remove_lockfiles (void); + +#ifdef __cplusplus +} +#endif +#endif /*GNUPG_COMMON_DOTLOCK_H*/ diff --git a/common/dynload.h b/common/dynload.h new file mode 100644 index 0000000..61930d2 --- /dev/null +++ b/common/dynload.h @@ -0,0 +1,97 @@ +/* dynload.h - Wrapper functions for run-time dynamic loading + * Copyright (C) 2003, 2010 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_DYNLOAD_H +#define GNUPG_COMMON_DYNLOAD_H + +#ifndef __MINGW32__ +# include +#else +# include +# include "utf8conv.h" +# include "mischelp.h" +# define RTLD_LAZY 0 + +static inline void * +dlopen (const char *name, int flag) +{ + void *hd; +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wname = utf8_to_wchar (name); + hd = wname? LoadLibrary (wname) : NULL; + xfree (wname); +#else + hd = LoadLibrary (name); +#endif + (void)flag; + return hd; +} + +static inline void * +dlsym (void *hd, const char *sym) +{ + if (hd && sym) + { +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wsym = utf8_to_wchar (sym); + void *fnc = wsym? GetProcAddress (hd, wsym) : NULL; + xfree (wsym); +#else + void *fnc = GetProcAddress (hd, sym); +#endif + if (!fnc) + return NULL; + return fnc; + } + return NULL; +} + + +static inline const char * +dlerror (void) +{ + static char buf[32]; + snprintf (buf, sizeof buf, "ec=%lu", GetLastError ()); + return buf; +} + + +static inline int +dlclose (void * hd) +{ + if (hd) + { + CloseHandle (hd); + return 0; + } + return -1; +} +# endif /*__MINGW32__*/ +#endif /*GNUPG_COMMON_DYNLOAD_H*/ diff --git a/common/exaudit.awk b/common/exaudit.awk new file mode 100644 index 0000000..0d2f95e --- /dev/null +++ b/common/exaudit.awk @@ -0,0 +1,43 @@ +# exaudit.awk - Extract audit event codes from audit.h +# Copyright (C) 2007 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 3 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, see . + +BEGIN { + print "# Output of exaudit.awk - DO NOT EDIT." + topheader = 0; + okay = 0; + code = 0; +} + +topheader == 0 && /^\/\*/ { topheader = 1 } +topheader == 1 { print $0 } +topheader == 1 && /\*\// { topheader = 2; print "" } + +/AUDIT_NULL_EVENT/ { okay = 1 } +!okay { next } +/AUDIT_LAST_EVENT/ { exit } +/AUDIT_[A-Za-z_]+/ { + sub (/[,\/\*]+/, "", $1); + desc = tolower (substr($1,7)); + gsub (/_/," ",desc); + printf "%d\t%s\t%s\n", code, $1, desc; + code++; +} + +END { + print "# end of audit codes." +} diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c new file mode 100644 index 0000000..7237993 --- /dev/null +++ b/common/exechelp-posix.c @@ -0,0 +1,892 @@ +/* exechelp.c - Fork and exec helpers for POSIX + * Copyright (C) 2004, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include + +#if defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM) +#error This code is only used on POSIX +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include +#include + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +#undef HAVE_NPTH +#undef USE_NPTH +#endif + +#ifdef HAVE_NPTH +#include +#endif +#include + +#ifdef HAVE_GETRLIMIT +#include +#include +#endif /*HAVE_GETRLIMIT*/ + +#ifdef HAVE_STAT +# include +#endif + +#if __linux__ +# include +# include +#endif /*__linux__ */ + +#include "util.h" +#include "i18n.h" +#include "sysutils.h" +#include "exechelp.h" + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static inline gpg_error_t +my_error (int errcode) +{ + return gpg_err_make (default_errsource, errcode); +} + + +/* Return the maximum number of currently allowed open file + descriptors. Only useful on POSIX systems but returns a value on + other systems too. */ +int +get_max_fds (void) +{ + int max_fds = -1; +#ifdef HAVE_GETRLIMIT + struct rlimit rl; + + /* Under Linux we can figure out the highest used file descriptor by + * reading /proc/PID/fd. This is in the common cases much fast than + * for example doing 4096 close calls where almost all of them will + * fail. On a system with a limit of 4096 files and only 8 files + * open with the highest number being 10, we speedup close_all_fds + * from 125ms to 0.4ms including readdir. + * + * Another option would be to close the file descriptors as returned + * from reading that directory - however then we need to snapshot + * that list before starting to close them. */ +#ifdef __linux__ + { + DIR *dir = NULL; + struct dirent *dir_entry; + const char *s; + int x; + + dir = opendir ("/proc/self/fd"); + if (dir) + { + while ((dir_entry = readdir (dir))) + { + s = dir_entry->d_name; + if ( *s < '0' || *s > '9') + continue; + x = atoi (s); + if (x > max_fds) + max_fds = x; + } + closedir (dir); + } + if (max_fds != -1) + return max_fds + 1; + } +#endif /* __linux__ */ + + +# ifdef RLIMIT_NOFILE + if (!getrlimit (RLIMIT_NOFILE, &rl)) + max_fds = rl.rlim_max; +# endif + +# ifdef RLIMIT_OFILE + if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl)) + max_fds = rl.rlim_max; + +# endif +#endif /*HAVE_GETRLIMIT*/ + +#ifdef _SC_OPEN_MAX + if (max_fds == -1) + { + long int scres = sysconf (_SC_OPEN_MAX); + if (scres >= 0) + max_fds = scres; + } +#endif + +#ifdef _POSIX_OPEN_MAX + if (max_fds == -1) + max_fds = _POSIX_OPEN_MAX; +#endif + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + /* AIX returns INT32_MAX instead of a proper value. We assume that + this is always an error and use an arbitrary limit. */ +#ifdef INT32_MAX + if (max_fds == INT32_MAX) + max_fds = 256; +#endif + + return max_fds; +} + + +/* Close all file descriptors starting with descriptor FIRST. If + EXCEPT is not NULL, it is expected to be a list of file descriptors + which shall not be closed. This list shall be sorted in ascending + order with the end marked by -1. */ +void +close_all_fds (int first, int *except) +{ + int max_fd = get_max_fds (); + int fd, i, except_start; + + if (except) + { + except_start = 0; + for (fd=first; fd < max_fd; fd++) + { + for (i=except_start; except[i] != -1; i++) + { + if (except[i] == fd) + { + /* If we found the descriptor in the exception list + we can start the next compare run at the next + index because the exception list is ordered. */ + except_start = i + 1; + break; + } + } + if (except[i] == -1) + close (fd); + } + } + else + { + for (fd=first; fd < max_fd; fd++) + close (fd); + } + + gpg_err_set_errno (0); +} + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO + accordingly. */ +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} + + +/* The exec core used right after the fork. This will never return. */ +static void +do_exec (const char *pgmname, const char *argv[], + int fd_in, int fd_out, int fd_err, + int *except, void (*preexec)(void) ) +{ + char **arg_list; + int i, j; + int fds[3]; + + fds[0] = fd_in; + fds[1] = fd_out; + fds[2] = fd_err; + + /* Create the command line argument array. */ + i = 0; + if (argv) + while (argv[i]) + i++; + arg_list = xcalloc (i+2, sizeof *arg_list); + arg_list[0] = strrchr (pgmname, '/'); + if (arg_list[0]) + arg_list[0]++; + else + arg_list[0] = xstrdup (pgmname); + if (argv) + for (i=0,j=1; argv[i]; i++, j++) + arg_list[j] = (char*)argv[i]; + + /* Assign /dev/null to unused FDs. */ + for (i=0; i <= 2; i++) + { + if (fds[i] == -1 ) + { + fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY); + if (fds[i] == -1) + log_fatal ("failed to open '%s': %s\n", + "/dev/null", strerror (errno)); + } + } + + /* Connect the standard files. */ + for (i=0; i <= 2; i++) + { + if (fds[i] != i && dup2 (fds[i], i) == -1) + log_fatal ("dup2 std%s failed: %s\n", + i==0?"in":i==1?"out":"err", strerror (errno)); + } + + /* Close all other files. */ + close_all_fds (3, except); + + if (preexec) + preexec (); + execv (pgmname, arg_list); + /* No way to print anything, as we have closed all streams. */ + _exit (127); +} + + +static gpg_error_t +do_create_pipe (int filedes[2]) +{ + gpg_error_t err = 0; + + if (pipe (filedes) == -1) + { + err = my_error_from_syserror (); + filedes[0] = filedes[1] = -1; + } + + return err; +} + + +static gpg_error_t +create_pipe_and_estream (int filedes[2], estream_t *r_fp, + int outbound, int nonblock) +{ + gpg_error_t err; + + if (pipe (filedes) == -1) + { + err = my_error_from_syserror (); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + filedes[0] = filedes[1] = -1; + *r_fp = NULL; + return err; + } + + if (!outbound) + *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r"); + else + *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w"); + if (!*r_fp) + { + err = my_error_from_syserror (); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + close (filedes[0]); + close (filedes[1]); + filedes[0] = filedes[1] = -1; + return err; + } + return 0; +} + + +/* Portable function to create a pipe. Under Windows the write end is + inheritable. If R_FP is not NULL, an estream is created for the + read end and stored at R_FP. */ +gpg_error_t +gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return create_pipe_and_estream (filedes, r_fp, 0, nonblock); + else + return do_create_pipe (filedes); +} + + +/* Portable function to create a pipe. Under Windows the read end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t +gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return create_pipe_and_estream (filedes, r_fp, 1, nonblock); + else + return do_create_pipe (filedes); +} + + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t +gnupg_create_pipe (int filedes[2]) +{ + return do_create_pipe (filedes); +} + + +/* Fork and exec the PGMNAME, see exechelp.h for details. */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + int inpipe[2] = {-1, -1}; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + estream_t infp = NULL; + estream_t outfp = NULL; + estream_t errfp = NULL; + int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); + + if (r_infp) + *r_infp = NULL; + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + if (r_infp) + { + err = create_pipe_and_estream (inpipe, &infp, 1, nonblock); + if (err) + return err; + } + + if (r_outfp) + { + err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock); + if (err) + { + if (infp) + es_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + return err; + } + } + + if (r_errfp) + { + err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock); + if (err) + { + if (infp) + es_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + return err; + } + } + + + *pid = fork (); + if (*pid == (pid_t)(-1)) + { + err = my_error_from_syserror (); + log_error (_("error forking process: %s\n"), gpg_strerror (err)); + + if (infp) + es_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + if (errfp) + es_fclose (errfp); + else if (errpipe[0] != -1) + close (errpipe[0]); + if (errpipe[1] != -1) + close (errpipe[1]); + return err; + } + + if (!*pid) + { + /* This is the child. */ + gcry_control (GCRYCTL_TERM_SECMEM); + es_fclose (infp); + es_fclose (outfp); + es_fclose (errfp); + do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], + except, preexec); + /*NOTREACHED*/ + } + + /* This is the parent. */ + if (inpipe[0] != -1) + close (inpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + if (errpipe[1] != -1) + close (errpipe[1]); + + if (r_infp) + *r_infp = infp; + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + + return 0; +} + + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process is required. + + Returns 0 on success or an error code. */ +gpg_error_t +gnupg_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + + *pid = fork (); + if (*pid == (pid_t)(-1)) + { + err = my_error_from_syserror (); + log_error (_("error forking process: %s\n"), strerror (errno)); + return err; + } + + if (!*pid) + { + gcry_control (GCRYCTL_TERM_SECMEM); + /* Run child. */ + do_exec (pgmname, argv, infd, outfd, errfd, NULL, NULL); + /*NOTREACHED*/ + } + + return 0; +} + + + + +/* Waiting for child processes. + + waitpid(2) may return information about terminated children that we + did not yet request, and there is no portable way to wait for a + specific set of children. + + As a workaround, we store the results of children for later use. + + XXX: This assumes that PIDs are not reused too quickly. */ + +struct terminated_child +{ + pid_t pid; + int exitcode; + struct terminated_child *next; +}; + +struct terminated_child *terminated_children; + + +static gpg_error_t +store_result (pid_t pid, int exitcode) +{ + struct terminated_child *c; + + c = xtrymalloc (sizeof *c); + if (c == NULL) + return gpg_err_code_from_syserror (); + + c->pid = pid; + c->exitcode = exitcode; + c->next = terminated_children; + terminated_children = c; + + return 0; +} + + +static int +get_result (pid_t pid, int *r_exitcode) +{ + struct terminated_child *c, **prevp; + + for (prevp = &terminated_children, c = terminated_children; + c; + prevp = &c->next, c = c->next) + if (c->pid == pid) + { + *prevp = c->next; + *r_exitcode = c->exitcode; + xfree (c); + return 1; + } + + return 0; +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) +{ + gpg_err_code_t ec; + int i, status; + + if (r_exitcode) + *r_exitcode = -1; + + if (pid == (pid_t)(-1)) + return gpg_error (GPG_ERR_INV_VALUE); + +#ifdef USE_NPTH + i = npth_waitpid (pid, &status, hang? 0:WNOHANG); +#else + while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1) + && errno == EINTR); +#endif + + if (i == (pid_t)(-1)) + { + ec = gpg_err_code_from_errno (errno); + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, strerror (errno)); + } + else if (!i) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + } + else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) + { + log_error (_("error running '%s': probably not installed\n"), pgmname); + ec = GPG_ERR_CONFIGURATION; + } + else if (WIFEXITED (status) && WEXITSTATUS (status)) + { + if (!r_exitcode) + log_error (_("error running '%s': exit status %d\n"), pgmname, + WEXITSTATUS (status)); + else + *r_exitcode = WEXITSTATUS (status); + ec = GPG_ERR_GENERAL; + } + else if (!WIFEXITED (status)) + { + log_error (_("error running '%s': terminated\n"), pgmname); + ec = GPG_ERR_GENERAL; + } + else + { + if (r_exitcode) + *r_exitcode = 0; + ec = 0; + } + + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + gpg_err_code_t ec = 0; + size_t i, left; + int *dummy = NULL; + + if (r_exitcodes == NULL) + { + dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count); + if (dummy == NULL) + return gpg_err_code_from_syserror (); + } + + for (i = 0, left = count; i < count; i++) + { + int status = -1; + + if (pids[i] == (pid_t)(-1)) + return my_error (GPG_ERR_INV_VALUE); + + /* See if there was a previously stored result for this pid. */ + if (get_result (pids[i], &status)) + left -= 1; + + r_exitcodes[i] = status; + } + + while (left > 0) + { + pid_t pid; + int status; + +#ifdef USE_NPTH + pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG); +#else + while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1) + && errno == EINTR); +#endif + + if (pid == (pid_t)(-1)) + { + ec = gpg_err_code_from_errno (errno); + log_error (_("waiting for processes to terminate failed: %s\n"), + strerror (errno)); + break; + } + else if (!pid) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + break; + } + else + { + for (i = 0; i < count; i++) + if (pid == pids[i]) + break; + + if (i == count) + { + /* No match, store this result. */ + ec = store_result (pid, status); + if (ec) + break; + continue; + } + + /* Process PIDS[i] died. */ + if (r_exitcodes[i] != (pid_t) -1) + { + log_error ("PID %d was reused", pid); + ec = GPG_ERR_GENERAL; + break; + } + + left -= 1; + r_exitcodes[i] = status; + } + } + + if (ec == 0) + for (i = 0; i < count; i++) + { + if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127) + { + log_error (_("error running '%s': probably not installed\n"), + pgmnames[i]); + ec = GPG_ERR_CONFIGURATION; + } + else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i])) + { + if (dummy) + log_error (_("error running '%s': exit status %d\n"), + pgmnames[i], WEXITSTATUS (r_exitcodes[i])); + else + r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]); + ec = GPG_ERR_GENERAL; + } + else if (!WIFEXITED (r_exitcodes[i])) + { + log_error (_("error running '%s': terminated\n"), pgmnames[i]); + ec = GPG_ERR_GENERAL; + } + } + + xfree (dummy); + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + + + +void +gnupg_release_process (pid_t pid) +{ + (void)pid; +} + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. All standard file descriptors are + connected to /dev/null. */ +gpg_error_t +gnupg_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + pid_t pid; + int i; + + if (getuid() != geteuid()) + return my_error (GPG_ERR_BUG); + + if (access (pgmname, X_OK)) + return my_error_from_syserror (); + + pid = fork (); + if (pid == (pid_t)(-1)) + { + log_error (_("error forking process: %s\n"), strerror (errno)); + return my_error_from_syserror (); + } + if (!pid) + { + pid_t pid2; + + gcry_control (GCRYCTL_TERM_SECMEM); + if (setsid() == -1 || chdir ("/")) + _exit (1); + + pid2 = fork (); /* Double fork to let init take over the new child. */ + if (pid2 == (pid_t)(-1)) + _exit (1); + if (pid2) + _exit (0); /* Let the parent exit immediately. */ + + if (envp) + for (i=0; envp[i]; i++) + putenv (xstrdup (envp[i])); + + do_exec (pgmname, argv, -1, -1, -1, NULL, NULL); + + /*NOTREACHED*/ + } + + if (waitpid (pid, NULL, 0) == -1) + log_error ("waitpid failed in gnupg_spawn_process_detached: %s", + strerror (errno)); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +gnupg_kill_process (pid_t pid) +{ + if (pid != (pid_t)(-1)) + { + kill (pid, SIGTERM); + } +} diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c new file mode 100644 index 0000000..a7a6db3 --- /dev/null +++ b/common/exechelp-w32.c @@ -0,0 +1,921 @@ +/* exechelp-w32.c - Fork and exec helpers for W32. + * Copyright (C) 2004, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include + +#if !defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM) +#error This code is only used on W32. +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include +#include + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +#undef HAVE_NPTH +#undef USE_NPTH +#endif + +#ifdef HAVE_NPTH +#include +#endif + +#ifdef HAVE_STAT +# include +#endif + + +#include "util.h" +#include "i18n.h" +#include "sysutils.h" +#include "exechelp.h" + +/* Define to 1 do enable debugging. */ +#define DEBUG_W32_SPAWN 0 + + +/* It seems Vista doesn't grok X_OK and so fails access() tests. + Previous versions interpreted X_OK as F_OK anyway, so we'll just + use F_OK directly. */ +#undef X_OK +#define X_OK F_OK + +/* We assume that a HANDLE can be represented by an int which should + be true for all i386 systems (HANDLE is defined as void *) and + these are the only systems for which Windows is available. Further + we assume that -1 denotes an invalid handle. */ +# define fd_to_handle(a) ((HANDLE)(a)) +# define handle_to_fd(a) ((int)(a)) +# define pid_to_handle(a) ((HANDLE)(a)) +# define handle_to_pid(a) ((int)(a)) + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static inline gpg_error_t +my_error (int errcode) +{ + return gpg_err_make (default_errsource, errcode); +} + + +/* Return the maximum number of currently allowed open file + descriptors. Only useful on POSIX systems but returns a value on + other systems too. */ +int +get_max_fds (void) +{ + int max_fds = -1; + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + return max_fds; +} + + +/* Under Windows this is a dummy function. */ +void +close_all_fds (int first, int *except) +{ + (void)first; + (void)except; +} + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO + accordingly. */ +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} + + +/* Helper function to build_w32_commandline. */ +static char * +build_w32_commandline_copy (char *buffer, const char *string) +{ + char *p = buffer; + const char *s; + + if (!*string) /* Empty string. */ + p = stpcpy (p, "\"\""); + else if (strpbrk (string, " \t\n\v\f\"")) + { + /* Need to do some kind of quoting. */ + p = stpcpy (p, "\""); + for (s=string; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else + p = stpcpy (p, string); + + return p; +} + +/* Build a command line for use with W32's CreateProcess. On success + CMDLINE gets the address of a newly allocated string. */ +static gpg_error_t +build_w32_commandline (const char *pgmname, const char * const *argv, + char **cmdline) +{ + int i, n; + const char *s; + char *buf, *p; + + *cmdline = NULL; + n = 0; + s = pgmname; + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + for (i=0; (s=argv[i]); i++) + { + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + } + n++; + + buf = p = xtrymalloc (n); + if (!buf) + return my_error_from_syserror (); + + p = build_w32_commandline_copy (p, pgmname); + for (i=0; argv[i]; i++) + { + *p++ = ' '; + p = build_w32_commandline_copy (p, argv[i]); + } + + *cmdline= buf; + return 0; +} + + +#define INHERIT_READ 1 +#define INHERIT_WRITE 2 +#define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE) + +/* Create pipe. FLAGS indicates which ends are inheritable. */ +static int +create_inheritable_pipe (HANDLE filedes[2], int flags) +{ + HANDLE r, w; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = TRUE; + + if (!CreatePipe (&r, &w, &sec_attr, 0)) + return -1; + + if ((flags & INHERIT_READ) == 0) + if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0)) + goto fail; + + if ((flags & INHERIT_WRITE) == 0) + if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0)) + goto fail; + + filedes[0] = r; + filedes[1] = w; + return 0; + + fail: + log_error ("SetHandleInformation failed: %s\n", w32_strerror (-1)); + CloseHandle (r); + CloseHandle (w); + return -1; +} + + +static HANDLE +w32_open_null (int for_write) +{ + HANDLE hfile; + + hfile = CreateFileW (L"nul", + for_write? GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (hfile == INVALID_HANDLE_VALUE) + log_debug ("can't open 'nul': %s\n", w32_strerror (-1)); + return hfile; +} + + +static gpg_error_t +create_pipe_and_estream (int filedes[2], int flags, + estream_t *r_fp, int outbound, int nonblock) +{ + gpg_error_t err = 0; + HANDLE fds[2]; + + filedes[0] = filedes[1] = -1; + err = my_error (GPG_ERR_GENERAL); + if (!create_inheritable_pipe (fds, flags)) + { + filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY); + if (filedes[0] == -1) + { + log_error ("failed to translate osfhandle %p\n", fds[0]); + CloseHandle (fds[1]); + } + else + { + filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND); + if (filedes[1] == -1) + { + log_error ("failed to translate osfhandle %p\n", fds[1]); + close (filedes[0]); + filedes[0] = -1; + CloseHandle (fds[1]); + } + else + err = 0; + } + } + + if (! err && r_fp) + { + if (!outbound) + *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r"); + else + *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w"); + if (!*r_fp) + { + err = my_error_from_syserror (); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + close (filedes[0]); + close (filedes[1]); + filedes[0] = filedes[1] = -1; + return err; + } + } + + return err; +} + +/* Portable function to create a pipe. Under Windows the write end is + inheritable. If R_FP is not NULL, an estream is created for the + read end and stored at R_FP. */ +gpg_error_t +gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + return create_pipe_and_estream (filedes, INHERIT_WRITE, + r_fp, 0, nonblock); +} + + +/* Portable function to create a pipe. Under Windows the read end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t +gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + return create_pipe_and_estream (filedes, INHERIT_READ, + r_fp, 1, nonblock); +} + + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t +gnupg_create_pipe (int filedes[2]) +{ + return create_pipe_and_estream (filedes, INHERIT_BOTH, + NULL, 0, 0); +} + + +/* Fork and exec the PGMNAME, see exechelp.h for details. */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFO si; + int cr_flags; + char *cmdline; + HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + estream_t infp = NULL; + estream_t outfp = NULL; + estream_t errfp = NULL; + HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE}; + int i; + es_syshd_t syshd; + gpg_err_source_t errsource = default_errsource; + int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); + + (void)except; /* Not yet used. */ + + if (r_infp) + *r_infp = NULL; + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + if (r_infp) + { + if (create_inheritable_pipe (inpipe, INHERIT_READ)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = inpipe[1]; + infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w"); + if (!infp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (inpipe[0]); + CloseHandle (inpipe[1]); + inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE; + return err; + } + } + + if (r_outfp) + { + if (create_inheritable_pipe (outpipe, INHERIT_WRITE)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = outpipe[0]; + outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + if (!outfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (outpipe[0]); + CloseHandle (outpipe[1]); + outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; + if (infp) + es_fclose (infp); + else if (inpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[1]); + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + return err; + } + } + + if (r_errfp) + { + if (create_inheritable_pipe (errpipe, INHERIT_WRITE)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = errpipe[0]; + errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + if (!errfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (errpipe[0]); + CloseHandle (errpipe[1]); + errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (infp) + es_fclose (infp); + else if (inpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[1]); + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + return err; + } + } + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + if (inpipe[0] == INVALID_HANDLE_VALUE) + nullhd[0] = w32_open_null (0); + if (outpipe[1] == INVALID_HANDLE_VALUE) + nullhd[1] = w32_open_null (1); + if (errpipe[1] == INVALID_HANDLE_VALUE) + nullhd[2] = w32_open_null (1); + + /* Start the process. Note that we can't run the PREEXEC function + because this might change our own environment. */ + (void)preexec; + + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0]; + si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; + si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0) + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED); +/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ + if (!CreateProcess (pgmname, /* Program to start. */ + cmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + TRUE, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + if (infp) + es_fclose (infp); + else if (inpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (errfp) + es_fclose (errfp); + else if (errpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[0]); + if (errpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[1]); + return gpg_err_make (errsource, GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + /* Close the inherited handles to /dev/null. */ + for (i=0; i < DIM (nullhd); i++) + if (nullhd[i] != INVALID_HANDLE_VALUE) + CloseHandle (nullhd[i]); + + /* Close the inherited ends of the pipes. */ + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (errpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[1]); + + /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ + + /* Fixme: For unknown reasons AllowSetForegroundWindow returns an + invalid argument error if we pass it the correct processID. As a + workaround we use -1 (ASFW_ANY). */ + if ((flags & GNUPG_SPAWN_RUN_ASFW)) + gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + if (r_infp) + *r_infp = infp; + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + + *pid = handle_to_pid (pi.hProcess); + return 0; + +} + + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process is required. + + Returns 0 on success or an error code. */ +gpg_error_t +gnupg_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFO si; + char *cmdline; + int i; + HANDLE stdhd[3]; + + /* Setup return values. */ + *pid = (pid_t)(-1); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; + stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; + stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; + si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd); + si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd); + si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd); + +/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ + if (!CreateProcess (pgmname, /* Program to start. */ + cmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + TRUE, /* Inherit handles. */ + (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED | DETACHED_PROCESS), + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + err = my_error (GPG_ERR_GENERAL); + } + else + err = 0; + xfree (cmdline); + for (i=0; i < 3; i++) + if (stdhd[i] != INVALID_HANDLE_VALUE) + CloseHandle (stdhd[i]); + if (err) + return err; + +/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ +/* " dwProcessID=%d dwThreadId=%d\n", */ +/* pi.hProcess, pi.hThread, */ +/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + *pid = handle_to_pid (pi.hProcess); + return 0; + +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) +{ + return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode); +} + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + gpg_err_code_t ec = 0; + size_t i; + HANDLE *procs; + int code; + + procs = xtrycalloc (count, sizeof *procs); + if (procs == NULL) + return my_error_from_syserror (); + + for (i = 0; i < count; i++) + { + if (r_exitcodes) + r_exitcodes[i] = -1; + + if (pids[i] == (pid_t)(-1)) + return my_error (GPG_ERR_INV_VALUE); + + procs[i] = fd_to_handle (pids[i]); + } + + /* FIXME: We should do a pth_waitpid here. However this has not yet + been implemented. A special W32 pth system call would even be + better. */ + code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0); + switch (code) + { + case WAIT_TIMEOUT: + ec = GPG_ERR_TIMEOUT; + goto leave; + + case WAIT_FAILED: + log_error (_("waiting for processes to terminate failed: %s\n"), + w32_strerror (-1)); + ec = GPG_ERR_GENERAL; + goto leave; + + case WAIT_OBJECT_0: + for (i = 0; i < count; i++) + { + DWORD exc; + + if (! GetExitCodeProcess (procs[i], &exc)) + { + log_error (_("error getting exit code of process %d: %s\n"), + (int) pids[i], w32_strerror (-1) ); + ec = GPG_ERR_GENERAL; + } + else if (exc) + { + if (!r_exitcodes) + log_error (_("error running '%s': exit status %d\n"), + pgmnames[i], (int)exc); + else + r_exitcodes[i] = (int)exc; + ec = GPG_ERR_GENERAL; + } + else + { + if (r_exitcodes) + r_exitcodes[i] = 0; + } + } + break; + + default: + log_error ("WaitForMultipleObjects returned unexpected " + "code %d\n", code); + ec = GPG_ERR_GENERAL; + break; + } + + leave: + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + + + +void +gnupg_release_process (pid_t pid) +{ + if (pid != (pid_t)INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE)pid; + + CloseHandle (process); + } +} + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. All standard file descriptors are + connected to /dev/null. */ +gpg_error_t +gnupg_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFO si; + int cr_flags; + char *cmdline; + + + /* We don't use ENVP. */ + (void)envp; + + if (access (pgmname, X_OK)) + return my_error_from_syserror (); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + /* Start the process. */ + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_NEW_PROCESS_GROUP + | DETACHED_PROCESS); +/* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */ +/* pgmname, cmdline); */ + if (!CreateProcess (pgmname, /* Program to start. */ + cmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + FALSE, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + return my_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + +/* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ +/* " dwProcessID=%d dwThreadId=%d\n", */ +/* pi.hProcess, pi.hThread, */ +/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + CloseHandle (pi.hThread); + CloseHandle (pi.hProcess); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +gnupg_kill_process (pid_t pid) +{ + if (pid != (pid_t) INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE) pid; + + /* Arbitrary error code. */ + TerminateProcess (process, 1); + } +} diff --git a/common/exechelp-w32ce.c b/common/exechelp-w32ce.c new file mode 100644 index 0000000..ec9f014 --- /dev/null +++ b/common/exechelp-w32ce.c @@ -0,0 +1,886 @@ +/* exechelp-w32.c - Fork and exec helpers for W32CE. + * Copyright (C) 2004, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include + +#if !defined(HAVE_W32_SYSTEM) && !defined (HAVE_W32CE_SYSTEM) +#error This code is only used on W32CE. +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include +#include + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +#undef HAVE_NPTH +#undef USE_NPTH +#endif + +#ifdef HAVE_NPTH +#include +#endif + +#ifdef HAVE_STAT +# include +#endif + +#include + +#include "util.h" +#include "i18n.h" +#include "sysutils.h" +#include "exechelp.h" + + +/* It seems Vista doesn't grok X_OK and so fails access() tests. + Previous versions interpreted X_OK as F_OK anyway, so we'll just + use F_OK directly. */ +#undef X_OK +#define X_OK F_OK + + +/* We assume that a HANDLE can be represented by an int which should + be true for all i386 systems (HANDLE is defined as void *) and + these are the only systems for which Windows is available. Further + we assume that -1 denotes an invalid handle. */ +#define fd_to_handle(a) ((HANDLE)(a)) +#define handle_to_fd(a) ((int)(a)) +#define pid_to_handle(a) ((HANDLE)(a)) +#define handle_to_pid(a) ((int)(a)) + + +#ifdef USE_NPTH +/* The data passed to the feeder_thread. */ +struct feeder_thread_parms +{ + estream_t stream; + volatile int stream_valid; + HANDLE hd; + int direction; +}; + + +/* The thread started by start_feede3. */ +static void * +feeder_thread (void *arg) +{ + struct feeder_thread_parms *parm = arg; + char buffer[4096]; + int rc; + + if (parm->direction) + { + size_t nread = 0; + DWORD nwritten; + + log_debug ("feeder_thread estream->pipe: stream=%p pipe=%p\n", + parm->stream, parm->hd); + while (parm->stream_valid + && !es_read (parm->stream, buffer, sizeof buffer, &nread)) + { + do + { + pth_enter (); + rc = WriteFile (parm->hd, buffer, nread, &nwritten, NULL); + pth_leave (); + if (!rc) + { + log_debug ("feeder(%p): WriteFile error: rc=%d\n", + parm->hd, (int)GetLastError ()); + goto leave; + } + nread -= nwritten; + } + while (nread); + } + if (!parm->stream_valid) + log_debug ("feeder(%p): closed by other thread\n", parm->hd); + else if (nread) + log_debug ("feeder(%p): es_read error: %s\n", + parm->hd, strerror (errno)); + } + else + { + DWORD nread = 0; + size_t nwritten; + + log_debug ("feeder_thread pipe->estream: stream=%p pipe=%p\n", + parm->stream, parm->hd); + while ( (pth_enter (), + (rc = ReadFile (parm->hd, buffer, sizeof buffer, &nread, NULL)), + pth_leave (), + rc) && nread) + { + log_debug ("feeder_thread pipe->estream: read %d bytes\n", + (int)nread); + do + { + if (parm->stream_valid + && es_write (parm->stream, buffer, nread, &nwritten)) + { + log_debug ("feeder(%p): es_write error: %s\n", + parm->hd, strerror (errno)); + goto leave; + } + log_debug ("feeder_thread pipe->estream: es_wrote %d bytes\n", + (int)nwritten); + nread -= nwritten; + } + while (nread && parm->stream_valid); + } + if (!parm->stream_valid) + log_debug ("feeder(%p): closed by other thread\n", parm->hd); + else if (nread) + log_debug ("feeder(%p): ReadFile error: rc=%d\n", + parm->hd, (int)GetLastError ()); + else + log_debug ("feeder(%p): eof\n", parm->hd); + } + +leave: + log_debug ("feeder(%p): waiting for es_fclose\n", parm->hd); + while (parm->stream_valid) + pth_yield (NULL); + log_debug ("feeder(%p): about to close the pipe handle\n", parm->hd); + CloseHandle (parm->hd); + log_debug ("feeder(%p): pipe handle closed\n", parm->hd); + xfree (parm); + return NULL; +} +#endif /*USE_NPTH*/ + +#ifdef USE_NPTH +static void +feeder_onclose_notification (estream_t stream, void *opaque) +{ + struct feeder_thread_parms *parm = opaque; + (void)stream; + log_debug ("feeder(%p): received onclose note\n", parm->hd); + parm->stream_valid = 0; +} +#endif /*USE_NPTH*/ + +/* Fire up a thread to copy data between STREAM and a pipe's + descriptor FD. With DIRECTION set to true the copy takes place + from the stream to the pipe, otherwise from the pipe to the + stream. */ +static gpg_error_t +start_feeder (estream_t stream, HANDLE hd, int direction) +{ +#ifdef USE_NPTH + gpg_error_t err; + struct feeder_thread_parms *parm; + pth_attr_t tattr; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return gpg_error_from_syserror (); + parm->stream = stream; + parm->stream_valid = 1; + parm->hd = hd; + parm->direction = direction; + + if (es_onclose (stream, 1, feeder_onclose_notification, parm)) + { + err = gpg_error_from_syserror (); + xfree (parm); + return err; + } + + tattr = pth_attr_new (); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "exec-feeder"); + + log_debug ("spawning new feeder(%p, %p, %d)\n", stream, hd, direction); + if(!pth_spawn (tattr, feeder_thread, parm)) + { + err = gpg_error_from_syserror (); + es_onclose (stream, 0, feeder_onclose_notification, parm); + xfree (parm); + } + else + err = 0; + pth_attr_destroy (tattr); + + return err; +#else + (void)stream; + (void)hd; + (void)direction; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* No Pth. */ +#endif +} + + + +/* Return the maximum number of currently allowed open file + descriptors. Only useful on POSIX systems but returns a value on + other systems too. */ +int +get_max_fds (void) +{ + int max_fds = -1; + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + return max_fds; +} + + +/* Under Windows this is a dummy function. */ +void +close_all_fds (int first, int *except) +{ + (void)first; + (void)except; +} + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO + accordingly. */ +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} + + + +static char * +copy_quoted (char *p, const char *string) +{ + const char *s; + + if (!*string) /* Empty string. */ + p = stpcpy (p, "\"\""); + else if (strpbrk (string, " \t\n\v\f\"")) /* Need quotes. */ + { + p = stpcpy (p, "\""); + for (s = string; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else /* Copy verbatim. */ + p = stpcpy (p, string); + + return p; +} + + +/* Build a command line for use with W32's CreateProcess. On success + CMDLINE gets the address of a newly allocated string. */ +static int +build_w32_commandline (const char * const *argv, + int rvid0, int rvid1, int rvid2, + char **cmdline) +{ + int i, n; + const char *s; + char *buf, *p; + char fdbuf[3*30]; + + p = fdbuf; + *p = 0; + + if (rvid0) + snprintf (p, 25, "-&S0=%d ", rvid0); + else + strcpy (p, "-&S0=null "); + p += strlen (p); + + if (rvid1) + snprintf (p, 25, "-&S1=%d ", rvid1); + else + strcpy (p, "-&S1=null "); + p += strlen (p); + + if (rvid2) + snprintf (p, 25, "-&S2=%d ", rvid2); + else + strcpy (p, "-&S2=null "); + p += strlen (p); + + *cmdline = NULL; + n = strlen (fdbuf); + for (i=0; (s = argv[i]); i++) + { + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + } + n++; + + buf = p = xtrymalloc (n); + if (! buf) + return -1; + + p = stpcpy (p, fdbuf); + for (i = 0; argv[i]; i++) + { + *p++ = ' '; + p = copy_quoted (p, argv[i]); + } + + *cmdline = buf; + return 0; +} + + +/* Create pipe where one end is inheritable: With an INHERIT_IDX of 0 + the read end is inheritable, with 1 the write end is inheritable. + Note that the inheritable ends are rendezvous ids and no file + descriptors or handles. */ +static gpg_error_t +create_inheritable_pipe (int filedes[2], int inherit_idx) +{ + HANDLE hd; + int rvid; + + filedes[0] = filedes[1] = -1; + hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx); + if (hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1)); + gpg_err_set_errno (EIO); + return gpg_error_from_syserror (); + } + + if (inherit_idx) + { + filedes[0] = handle_to_fd (hd); + filedes[1] = rvid; + } + else + { + filedes[0] = rvid; + filedes[1] = handle_to_fd (hd); + } + return 0; +} + + +/* Portable function to create a pipe. Under Windows the write end is + inheritable (i.e. an rendezvous id). */ +gpg_error_t +gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + return create_inheritable_pipe (filedes, 1); +} + + +/* Portable function to create a pipe. Under Windows the read end is + inheritable (i.e. an rendezvous id). */ +gpg_error_t +gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + return create_inheritable_pipe (filedes, 0); +} + + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t +gnupg_create_pipe (int filedes[2]) +{ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +static int +create_process (const char *pgmname, const char *cmdline, + PROCESS_INFORMATION *pi) +{ + int res; + wchar_t *wpgmname, *wcmdline; + + wpgmname = utf8_to_wchar (pgmname); + if (!wpgmname) + return 0; + wcmdline = utf8_to_wchar (cmdline); + if (!wcmdline) + { + xfree (wpgmname); + return 0; + } + res = CreateProcess (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + NULL, /* Process security attributes. */ + NULL, /* Thread security attributes. */ + FALSE, /* Inherit handles. */ + CREATE_SUSPENDED, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + NULL, /* Startup information. */ + pi); /* Returns process information. */ + xfree (wcmdline); + xfree (wpgmname); + return res; +} + + +/* Fork and exec the PGMNAME, see exechelp.h for details. */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + PROCESS_INFORMATION pi = {NULL }; + char *cmdline; + es_syshd_t syshd; + struct { + HANDLE hd; + int rvid; + } inpipe = {INVALID_HANDLE_VALUE, 0}; + struct { + HANDLE hd; + int rvid; + } outpipe = {INVALID_HANDLE_VALUE, 0}; + struct { + HANDLE hd; + int rvid; + } errpipe = {INVALID_HANDLE_VALUE, 0}; + estream_t outfp = NULL; + estream_t errfp = NULL; + gpg_err_source_t errsource = default_errsource; + + (void)except; /* Not yet used. */ + (void)preexec; + (void)flags; + + /* Setup return values. */ + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + log_debug ("%s: enter\n", __func__); + if (infp) + { + es_fflush (infp); + es_rewind (infp); + + /* Create a pipe to copy our infile to the stdin of the child + process. On success inpipe.hd is owned by the feeder. */ + inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1); + if (inpipe.hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", + w32_strerror (-1)); + gpg_err_set_errno (EIO); + return gpg_error_from_syserror (); + } + log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__, + infp, inpipe.hd, inpipe.rvid); + err = start_feeder (infp, inpipe.hd, 1); + if (err) + { + log_error ("error spawning feeder: %s\n", gpg_strerror (err)); + CloseHandle (inpipe.hd); + return err; + } + inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder. */ + log_debug ("%s: inpipe %p created; feeder started\n", __func__, + infp); + } + + if (r_outfp) + { + /* Create a pipe to make the stdout of the child process + available as a stream. */ + outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0); + if (outpipe.hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", + w32_strerror (-1)); + gpg_err_set_errno (EIO); + /* Fixme release other stuff/kill feeder. */ + return gpg_error_from_syserror (); + } + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = outpipe.hd; + err = 0; + outfp = es_sysopen (&syshd, "r"); + if (!outfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error ("error opening pipe stream: %s\n", gpg_strerror (err)); + CloseHandle (outpipe.hd); + return err; + } + log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__, + outfp, outpipe.hd, outpipe.rvid); + outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP. */ + } + + if (r_errfp) + { + /* Create a pipe to make the stderr of the child process + available as a stream. */ + errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0); + if (errpipe.hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", + w32_strerror (-1)); + gpg_err_set_errno (EIO); + /* Fixme release other stuff/kill feeder. */ + return gpg_error_from_syserror (); + } + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = errpipe.hd; + err = 0; + errfp = es_sysopen (&syshd, "r"); + if (!errfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error ("error opening pipe stream: %s\n", gpg_strerror (err)); + CloseHandle (errpipe.hd); + return err; + } + log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__, + errfp, errpipe.hd, errpipe.rvid); + errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP. */ + } + + + + /* Build the command line. */ + err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid, + &cmdline); + if (err) + { + /* Fixme release other stuff/kill feeder. */ + CloseHandle (errpipe.hd); + return err; + } + + log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); + if (!create_process (pgmname, cmdline, &pi)) + { + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + /* Fixme release other stuff/kill feeder. */ + CloseHandle (errpipe.hd); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + /* Note: The other end of the pipe is a rendezvous id and thus there + is no need for a close. */ + + log_debug ("CreateProcess ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + *pid = handle_to_pid (pi.hProcess); + return 0; +} + + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process is required. + + Returns 0 on success or an error code. */ +gpg_error_t +gnupg_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + PROCESS_INFORMATION pi = {NULL}; + char *cmdline; + + /* Setup return values. */ + *pid = (pid_t)(-1); + + if (infd != -1 || outfd != -1 || errfd != -1) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + /* Build the command line. */ + err = build_w32_commandline (argv, 0, 0, 0, &cmdline); + if (err) + return err; + + log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); + if (!create_process (pgmname, cmdline, &pi)) + { + log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + log_debug ("CreateProcess(fd) ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + *pid = handle_to_pid (pi.hProcess); + return 0; +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode) +{ + gpg_err_code_t ec; + HANDLE proc = fd_to_handle (pid); + int code; + DWORD exc; + + if (exitcode) + *exitcode = -1; + + if (pid == (pid_t)(-1)) + return gpg_error (GPG_ERR_INV_VALUE); + + /* FIXME: We should do a pth_waitpid here. However this has not yet + been implemented. A special W32 pth system call would even be + better. */ + code = WaitForSingleObject (proc, hang? INFINITE : 0); + switch (code) + { + case WAIT_TIMEOUT: + ec = GPG_ERR_TIMEOUT; + break; + + case WAIT_FAILED: + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, w32_strerror (-1)); + ec = GPG_ERR_GENERAL; + break; + + case WAIT_OBJECT_0: + if (!GetExitCodeProcess (proc, &exc)) + { + log_error (_("error getting exit code of process %d: %s\n"), + (int)pid, w32_strerror (-1) ); + ec = GPG_ERR_GENERAL; + } + else if (exc) + { + log_error (_("error running '%s': exit status %d\n"), + pgmname, (int)exc ); + if (exitcode) + *exitcode = (int)exc; + ec = GPG_ERR_GENERAL; + } + else + { + if (exitcode) + *exitcode = 0; + ec = 0; + } + break; + + default: + log_error ("WaitForSingleObject returned unexpected " + "code %d for pid %d\n", code, (int)pid ); + ec = GPG_ERR_GENERAL; + break; + } + + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +void +gnupg_release_process (pid_t pid) +{ + if (pid != (pid_t)INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE)pid; + + CloseHandle (process); + } +} + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. All standard file descriptors are + connected to /dev/null. */ +gpg_error_t +gnupg_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + gpg_error_t err; + char *cmdline; + PROCESS_INFORMATION pi = {NULL }; + + (void)envp; + + /* Build the command line. */ + err = build_w32_commandline (argv, 0, 0, 0, &cmdline); + if (err) + return err; + + /* Note: There is no detached flag under CE. */ + log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); + if (!create_process (pgmname, cmdline, &pi)) + { + log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +gnupg_kill_process (pid_t pid) +{ + if (pid != (pid_t) INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE) pid; + + /* Arbitrary error code. */ + TerminateProcess (process, 1); + } +} diff --git a/common/exechelp.h b/common/exechelp.h new file mode 100644 index 0000000..6f2653b --- /dev/null +++ b/common/exechelp.h @@ -0,0 +1,199 @@ +/* exechelp.h - Definitions for the fork and exec helpers + * Copyright (C) 2004, 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_EXECHELP_H +#define GNUPG_COMMON_EXECHELP_H + + +/* Return the maximum number of currently allowed file descriptors. + Only useful on POSIX systems. */ +int get_max_fds (void); + + +/* Close all file descriptors starting with descriptor FIRST. If + EXCEPT is not NULL, it is expected to be a list of file descriptors + which are not to close. This list shall be sorted in ascending + order with its end marked by -1. */ +void close_all_fds (int first, int *except); + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO accordingly. */ +int *get_all_open_fds (void); + + +/* Portable function to create a pipe. Under Windows the write end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t gnupg_create_inbound_pipe (int filedes[2], + estream_t *r_fp, int nonblock); + +/* Portable function to create a pipe. Under Windows the read end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t gnupg_create_outbound_pipe (int filedes[2], + estream_t *r_fp, int nonblock); + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t gnupg_create_pipe (int filedes[2]); + + +#define GNUPG_SPAWN_NONBLOCK 16 +#define GNUPG_SPAWN_RUN_ASFW 64 +#define GNUPG_SPAWN_DETACHED 128 + + +/* Fork and exec the program PGMNAME. + + If R_INFP is NULL connect stdin of the new process to /dev/null; if + it is not NULL store the address of a pointer to a new estream + there. If R_OUTFP is NULL connect stdout of the new process to + /dev/null; if it is not NULL store the address of a pointer to a + new estream there. If R_ERRFP is NULL connect stderr of the new + process to /dev/null; if it is not NULL store the address of a + pointer to a new estream there. On success the pid of the new + process is stored at PID. On error -1 is stored at PID and if + R_OUTFP or R_ERRFP are not NULL, NULL is stored there. + + The arguments for the process are expected in the NULL terminated + array ARGV. The program name itself should not be included there. + If PREEXEC is not NULL, the given function will be called right + before the exec. + + IF EXCEPT is not NULL, it is expected to be an ordered list of file + descriptors, terminated by an entry with the value (-1). These + file descriptors won't be closed before spawning a new program. + + Returns 0 on success or an error code. Calling gnupg_wait_process + and gnupg_release_process is required if the function succeeded. + + FLAGS is a bit vector: + + GNUPG_SPAWN_NONBLOCK + If set the two output streams are created in non-blocking + mode and the input stream is switched to non-blocking mode. + This is merely a convenience feature because the caller + could do the same with gpgrt_set_nonblock. Does not yet + work for Windows. + + GNUPG_SPAWN_DETACHED + If set the process will be started as a background process. + This flag is only useful under W32 (but not W32CE) systems, + so that no new console is created and pops up a console + window when starting the server. Does not work on W32CE. + + GNUPG_SPAWN_RUN_ASFW + On W32 (but not on W32CE) run AllowSetForegroundWindow for + the child. Note that due to unknown problems this actually + allows SetForegroundWindow for all childs of this process. + + */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *execpt, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid); + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process and + gnupg_release_process is required. Returns 0 on success or an + error code. */ +gpg_error_t gnupg_spawn_process_fd (const char *pgmname, + const char *argv[], + int infd, int outfd, int errfd, + pid_t *pid); + + +/* If HANG is true, waits for the process identified by PID to exit; + if HANG is false, checks whether the process has terminated. + PGMNAME should be the same as supplied to the spawn function and is + only used for diagnostics. Return values: + + 0 + The process exited successful. 0 is stored at R_EXITCODE. + + GPG_ERR_GENERAL + The process exited without success. The exit code of process + is then stored at R_EXITCODE. An exit code of -1 indicates + that the process terminated abnormally (e.g. due to a signal). + + GPG_ERR_TIMEOUT + The process is still running (returned only if HANG is false). + + GPG_ERR_INV_VALUE + An invalid PID has been specified. + + Other error codes may be returned as well. Unless otherwise noted, + -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL + if the exit code is not required (in that case an error messge will + be printed). Note that under Windows PID is not the process id but + the handle of the process. */ +gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang, + int *r_exitcode); + +/* Like gnupg_wait_process, but for COUNT processes. */ +gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids, + size_t count, int hang, int *r_exitcodes); + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void gnupg_kill_process (pid_t pid); + +/* Release the process identified by PID. This function is actually + only required for Windows but it does not harm to always call it. + It is a nop if PID is invalid. */ +void gnupg_release_process (pid_t pid); + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. */ +gpg_error_t gnupg_spawn_process_detached (const char *pgmname, + const char *argv[], + const char *envp[] ); + + + +#endif /*GNUPG_COMMON_EXECHELP_H*/ diff --git a/common/exectool.c b/common/exectool.c new file mode 100644 index 0000000..4593abd --- /dev/null +++ b/common/exectool.c @@ -0,0 +1,629 @@ +/* exectool.c - Utility functions to execute a helper tool + * Copyright (C) 2015 Werner Koch + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "i18n.h" +#include "logging.h" +#include "membuf.h" +#include "mischelp.h" +#include "exechelp.h" +#include "sysutils.h" +#include "util.h" +#include "exectool.h" + +typedef struct +{ + const char *pgmname; + exec_tool_status_cb_t status_cb; + void *status_cb_value; + int cont; + size_t used; + size_t buffer_size; + char *buffer; +} read_and_log_buffer_t; + + +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + + +static void +read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr) +{ + gpg_error_t err; + int c; + + if (!fderr) + { + /* Flush internal buffer. */ + if (state->used) + { + const char *pname; + int len; + + state->buffer[state->used] = 0; + state->used = 0; + + pname = strrchr (state->pgmname, '/'); + if (pname && pname != state->pgmname && pname[1]) + pname++; + else + pname = state->pgmname; + len = strlen (pname); + + if (state->status_cb + && !strncmp (state->buffer, "[GNUPG:] ", 9) + && state->buffer[9] >= 'A' && state->buffer[9] <= 'Z') + { + char *rest; + + rest = strchr (state->buffer + 9, ' '); + if (!rest) + { + /* Set REST to an empty string. */ + rest = state->buffer + strlen (state->buffer); + } + else + { + *rest++ = 0; + trim_spaces (rest); + } + state->status_cb (state->status_cb_value, + state->buffer + 9, rest); + } + else if (!state->cont + && !strncmp (state->buffer, pname, len) + && strlen (state->buffer) > strlen (pname) + && state->buffer[len] == ':' ) + { + /* PGMNAME plus colon is identical to the start of + the output: print only the output. */ + log_info ("%s\n", state->buffer); + } + else + log_info ("%s%c %s\n", + pname, state->cont? '+':':', state->buffer); + } + state->cont = 0; + return; + } + for (;;) + { + c = es_fgetc (fderr->stream); + if (c == EOF) + { + if (es_feof (fderr->stream)) + { + fderr->ignore = 1; /* Not anymore needed. */ + } + else if (es_ferror (fderr->stream)) + { + err = my_error_from_syserror (); + log_error ("error reading stderr of '%s': %s\n", + state->pgmname, gpg_strerror (err)); + fderr->ignore = 1; /* Disable. */ + } + + break; + } + else if (c == '\n') + { + read_and_log_stderr (state, NULL); + } + else + { + if (state->used >= state->buffer_size - 1) + { + if (state->status_cb) + { + /* A status callback requires that we have a full + * line. Thus we need to enlarget the buffer in + * this case. */ + char *newbuffer; + size_t newsize = state->buffer_size + 256; + + newbuffer = xtrymalloc (newsize); + if (!newbuffer) + { + log_error ("error allocating memory for status cb: %s\n", + gpg_strerror (my_error_from_syserror ())); + /* We better disable the status CB in this case. */ + state->status_cb = NULL; + read_and_log_stderr (state, NULL); + state->cont = 1; + } + else + { + memcpy (newbuffer, state->buffer, state->used); + xfree (state->buffer); + state->buffer = newbuffer; + state->buffer_size = newsize; + } + } + else + { + read_and_log_stderr (state, NULL); + state->cont = 1; + } + } + state->buffer[state->used++] = c; + } + } +} + + + +/* A buffer to copy from one stream to another. */ +struct copy_buffer +{ + char buffer[4096]; + char *writep; + size_t nread; +}; + + +/* Initialize a copy buffer. */ +static void +copy_buffer_init (struct copy_buffer *c) +{ + c->writep = c->buffer; + c->nread = 0; +} + + +/* Securely wipe a copy buffer. */ +static void +copy_buffer_shred (struct copy_buffer *c) +{ + if (c == NULL) + return; + wipememory (c->buffer, sizeof c->buffer); + c->writep = NULL; + c->nread = ~0U; +} + + +/* Copy data from SOURCE to SINK using copy buffer C. */ +static gpg_error_t +copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink) +{ + gpg_error_t err; + size_t nwritten = 0; + + if (c->nread == 0) + { + c->writep = c->buffer; + err = es_read (source, c->buffer, sizeof c->buffer, &c->nread); + if (err) + { + if (errno == EAGAIN) + return 0; /* We will just retry next time. */ + + return my_error_from_syserror (); + } + + assert (c->nread <= sizeof c->buffer); + } + + if (c->nread == 0) + return 0; /* Done copying. */ + + + nwritten = 0; + err = sink? es_write (sink, c->writep, c->nread, &nwritten) : 0; + + assert (nwritten <= c->nread); + c->writep += nwritten; + c->nread -= nwritten; + assert (c->writep - c->buffer <= sizeof c->buffer); + + if (err) + { + if (errno == EAGAIN) + return 0; /* We will just retry next time. */ + + return my_error_from_syserror (); + } + + if (sink && es_fflush (sink) && errno != EAGAIN) + err = my_error_from_syserror (); + + return err; +} + + +/* Flush the remaining data to SINK. */ +static gpg_error_t +copy_buffer_flush (struct copy_buffer *c, estream_t sink) +{ + gpg_error_t err; + + while (c->nread > 0) + { + err = copy_buffer_do_copy (c, NULL, sink); + if (err) + return err; + } + + return 0; +} + + + +/* Run the program PGMNAME with the command line arguments given in + * the NULL terminates array ARGV. If INPUT is not NULL it will be + * fed to stdin of the process. stderr is logged using log_info and + * the process' stdout is written to OUTPUT. If OUTPUT is NULL the + * output is discarded. If INEXTRA is given, an additional input + * stream will be passed to the child; to tell the child about this + * ARGV is scanned and the first occurrence of an argument + * "-&@INEXTRA@" is replaced by the concatenation of "-&" and the + * child's file descriptor of the pipe created for the INEXTRA stream. + * + * On error a diagnostic is printed and an error code returned. */ +gpg_error_t +gnupg_exec_tool_stream (const char *pgmname, const char *argv[], + estream_t input, estream_t inextra, + estream_t output, + exec_tool_status_cb_t status_cb, + void *status_cb_value) +{ + gpg_error_t err; + pid_t pid = (pid_t) -1; + estream_t infp = NULL; + estream_t extrafp = NULL; + estream_t outfp = NULL, errfp = NULL; + es_poll_t fds[4]; + int exceptclose[2]; + int extrapipe[2] = {-1, -1}; + char extrafdbuf[20]; + const char *argsave = NULL; + int argsaveidx; + int count; + read_and_log_buffer_t fderrstate; + struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL; + + memset (fds, 0, sizeof fds); + memset (&fderrstate, 0, sizeof fderrstate); + + cpbuf_in = xtrymalloc (sizeof *cpbuf_in); + if (cpbuf_in == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + copy_buffer_init (cpbuf_in); + + cpbuf_out = xtrymalloc (sizeof *cpbuf_out); + if (cpbuf_out == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + copy_buffer_init (cpbuf_out); + + cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra); + if (cpbuf_extra == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + copy_buffer_init (cpbuf_extra); + + fderrstate.pgmname = pgmname; + fderrstate.status_cb = status_cb; + fderrstate.status_cb_value = status_cb_value; + fderrstate.buffer_size = 256; + fderrstate.buffer = xtrymalloc (fderrstate.buffer_size); + if (!fderrstate.buffer) + { + err = my_error_from_syserror (); + goto leave; + } + + if (inextra) + { + err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1); + if (err) + { + log_error ("error running outbound pipe for extra fp: %s\n", + gpg_strerror (err)); + goto leave; + } + exceptclose[0] = extrapipe[0]; /* Do not close in child. */ + exceptclose[1] = -1; + /* Now find the argument marker and replace by the pipe's fd. + Yeah, that is an ugly non-thread safe hack but it safes us to + create a copy of the array. */ + snprintf (extrafdbuf, sizeof extrafdbuf, "-&%d", extrapipe[0]); + for (argsaveidx=0; argv[argsaveidx]; argsaveidx++) + if (!strcmp (argv[argsaveidx], "-&@INEXTRA@")) + { + argsave = argv[argsaveidx]; + argv[argsaveidx] = extrafdbuf; + break; + } + } + else + exceptclose[0] = -1; + + err = gnupg_spawn_process (pgmname, argv, + exceptclose, NULL, GNUPG_SPAWN_NONBLOCK, + input? &infp : NULL, + &outfp, &errfp, &pid); + if (extrapipe[0] != -1) + close (extrapipe[0]); + if (argsave) + argv[argsaveidx] = argsave; + if (err) + { + log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); + goto leave; + } + + fds[0].stream = infp; + fds[0].want_write = 1; + if (!input) + fds[0].ignore = 1; + fds[1].stream = outfp; + fds[1].want_read = 1; + fds[2].stream = errfp; + fds[2].want_read = 1; + fds[3].stream = extrafp; + fds[3].want_write = 1; + if (!inextra) + fds[3].ignore = 1; + + /* Now read as long as we have something to poll. We continue + reading even after EOF or error on stdout so that we get the + other error messages or remaining outut. */ + while (! (fds[1].ignore && fds[2].ignore)) + { + count = es_poll (fds, DIM(fds), -1); + if (count == -1) + { + err = my_error_from_syserror (); + log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err)); + goto leave; + } + if (!count) + { + log_debug ("unexpected timeout while polling '%s'\n", pgmname); + break; + } + + if (fds[0].got_write) + { + err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream); + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + if (es_feof (input)) + { + err = copy_buffer_flush (cpbuf_in, fds[0].stream); + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + fds[0].ignore = 1; /* ready. */ + es_fclose (infp); infp = NULL; + } + } + + if (fds[3].got_write) + { + log_assert (inextra); + err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream); + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + if (es_feof (inextra)) + { + err = copy_buffer_flush (cpbuf_extra, fds[3].stream); + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + fds[3].ignore = 1; /* ready. */ + es_fclose (extrafp); extrafp = NULL; + } + } + + if (fds[1].got_read) + { + err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output); + if (err) + { + log_error ("error reading data from '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + if (es_feof (fds[1].stream)) + { + err = copy_buffer_flush (cpbuf_out, output); + if (err) + { + log_error ("error reading data from '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + fds[1].ignore = 1; /* ready. */ + } + } + + if (fds[2].got_read) + read_and_log_stderr (&fderrstate, fds + 2); + } + + read_and_log_stderr (&fderrstate, NULL); /* Flush. */ + es_fclose (infp); infp = NULL; + es_fclose (extrafp); extrafp = NULL; + es_fclose (outfp); outfp = NULL; + es_fclose (errfp); errfp = NULL; + + err = gnupg_wait_process (pgmname, pid, 1, NULL); + pid = (pid_t)(-1); + + leave: + if (err && pid != (pid_t) -1) + gnupg_kill_process (pid); + + es_fclose (infp); + es_fclose (extrafp); + es_fclose (outfp); + es_fclose (errfp); + if (pid != (pid_t)(-1)) + gnupg_wait_process (pgmname, pid, 1, NULL); + gnupg_release_process (pid); + + copy_buffer_shred (cpbuf_in); + xfree (cpbuf_in); + copy_buffer_shred (cpbuf_out); + xfree (cpbuf_out); + copy_buffer_shred (cpbuf_extra); + xfree (cpbuf_extra); + xfree (fderrstate.buffer); + return err; +} + + +/* A dummy free function to pass to 'es_mopen'. */ +static void +nop_free (void *ptr) +{ + (void) ptr; +} + +/* Run the program PGMNAME with the command line arguments given in + the NULL terminates array ARGV. If INPUT_STRING is not NULL it + will be fed to stdin of the process. stderr is logged using + log_info and the process' stdout is returned in a newly malloced + buffer RESULT with the length stored at RESULTLEN if not given as + NULL. A hidden Nul is appended to the output. On error NULL is + stored at RESULT, a diagnostic is printed, and an error code + returned. */ +gpg_error_t +gnupg_exec_tool (const char *pgmname, const char *argv[], + const char *input_string, + char **result, size_t *resultlen) +{ + gpg_error_t err; + estream_t input = NULL; + estream_t output; + size_t len; + size_t nread; + + *result = NULL; + if (resultlen) + *resultlen = 0; + + if (input_string) + { + len = strlen (input_string); + input = es_mopen ((char *) input_string, len, len, + 0 /* don't grow */, NULL, nop_free, "rb"); + if (! input) + return my_error_from_syserror (); + } + + output = es_fopenmem (0, "wb"); + if (! output) + { + err = my_error_from_syserror (); + goto leave; + } + + err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL); + if (err) + goto leave; + + len = es_ftello (output); + err = es_fseek (output, 0, SEEK_SET); + if (err) + goto leave; + + *result = xtrymalloc (len + 1); + if (!*result) + { + err = my_error_from_syserror (); + goto leave; + } + + if (len) + { + err = es_read (output, *result, len, &nread); + if (err) + goto leave; + if (nread != len) + log_fatal ("%s: short read from memstream\n", __func__); + } + (*result)[len] = 0; + + if (resultlen) + *resultlen = len; + + leave: + es_fclose (input); + es_fclose (output); + if (err) + { + xfree (*result); + *result = NULL; + } + return err; +} diff --git a/common/exectool.h b/common/exectool.h new file mode 100644 index 0000000..27bbfc9 --- /dev/null +++ b/common/exectool.h @@ -0,0 +1,69 @@ +/* sh-exectool.h - Utility functions to execute a helper tool + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_EXECTOOL_H +#define GNUPG_COMMON_EXECTOOL_H + +#include + +/* This callback can be used to process --status-fd outputs of GnuPG + * tools. OPAQUE can be used to communicate between the caller of the + * function and the callback. KEYWORD is the status keyword (see + * doc/DETAILS); it is never NULL. ARGS are the arguments of the + * status line and will also never be NULL; the caller may modify this + * string. */ +typedef void (*exec_tool_status_cb_t) (void *opaque, + const char *keyword, + char *args); + + +/* Run the program PGMNAME with the command line arguments given in + the NULL terminates array ARGV. If INPUT_STRING is not NULL it + will be fed to stdin of the process. stderr is logged using + log_info and the process' stdout is returned in a newly malloced + buffer RESULT with the length stored at RESULTLEN if not given as + NULL. A hidden Nul is appended to the output. On error NULL is + stored at RESULT, a diagnostic is printed, and an error code + returned. */ +gpg_error_t gnupg_exec_tool (const char *pgmname, const char *argv[], + const char *input_string, + char **result, size_t *resultlen); + +/* Run the program PGMNAME with the command line arguments given in + the NULL terminates array ARGV. If INPUT is not NULL it will be + fed to stdin of the process. stderr is logged using log_info and + the process' stdout is written to OUTPUT. On error a diagnostic is + printed, and an error code returned. INEXTRA is reserved. */ +gpg_error_t gnupg_exec_tool_stream (const char *pgmname, const char *argv[], + estream_t input, estream_t inextra, + estream_t output, + exec_tool_status_cb_t status_cb, + void *status_cb_value); + +#endif /* GNUPG_COMMON_EXECTOOL_H */ diff --git a/common/exstatus.awk b/common/exstatus.awk new file mode 100644 index 0000000..fb13819 --- /dev/null +++ b/common/exstatus.awk @@ -0,0 +1,39 @@ +# exstatus.awk - Extract status codes from status.h +# Copyright (C) 2007 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 3 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, see . + +BEGIN { + print "# Created by exstatus.awk - DO NOT EDIT." + topheader = 0; + code = 0; +} + +topheader == 0 && /^\/\*/ { topheader = 1 } +topheader == 1 { print $0 } +topheader == 1 && /\*\// { topheader = 2; print "" } + +/^[ \t]+STATUS_[A-Za-z_]+/ { + sub (/[,\/\*]+/, "", $1); + desc = substr($1,8); + printf "%d\t%s\t%s\n", code, $1, desc; + code++; +} + + +END { + print "# end of status codes." +} diff --git a/common/fwddecl.h b/common/fwddecl.h new file mode 100644 index 0000000..b945406 --- /dev/null +++ b/common/fwddecl.h @@ -0,0 +1,39 @@ +/* fwddecl.h - Forward declarations + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_FWDDECL_H +#define GNUPG_COMMON_FWDDECL_H + + +/*-- Forward declaration of the commonly used server control structure. */ +struct server_control_s; +typedef struct server_control_s *ctrl_t; + + +#endif /*GNUPG_COMMON_FWDDECL_H*/ diff --git a/common/gc-opt-flags.h b/common/gc-opt-flags.h new file mode 100644 index 0000000..11ecec0 --- /dev/null +++ b/common/gc-opt-flags.h @@ -0,0 +1,46 @@ +/* gc-opt-flags.h - gpgconf constants used by the backends. + * Copyright (C) 2004, 2007 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. + */ + +#ifndef GNUPG_GC_OPT_FLAGS_H +#define GNUPG_GC_OPT_FLAGS_H + +/* Public option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE + EXISTING FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. See + gnupg/tools/gpgconf-comp.c for details. */ + +#define GC_OPT_FLAG_NONE 0UL + +/* The RUNTIME flag for an option indicates that the option can be + changed at runtime. */ +#define GC_OPT_FLAG_RUNTIME (1UL << 3) + +/* 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) + +/* The NO_CHANGE flag for an option indicates that the user should not + be allowed to change this option using the standard gpgconf method. + Frontends using gpgconf should grey out such options, so that only + the current value is displayed. */ +#define GC_OPT_FLAG_NO_CHANGE (1UL <<7) + + +#endif /*GNUPG_GC_OPT_FLAGS_H*/ diff --git a/common/get-passphrase.c b/common/get-passphrase.c new file mode 100644 index 0000000..c24b40e --- /dev/null +++ b/common/get-passphrase.c @@ -0,0 +1,255 @@ +/* get-passphrase.c - Ask for a passphrase via the agent + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "i18n.h" +#include "asshelp.h" +#include "membuf.h" +#include "sysutils.h" +#include "get-passphrase.h" + +/* The context used by this process to ask for the passphrase. */ +static assuan_context_t agent_ctx; +static struct +{ + gpg_err_source_t errsource; + int verbosity; + const char *agent_program; + const char *lc_ctype; + const char *lc_messages; + session_env_t session_env; + const char *pinentry_user_data; +} agentargs; + + +/* Set local variable to be used for a possible agent startup. Note + that the strings are just pointers and should not anymore be + modified by the caller. */ +void +gnupg_prepare_get_passphrase (gpg_err_source_t errsource, + int verbosity, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env) +{ + agentargs.errsource = errsource; + agentargs.verbosity = verbosity; + agentargs.agent_program = agent_program; + agentargs.lc_ctype = opt_lc_ctype; + agentargs.lc_messages = opt_lc_messages; + agentargs.session_env = session_env; +} + + +/* Try to connect to the agent via socket or fork it off and work by + pipes. Handle the server's initial greeting. */ +static gpg_error_t +start_agent (void) +{ + gpg_error_t err; + + /* Fixme: This code is not thread safe, thus we don't build it with + pth. We will need a context for each thread or serialize the + access to the agent. */ + if (agent_ctx) + return 0; + + err = start_new_gpg_agent (&agent_ctx, + agentargs.errsource, + agentargs.agent_program, + agentargs.lc_ctype, + agentargs.lc_messages, + agentargs.session_env, + 1, agentargs.verbosity, 0, NULL, NULL); + if (!err) + { + /* Tell the agent that we support Pinentry notifications. No + error checking so that it will work with older agents. */ + assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", + NULL, NULL, NULL, NULL, NULL, NULL); + } + + return err; +} + + +/* This is the default inquiry callback. It merely handles the + Pinentry notification. */ +static gpg_error_t +default_inq_cb (void *opaque, const char *line) +{ + (void)opaque; + + if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) + { + gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); + /* We do not return errors to avoid breaking other code. */ + } + else + log_debug ("ignoring gpg-agent inquiry '%s'\n", line); + + return 0; +} + + +/* Ask for a passphrase via gpg-agent. On success the caller needs to + free the string stored at R_PASSPHRASE. On error NULL will be + stored at R_PASSPHRASE and an appropriate gpg error code is + returned. With REPEAT set to 1, gpg-agent will ask the user to + repeat the just entered passphrase. CACHE_ID is a gpg-agent style + passphrase cache id or NULL. ERR_MSG is a error message to be + presented to the user (e.g. "bad passphrase - try again") or NULL. + PROMPT is the prompt string to label the entry box, it may be NULL + for a default one. DESC_MSG is a longer description to be + displayed above the entry box, if may be NULL for a default one. + If USE_SECMEM is true, the returned passphrase is returned in + secure memory. The length of all these strings is limited; they + need to fit in their encoded form into a standard Assuan line (i.e + less then about 950 characters). All strings shall be UTF-8. */ +gpg_error_t +gnupg_get_passphrase (const char *cache_id, + const char *err_msg, + const char *prompt, + const char *desc_msg, + int repeat, + int check_quality, + int use_secmem, + char **r_passphrase) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + const char *arg1 = NULL; + char *arg2 = NULL; + char *arg3 = NULL; + char *arg4 = NULL; + membuf_t data; + + *r_passphrase = NULL; + + err = start_agent (); + if (err) + return err; + + /* Check that the gpg-agent understands the repeat option. */ + if (assuan_transact (agent_ctx, + "GETINFO cmd_has_option GET_PASSPHRASE repeat", + NULL, NULL, NULL, NULL, NULL, NULL)) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + arg1 = cache_id && *cache_id? cache_id:NULL; + if (err_msg && *err_msg) + if (!(arg2 = percent_plus_escape (err_msg))) + goto no_mem; + if (prompt && *prompt) + if (!(arg3 = percent_plus_escape (prompt))) + goto no_mem; + if (desc_msg && *desc_msg) + if (!(arg4 = percent_plus_escape (desc_msg))) + goto no_mem; + + snprintf (line, DIM(line), + "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s", + check_quality? "--check ":"", + repeat, + arg1? arg1:"X", + arg2? arg2:"X", + arg3? arg3:"X", + arg4? arg4:"X"); + xfree (arg2); + xfree (arg3); + xfree (arg4); + + if (use_secmem) + init_membuf_secure (&data, 64); + else + init_membuf (&data, 64); + err = assuan_transact (agent_ctx, line, + put_membuf_cb, &data, + default_inq_cb, NULL, NULL, NULL); + + /* Older Pinentries return the old assuan error code for canceled + which gets translated by libassuan to GPG_ERR_ASS_CANCELED and + not to the code for a user cancel. Fix this here. */ + if (err && gpg_err_source (err) + && gpg_err_code (err) == GPG_ERR_ASS_CANCELED) + err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); + + if (err) + { + void *p; + size_t n; + + p = get_membuf (&data, &n); + if (p) + wipememory (p, n); + xfree (p); + } + else + { + put_membuf (&data, "", 1); + *r_passphrase = get_membuf (&data, NULL); + if (!*r_passphrase) + err = gpg_error_from_syserror (); + } + return err; + no_mem: + err = gpg_error_from_syserror (); + xfree (arg2); + xfree (arg3); + xfree (arg4); + return err; +} + + +/* Flush the passphrase cache with Id CACHE_ID. */ +gpg_error_t +gnupg_clear_passphrase (const char *cache_id) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + + if (!cache_id || !*cache_id) + return 0; + + err = start_agent (); + if (err) + return err; + + snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id); + return assuan_transact (agent_ctx, line, NULL, NULL, + default_inq_cb, NULL, NULL, NULL); +} diff --git a/common/get-passphrase.h b/common/get-passphrase.h new file mode 100644 index 0000000..afdbe78 --- /dev/null +++ b/common/get-passphrase.h @@ -0,0 +1,54 @@ +/* get-passphrase.h - Definitions to ask for a passphrase via the agent. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_GET_PASSPHRASE_H +#define GNUPG_COMMON_GET_PASSPHRASE_H + +#include "session-env.h" + +void gnupg_prepare_get_passphrase (gpg_err_source_t errsource, + int verbosity, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env); + +gpg_error_t gnupg_get_passphrase (const char *cache_id, + const char *err_msg, + const char *prompt, + const char *desc_msg, + int repeat, + int check_quality, + int use_secmem, + char **r_passphrase); + +gpg_error_t gnupg_clear_passphrase (const char *cache_id); + + +#endif /*GNUPG_COMMON_GET_PASSPHRASE_H*/ diff --git a/common/gettime.c b/common/gettime.c new file mode 100644 index 0000000..e5da4fb --- /dev/null +++ b/common/gettime.c @@ -0,0 +1,993 @@ +/* gettime.c - Wrapper for time functions + * Copyright (C) 1998, 2002, 2007, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#ifdef HAVE_LANGINFO_H +#include +#endif + +#include "util.h" +#include "i18n.h" +#include "gettime.h" + +#ifdef HAVE_UNSIGNED_TIME_T +# define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1)) +#else + /* Error or 32 bit time_t and value after 2038-01-19. */ +# define IS_INVALID_TIME_T(a) ((a) < 0) +#endif + + +static unsigned long timewarp; +static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode; + +/* Correction used to map to real Julian days. */ +#define JD_DIFF 1721060L + + +/* Wrapper for the time(3). We use this here so we can fake the time + for tests */ +time_t +gnupg_get_time () +{ + time_t current = time (NULL); + if (current == (time_t)(-1)) + log_fatal ("time() failed\n"); + + if (timemode == NORMAL) + return current; + else if (timemode == FROZEN) + return timewarp; + else if (timemode == FUTURE) + return current + timewarp; + else + return current - timewarp; +} + + +/* Wrapper around gmtime_r. + + On systems without gmtime_r this implementation works within gnupg + because we use only one thread a time. FIXME: An independent + library may use gmtime in one of its own thread (or via + npth_enter/npth_leave) - in this case we run into a problem. The + solution would be to use a mutex here. */ +struct tm * +gnupg_gmtime (const time_t *timep, struct tm *result) +{ +#ifdef HAVE_GMTIME_R + return gmtime_r (timep, result); +#else + struct tm *tp; + + tp = gmtime (timep); + if (tp) + memcpy (result, tp, sizeof *result); + return tp; +#endif +} + + +/* Return the current time (possibly faked) in ISO format. */ +void +gnupg_get_isotime (gnupg_isotime_t timebuf) +{ + time_t atime = gnupg_get_time (); + struct tm *tp; + struct tm tmbuf; + + tp = gnupg_gmtime (&atime, &tmbuf); + if (!tp) + *timebuf = 0; + else + snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d", + 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); +} + + +/* Set the time to NEWTIME so that gnupg_get_time returns a time + starting with this one. With FREEZE set to 1 the returned time + will never change. Just for completeness, a value of (time_t)-1 + for NEWTIME gets you back to reality. Note that this is obviously + not thread-safe but this is not required. */ +void +gnupg_set_time (time_t newtime, int freeze) +{ + time_t current = time (NULL); + + if ( newtime == (time_t)-1 || current == newtime) + { + timemode = NORMAL; + timewarp = 0; + } + else if (freeze) + { + timemode = FROZEN; + timewarp = current; + } + else if (newtime > current) + { + timemode = FUTURE; + timewarp = newtime - current; + } + else + { + timemode = PAST; + timewarp = current - newtime; + } +} + +/* Returns true when we are in timewarp mode */ +int +gnupg_faked_time_p (void) +{ + return timemode; +} + + +/* This function is used by gpg because OpenPGP defines the timestamp + as an unsigned 32 bit value. */ +u32 +make_timestamp (void) +{ + time_t t = gnupg_get_time (); + return (u32)t; +} + + + +/**************** + * Scan a date string and return a timestamp. + * The only supported format is "yyyy-mm-dd" + * Returns 0 for an invalid date. + */ +u32 +scan_isodatestr( const char *string ) +{ + int year, month, day; + struct tm tmbuf; + time_t stamp; + int i; + + if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' ) + return 0; + for( i=0; i < 4; i++ ) + if( !digitp (string+i) ) + return 0; + if( !digitp (string+5) || !digitp(string+6) ) + return 0; + if( !digitp(string+8) || !digitp(string+9) ) + return 0; + year = atoi(string); + month = atoi(string+5); + day = atoi(string+8); + /* some basic checks */ + if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ) + return 0; + memset( &tmbuf, 0, sizeof tmbuf ); + tmbuf.tm_mday = day; + tmbuf.tm_mon = month-1; + tmbuf.tm_year = year - 1900; + tmbuf.tm_isdst = -1; + stamp = mktime( &tmbuf ); + if( stamp == (time_t)-1 ) + return 0; + return stamp; +} + + +int +isotime_p (const char *string) +{ + const char *s; + int i; + + if (!*string) + return 0; + for (s=string, i=0; i < 8; i++, s++) + if (!digitp (s)) + return 0; + if (*s != 'T') + return 0; + for (s++, i=9; i < 15; i++, s++) + if (!digitp (s)) + return 0; + if ( !(!*s || (isascii (*s) && isspace(*s)) || *s == ':' || *s == ',')) + return 0; /* Wrong delimiter. */ + + return 1; +} + + +/* Scan a string and return true if the string represents the human + readable format of an ISO time. This format is: + yyyy-mm-dd[ hh[:mm[:ss]]] + Scanning stops at the second space or at a comma. If DATE_ONLY is + true the time part is not expected and the scanning stops at the + first space or at a comma. */ +int +isotime_human_p (const char *string, int date_only) +{ + const char *s; + int i; + + if (!*string) + return 0; + for (s=string, i=0; i < 4; i++, s++) + if (!digitp (s)) + return 0; + if (*s != '-') + return 0; + s++; + if (!digitp (s) || !digitp (s+1) || s[2] != '-') + return 0; + i = atoi_2 (s); + if (i < 1 || i > 12) + return 0; + s += 3; + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 1 || i > 31) + return 0; + s += 2; + if (!*s || *s == ',') + return 1; /* Okay; only date given. */ + if (!spacep (s)) + return 0; + if (date_only) + return 1; /* Okay; only date was requested. */ + s++; + if (spacep (s)) + return 1; /* Okay, second space stops scanning. */ + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 0 || i > 23) + return 0; + s += 2; + if (!*s || *s == ',') + return 1; /* Okay; only date and hour given. */ + if (*s != ':') + return 0; + s++; + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 0 || i > 59) + return 0; + s += 2; + if (!*s || *s == ',') + return 1; /* Okay; only date, hour and minute given. */ + if (*s != ':') + return 0; + s++; + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 0 || i > 60) + return 0; + s += 2; + if (!*s || *s == ',' || spacep (s)) + return 1; /* Okay; date, hour and minute and second given. */ + + return 0; /* Unexpected delimiter. */ +} + +/* Convert a standard isotime or a human readable variant into an + isotime structure. The allowed formats are those described by + isotime_p and isotime_human_p. The function returns 0 on failure + or the length of the scanned string on success. */ +size_t +string2isotime (gnupg_isotime_t atime, const char *string) +{ + gnupg_isotime_t dummyatime; + + if (!atime) + atime = dummyatime; + + atime[0] = 0; + if (isotime_p (string)) + { + memcpy (atime, string, 15); + atime[15] = 0; + return 15; + } + if (!isotime_human_p (string, 0)) + return 0; + atime[0] = string[0]; + atime[1] = string[1]; + atime[2] = string[2]; + atime[3] = string[3]; + atime[4] = string[5]; + atime[5] = string[6]; + atime[6] = string[8]; + atime[7] = string[9]; + atime[8] = 'T'; + memset (atime+9, '0', 6); + atime[15] = 0; + if (!spacep (string+10)) + return 10; + if (spacep (string+11)) + return 11; /* As per def, second space stops scanning. */ + atime[9] = string[11]; + atime[10] = string[12]; + if (string[13] != ':') + return 13; + atime[11] = string[14]; + atime[12] = string[15]; + if (string[16] != ':') + return 16; + atime[13] = string[17]; + atime[14] = string[18]; + return 19; +} + + +/* Scan an ISO timestamp and return an Epoch based timestamp. The only + supported format is "yyyymmddThhmmss" delimited by white space, nul, a + colon or a comma. Returns (time_t)(-1) for an invalid string. */ +time_t +isotime2epoch (const char *string) +{ + int year, month, day, hour, minu, sec; + struct tm tmbuf; + + if (!isotime_p (string)) + return (time_t)(-1); + + year = atoi_4 (string); + month = atoi_2 (string + 4); + day = atoi_2 (string + 6); + hour = atoi_2 (string + 9); + minu = atoi_2 (string + 11); + sec = atoi_2 (string + 13); + + /* Basic checks. */ + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 + || hour > 23 || minu > 59 || sec > 61 ) + return (time_t)(-1); + + memset (&tmbuf, 0, sizeof tmbuf); + tmbuf.tm_sec = sec; + tmbuf.tm_min = minu; + tmbuf.tm_hour = hour; + tmbuf.tm_mday = day; + tmbuf.tm_mon = month-1; + tmbuf.tm_year = year - 1900; + tmbuf.tm_isdst = -1; + return timegm (&tmbuf); +} + + +/* Convert an Epoch time to an iso time stamp. */ +void +epoch2isotime (gnupg_isotime_t timebuf, time_t atime) +{ + if (atime == (time_t)(-1)) + *timebuf = 0; + else + { + struct tm *tp; +#ifdef HAVE_GMTIME_R + struct tm tmbuf; + + tp = gmtime_r (&atime, &tmbuf); +#else + tp = gmtime (&atime); +#endif + snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d", + 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + } +} + + +/* Parse a short ISO date string (YYYY-MM-DD) into a TM structure. + Returns 0 on success. */ +int +isodate_human_to_tm (const char *string, struct tm *t) +{ + int year, month, day; + + if (!isotime_human_p (string, 1)) + return -1; + + year = atoi_4 (string); + month = atoi_2 (string + 5); + day = atoi_2 (string + 8); + + /* Basic checks. */ + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31) + return -1; + + memset (t, 0, sizeof *t); + t->tm_sec = 0; + t->tm_min = 0; + t->tm_hour = 0; + t->tm_mday = day; + t->tm_mon = month-1; + t->tm_year = year - 1900; + t->tm_isdst = -1; + return 0; +} + + +/* This function is a copy of gpgme/src/conversion.c:_gpgme_timegm. + If you change it, then update the other one too. */ +#ifdef HAVE_W32_SYSTEM +static time_t +_win32_timegm (struct tm *tm) +{ + /* This one is thread safe. */ + SYSTEMTIME st; + FILETIME ft; + unsigned long long cnsecs; + + st.wYear = tm->tm_year + 1900; + st.wMonth = tm->tm_mon + 1; + st.wDay = tm->tm_mday; + st.wHour = tm->tm_hour; + st.wMinute = tm->tm_min; + st.wSecond = tm->tm_sec; + st.wMilliseconds = 0; /* Not available. */ + st.wDayOfWeek = 0; /* Ignored. */ + + /* System time is UTC thus the conversion is pretty easy. */ + if (!SystemTimeToFileTime (&st, &ft)) + { + gpg_err_set_errno (EINVAL); + return (time_t)(-1); + } + + cnsecs = (((unsigned long long)ft.dwHighDateTime << 32) + | ft.dwLowDateTime); + cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */ + return (time_t)(cnsecs / 10000000ULL); +} +#endif + + +/* Parse the string TIMESTAMP into a time_t. The string may either be + seconds since Epoch or in the ISO 8601 format like + "20390815T143012". Returns 0 for an empty string or seconds since + Epoch. Leading spaces are skipped. If ENDP is not NULL, it will + point to the next non-parsed character in TIMESTRING. + + This function is a copy of + gpgme/src/conversion.c:_gpgme_parse_timestamp. If you change it, + then update the other one too. */ +time_t +parse_timestamp (const char *timestamp, char **endp) +{ + /* Need to skip leading spaces, because that is what strtoul does + but not our ISO 8601 checking code. */ + while (*timestamp && *timestamp== ' ') + timestamp++; + if (!*timestamp) + return 0; + + if (strlen (timestamp) >= 15 && timestamp[8] == 'T') + { + struct tm buf; + int year; + + year = atoi_4 (timestamp); + if (year < 1900) + return (time_t)(-1); + + if (endp) + *endp = (char*)(timestamp + 15); + + /* Fixme: We would better use a configure test to see whether + mktime can handle dates beyond 2038. */ + if (sizeof (time_t) <= 4 && year >= 2038) + return (time_t)2145914603; /* 2037-12-31 23:23:23 */ + + memset (&buf, 0, sizeof buf); + buf.tm_year = year - 1900; + buf.tm_mon = atoi_2 (timestamp+4) - 1; + buf.tm_mday = atoi_2 (timestamp+6); + buf.tm_hour = atoi_2 (timestamp+9); + buf.tm_min = atoi_2 (timestamp+11); + buf.tm_sec = atoi_2 (timestamp+13); + +#ifdef HAVE_W32_SYSTEM + return _win32_timegm (&buf); +#else +#ifdef HAVE_TIMEGM + return timegm (&buf); +#else + { + time_t tim; + + putenv ("TZ=UTC"); + tim = mktime (&buf); +#ifdef __GNUC__ +#warning fixme: we must somehow reset TZ here. It is not threadsafe anyway. +#endif + return tim; + } +#endif /* !HAVE_TIMEGM */ +#endif /* !HAVE_W32_SYSTEM */ + } + else + return (time_t)strtoul (timestamp, endp, 10); +} + + + +u32 +add_days_to_timestamp( u32 stamp, u16 days ) +{ + return stamp + days*86400L; +} + + +/**************** + * Return a string with a time value in the form: x Y, n D, n H + */ + +const char * +strtimevalue( u32 value ) +{ + static char buffer[30]; + unsigned int years, days, hours, minutes; + + value /= 60; + minutes = value % 60; + value /= 60; + hours = value % 24; + value /= 24; + days = value % 365; + value /= 365; + years = value; + + sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes ); + if( years ) + return buffer; + if( days ) + return strchr( buffer, 'y' ) + 1; + return strchr( buffer, 'd' ) + 1; +} + + + +/* Return a malloced string with the time elapsed between NOW and + SINCE. May return NULL on error. */ +char * +elapsed_time_string (time_t since, time_t now) +{ + char *result; + double diff; + unsigned long value; + unsigned int days, hours, minutes, seconds; + + if (!now) + now = gnupg_get_time (); + + diff = difftime (now, since); + if (diff < 0) + return xtrystrdup ("time-warp"); + + seconds = (unsigned long)diff % 60; + value = (unsigned long)(diff / 60); + minutes = value % 60; + value /= 60; + hours = value % 24; + value /= 24; + days = value % 365; + + if (days) + result = xtryasprintf ("%ud%uh%um%us", days, hours, minutes, seconds); + else if (hours) + result = xtryasprintf ("%uh%um%us", hours, minutes, seconds); + else if (minutes) + result = xtryasprintf ("%um%us", minutes, seconds); + else + result = xtryasprintf ("%us", seconds); + + return result; +} + + +/* + * Note: this function returns GMT + */ +const char * +strtimestamp (u32 stamp) +{ + static char buffer[11+5]; + struct tm *tp; + time_t atime = stamp; + + if (IS_INVALID_TIME_T (atime)) + { + strcpy (buffer, "????" "-??" "-??"); + } + else + { + tp = gmtime( &atime ); + snprintf (buffer, sizeof buffer, "%04d-%02d-%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); + } + return buffer; +} + + +/* + * Note: this function returns GMT + */ +const char * +isotimestamp (u32 stamp) +{ + static char buffer[25+5]; + struct tm *tp; + time_t atime = stamp; + + if (IS_INVALID_TIME_T (atime)) + { + strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??"); + } + else + { + tp = gmtime ( &atime ); + snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + } + return buffer; +} + + +/**************** + * Note: this function returns local time + */ +const char * +asctimestamp (u32 stamp) +{ + static char buffer[50]; +#if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO) + static char fmt[50]; +#endif + struct tm *tp; + time_t atime = stamp; + + if (IS_INVALID_TIME_T (atime)) + { + strcpy (buffer, "????" "-??" "-??"); + return buffer; + } + + tp = localtime( &atime ); +#ifdef HAVE_STRFTIME +# if defined(HAVE_NL_LANGINFO) + mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 ); + if (!strstr( fmt, "%Z" )) + strcat( fmt, " %Z"); + /* NOTE: gcc -Wformat-noliteral will complain here. I have found no + way to suppress this warning. */ + strftime (buffer, DIM(buffer)-1, fmt, tp); +# elif defined(HAVE_W32CE_SYSTEM) + /* tzset is not available but %Z nevertheless prints a default + nonsense timezone ("WILDABBR"). Thus we don't print the time + zone at all. */ + strftime (buffer, DIM(buffer)-1, "%c", tp); +# else + /* FIXME: we should check whether the locale appends a " %Z" These + * locales from glibc don't put the " %Z": fi_FI hr_HR ja_JP lt_LT + * lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN. */ + strftime (buffer, DIM(buffer)-1, "%c %Z", tp); +# endif + buffer[DIM(buffer)-1] = 0; +#else + mem2str( buffer, asctime(tp), DIM(buffer) ); +#endif + return buffer; +} + + +/* Return the timestamp STAMP in RFC-2822 format. This is always done + * in the C locale. We return the gmtime to avoid computing the + * timezone. The caller must release the returned string. + * + * Example: "Mon, 27 Jun 2016 1:42:00 +0000". + */ +char * +rfctimestamp (u32 stamp) +{ + time_t atime = stamp; + struct tm tmbuf, *tp; + + + if (IS_INVALID_TIME_T (atime)) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + tp = gnupg_gmtime (&atime, &tmbuf); + if (!tp) + return NULL; + return xtryasprintf ("%.3s, %02d %.3s %04d %02d:%02d:%02d +0000", + &"SunMonTueWedThuFriSat"[(tp->tm_wday%7)*3], + tp->tm_mday, + &"JanFebMarAprMayJunJulAugSepOctNovDec" + [(tp->tm_mon%12)*3], + tp->tm_year + 1900, + tp->tm_hour, + tp->tm_min, + tp->tm_sec); +} + + +static int +days_per_year (int y) +{ + int s ; + + s = !(y % 4); + if ( !(y % 100)) + if ((y%400)) + s = 0; + return s ? 366 : 365; +} + +static int +days_per_month (int y, int m) +{ + int s; + + switch(m) + { + case 1: case 3: case 5: case 7: case 8: case 10: case 12: + return 31 ; + case 2: + s = !(y % 4); + if (!(y % 100)) + if ((y % 400)) + s = 0; + return s? 29 : 28 ; + case 4: case 6: case 9: case 11: + return 30; + } + BUG(); +} + + +/* Convert YEAR, MONTH and DAY into the Julian date. We assume that + it is already noon. We do not support dates before 1582-10-15. */ +static unsigned long +date2jd (int year, int month, int day) +{ + unsigned long jd; + + jd = 365L * year + 31 * (month-1) + day + JD_DIFF; + if (month < 3) + year-- ; + else + jd -= (4 * month + 23) / 10; + + jd += year / 4 - ((year / 100 + 1) *3) / 4; + + return jd ; +} + +/* Convert a Julian date back to YEAR, MONTH and DAY. Return day of + the year or 0 on error. This function uses some more or less + arbitrary limits, most important is that days before 1582 are not + supported. */ +static int +jd2date (unsigned long jd, int *year, int *month, int *day) +{ + int y, m, d; + long delta; + + if (!jd) + return 0 ; + if (jd < 1721425 || jd > 2843085) + return 0; + + y = (jd - JD_DIFF) / 366; + d = m = 1; + + while ((delta = jd - date2jd (y, m, d)) > days_per_year (y)) + y++; + + m = (delta / 31) + 1; + while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m)) + if (++m > 12) + { + m = 1; + y++; + } + + d = delta + 1 ; + if (d > days_per_month (y, m)) + { + d = 1; + m++; + } + if (m > 12) + { + m = 1; + y++; + } + + if (year) + *year = y; + if (month) + *month = m; + if (day) + *day = d ; + + return (jd - date2jd (y, 1, 1)) + 1; +} + + +/* Check that the 15 bytes in ATIME represent a valid ISO time. Note + that this function does not expect a string but a plain 15 byte + isotime buffer. */ +gpg_error_t +check_isotime (const gnupg_isotime_t atime) +{ + int i; + const char *s; + + if (!*atime) + return gpg_error (GPG_ERR_NO_VALUE); + + for (s=atime, i=0; i < 8; i++, s++) + if (!digitp (s)) + return gpg_error (GPG_ERR_INV_TIME); + if (*s != 'T') + return gpg_error (GPG_ERR_INV_TIME); + for (s++, i=9; i < 15; i++, s++) + if (!digitp (s)) + return gpg_error (GPG_ERR_INV_TIME); + return 0; +} + + +/* Dump the ISO time T to the log stream without a LF. */ +void +dump_isotime (const gnupg_isotime_t t) +{ + if (!t || !*t) + log_printf ("%s", _("[none]")); + else + log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s", + t, t+4, t+6, t+9, t+11, t+13); +} + + +/* Copy one ISO date to another, this is inline so that we can do a + minimal sanity check. A null date (empty string) is allowed. */ +void +gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s) +{ + if (*s) + { + if ((strlen (s) != 15 || s[8] != 'T')) + BUG(); + memcpy (d, s, 15); + d[15] = 0; + } + else + *d = 0; +} + + +/* Add SECONDS to ATIME. SECONDS may not be negative and is limited + to about the equivalent of 62 years which should be more then + enough for our purposes. */ +gpg_error_t +add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds) +{ + gpg_error_t err; + int year, month, day, hour, minute, sec, ndays; + unsigned long jd; + + err = check_isotime (atime); + if (err) + return err; + + if (nseconds < 0 || nseconds >= (0x7fffffff - 61) ) + return gpg_error (GPG_ERR_INV_VALUE); + + year = atoi_4 (atime+0); + month = atoi_2 (atime+4); + day = atoi_2 (atime+6); + hour = atoi_2 (atime+9); + minute= atoi_2 (atime+11); + sec = atoi_2 (atime+13); + + if (year <= 1582) /* The julian date functions don't support this. */ + return gpg_error (GPG_ERR_INV_VALUE); + + sec += nseconds; + minute += sec/60; + sec %= 60; + hour += minute/60; + minute %= 60; + ndays = hour/24; + hour %= 24; + + jd = date2jd (year, month, day) + ndays; + jd2date (jd, &year, &month, &day); + + if (year > 9999 || month > 12 || day > 31 + || year < 0 || month < 1 || day < 1) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d", + year, month, day, hour, minute, sec); + return 0; +} + + +gpg_error_t +add_days_to_isotime (gnupg_isotime_t atime, int ndays) +{ + gpg_error_t err; + int year, month, day, hour, minute, sec; + unsigned long jd; + + err = check_isotime (atime); + if (err) + return err; + + if (ndays < 0 || ndays >= 9999*366 ) + return gpg_error (GPG_ERR_INV_VALUE); + + year = atoi_4 (atime+0); + month = atoi_2 (atime+4); + day = atoi_2 (atime+6); + hour = atoi_2 (atime+9); + minute= atoi_2 (atime+11); + sec = atoi_2 (atime+13); + + if (year <= 1582) /* The julian date functions don't support this. */ + return gpg_error (GPG_ERR_INV_VALUE); + + jd = date2jd (year, month, day) + ndays; + jd2date (jd, &year, &month, &day); + + if (year > 9999 || month > 12 || day > 31 + || year < 0 || month < 1 || day < 1) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d", + year, month, day, hour, minute, sec); + return 0; +} diff --git a/common/gettime.h b/common/gettime.h new file mode 100644 index 0000000..73f1886 --- /dev/null +++ b/common/gettime.h @@ -0,0 +1,70 @@ +/* gettime.h - Wrapper for time functions + * Copyright (C) 2010, 2012 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_GETTIME_H +#define GNUPG_COMMON_GETTIME_H + +#include /* We need time_t. */ +#include /* We need gpg_error_t. */ + + +/* A type to hold the ISO time. Note that this is the same as + the KSBA type ksba_isotime_t. */ +typedef char gnupg_isotime_t[16]; + +time_t gnupg_get_time (void); +struct tm *gnupg_gmtime (const time_t *timep, struct tm *result); +void gnupg_get_isotime (gnupg_isotime_t timebuf); +void gnupg_set_time (time_t newtime, int freeze); +int gnupg_faked_time_p (void); +u32 make_timestamp (void); +char *elapsed_time_string (time_t since, time_t now); + +u32 scan_isodatestr (const char *string); +int isotime_p (const char *string); +int isotime_human_p (const char *string, int date_only); +size_t string2isotime (gnupg_isotime_t atime, const char *string); +time_t isotime2epoch (const char *string); +void epoch2isotime (gnupg_isotime_t timebuf, time_t atime); +int isodate_human_to_tm (const char *string, struct tm *t); +time_t parse_timestamp (const char *timestamp, char **endp); +u32 add_days_to_timestamp (u32 stamp, u16 days); +const char *strtimevalue (u32 stamp); +const char *strtimestamp (u32 stamp); /* GMT */ +const char *isotimestamp (u32 stamp); /* GMT */ +const char *asctimestamp (u32 stamp); /* localized */ +char *rfctimestamp (u32 stamp); /* RFC format, malloced. */ +gpg_error_t add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds); +gpg_error_t add_days_to_isotime (gnupg_isotime_t atime, int ndays); +gpg_error_t check_isotime (const gnupg_isotime_t atime); +void dump_isotime (const gnupg_isotime_t atime); +void gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s); + + +#endif /*GNUPG_COMMON_GETTIME_H*/ diff --git a/common/gnupg.ico b/common/gnupg.ico new file mode 100644 index 0000000000000000000000000000000000000000..4c4bae0a9bed313af54eb34d8a87b8255ecc35ad GIT binary patch literal 10134 zcmeHMdvKK175_!TZqPM>g@k~}1~e%k2#GI5A*cnDs0G0gAMH$sKO8GloK9ND#@4L0 z#nx&z1WO%hk*U^4sE^Dj6KZGp1SQp4pt8I|Fpou`LS@54qQ>0*&b{Ba`|V3>^$*+O zZ0>jNIp=rIz2Eu1z4zP=kOdEh4jl@i30c7HwBOyRa;aTntM8 zK`H66@gpj^!1p$ZTz55#$uN4K<+BI30F9+AUSjr9_nD2Lw}~OhCwN#+c|JC1IBL__z6hT-UzN6`DuD!fw*?D#3L=U2e42Y{YGmf^^zTH1bt-46mC zO}XgZz7p@Q8jn4{=JWd7gXmc4$G(*#u zpicamy4P^B^A)I%-@?D%ydQfW0_S%xPISIZ%=0+e^)imU{zL5gIk0W1JgYvA3+!DE z>}JgEjJ>^%W84dDy%X5?+d>?A^$v8bpq=xMG~a*^Bd?-2{5TYK+wTFQ+Zyq{^*zS= z3y$x79v^+Mi}(BxI#*wTW4oS!dJ5RGgk!w~;+CNMnK``28QA$VxyKN?pS=|7Pe9k2 z3$cr1s24&1htd7ycu9+DZocK#+rE9y zcdA8-(ZZYJ^7S`-Yu@}D7sPDRnOk+8uy4wR(zO-dO6knLO0J}#B zwrq=9FN~zOIoTMENQKtQT#U)!wakl+azsZFmu*DcU_8FOY)K$rJkICHKIcBfO*>l7 zRm-xY3sI{{GB0F{97!uIM;3*hEL-J^IkO_+&MrHDAKO5l>KjDf72w`CW<>w-D0T*cldD;Gu=d#hVg(czk{v$*J~?p!J3(Vw>$2~&-nx+WPb<3DoQL*@f%jgT ztMhvLaykQFmkfApsvaH~Y$(6LrumL7&MPY`ub45V$gdG;>^AbYzycyN+dUWEa%)=q zsG92*-n6jfqMJR1{>=UhZ_2G+G;P6W#2{ zR$X`f9FJ%24UQ`38mH&*IXR=}daiY(f}FgePT%1ZXJ2)-(=RB<@lToINH(82tK8A( zZ08RSTsE9E&CyQ3-09iRtH7@*kXzyyZ0^qyM2<1@Izomg(y!R zS?UJxf&0hB72`y9-O_N8hsW(-wLC3d{8%D5ZXk91KzusJJ~ghG%6JL(DFuGFg5e6L z#Ni&##Oz5IOfGRWC6fyZMox4j&Bvd2esR7HDB(9KCojS5Id`n*%s_ro-nemjMPnI# zq$_(GIcl`$oH5yaM*3Oh7bdz>Vv+3ejo?=_zaVF1s@dZ)WwY-r7S74RonWk@2WJm* zB+Yf5(l51&)G4e@`m@NRrmyRiHr6SY)G1S=lv$^Qq9|vr;*}buUh0&-KfD8$ zt^#A)c)=$wnrxdR#<@>h;&QB~4Kc~p5ra##hqmu7T1J0>Ga@9*7fWV zBYC&AgM^8tmQUIeh5dx=h~ngPPHF;o>3KL42SWmMhz>qmZ851 zdA}^?!t0fKU;}x%Qr*I3rLQOF@+LkPkf)T&%m7|HGuq$GQbyCeUHusSS6-+G$1p!xp;ArhZLtiwl$=qCzK ztyzO`RH3Cup&_iWWV1p=t3q)z!P1&KfV%a-VjfVd_+x$9e1s#!w-LXV_z>~6#Mcm8 z%~nbLOyWz4FD8Bz@r>KjLVN@9ONg%^zL;1&A>u(ihzIeQj|lN?#IGejM0_prHN;mF zUx^OPB)*jRV&X>;A0@tr_%QLCiEkyonfPYnn~1L?elhV?#Fr7Th*!ib;uZ0VctyN? zI`~=7&q{u3`I)crQQ~`u4->zc_*UX2dNa{YMAs3$nCL2^%NRdGd>ip=i4PH9OMDIS z)x=j4Ka==U;){tNMLgrSv=HAw{1W1M)-Nt5p67P9#OyF@5#rm3UrT(5_*&v?h_5ET zlK7d-W+}5-%xsP#K1zHK@nPaO6W>aFGx5#lH<0!GePQKd;;V=+lUIazMZ6+j5wD0> z#B)L&oX~nsXeA$4Eg#qX`TX6I_#WcJ#BV0PmH1}jGrT^C5Z^}pTH-^**Aibte1_MD zal>dKzJd59#8(hsOnjyUh4}?+QEkMpB|bzvGp%ZfPoJPLm$??zLwuO{&BV77&*WrC zP?(8mVQXMp!dAgn%$6xZ$BrGtfddE7-rkNaTee{1#*J9FZXKGNo3VQJYBV)9VcD`} zsH>~P(xpqWc=2K^TC@mNRaKZZYZl7N%1}~Lg3+T#BR7}-v~*Eu`HIYx{(nNDP-A0b zNUkZquD}_(iQ{DeC8`_a6HGK@lwuwk5y=L7h-7t49ujI?uJ3kvBYy-VnOl$9({_e5 zICtA0L%tyXi^l(d#oLQRN3bUF4{q_e=d*olaGxc<_5E4>2GzqR2s0pGO!b{ zLDK6orSH1Z@>xr;+u7`*i7T>WH|JEIVDveaT%S`l=Q&k#o>L9)(!7i3R4t>>a5++H zO2o-I)#&9cD(BSu$azj3$P->K=Tte9@|>#3<($gCIj55Ils6!<97{K7&hwq-R2WVd zvGV{+l7jKe@SyGQ;yP|n&Zi`YTXIe%y`C4vJl(KBtoR$vKsz&#BVM>yoQE LrwW&Is#5<3=9S7l literal 0 HcmV?d00001 diff --git a/common/gpgrlhelp.c b/common/gpgrlhelp.c new file mode 100644 index 0000000..680d999 --- /dev/null +++ b/common/gpgrlhelp.c @@ -0,0 +1,97 @@ +/* gpgrlhelp.c - A readline wrapper. + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +/* This module may by used by applications to initializes readline + support. It is required so that we can have hooks in other parts + of libcommon without actually requiring to link against + libreadline. It works along with ttyio.c which is a proper part of + libcommon. */ + +#include +#include +#include + +#ifdef HAVE_LIBREADLINE +#define GNUPG_LIBREADLINE_H_INCLUDED +#include +#include +#include +#endif + +#include "util.h" +#include "common-defs.h" + + +#ifdef HAVE_LIBREADLINE +static void +set_completer (rl_completion_func_t *completer) +{ + rl_attempted_completion_function = completer; + rl_inhibit_completion = 0; +} + +static void +inhibit_completion (int value) +{ + rl_inhibit_completion = value; +} + +static void +cleanup_after_signal (void) +{ + rl_free_line_state (); + rl_cleanup_after_signal (); +} + +static void +init_stream (FILE *fp) +{ + rl_catch_signals = 0; + rl_instream = rl_outstream = fp; + rl_inhibit_completion = 1; +} + +#endif /*HAVE_LIBREADLINE*/ + + +/* Initialize our readline code. This should be called as early as + possible as it is actually a constructur. */ +void +gnupg_rl_initialize (void) +{ +#ifdef HAVE_LIBREADLINE + tty_private_set_rl_hooks (init_stream, + set_completer, + inhibit_completion, + cleanup_after_signal, + readline, + add_history); + rl_readline_name = GNUPG_NAME; +#endif +} diff --git a/common/helpfile.c b/common/helpfile.c new file mode 100644 index 0000000..7cb01a4 --- /dev/null +++ b/common/helpfile.c @@ -0,0 +1,272 @@ +/* helpfile.c - GnuPG's helpfile feature + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include + + +#include "util.h" +#include "i18n.h" +#include "membuf.h" + + +/* Try to find KEY in the file FNAME. */ +static char * +findkey_fname (const char *key, const char *fname) +{ + gpg_error_t err = 0; + FILE *fp; + int lnr = 0; + int c; + char *p, line[256]; + int in_item = 0; + membuf_t mb = MEMBUF_ZERO; + + fp = fopen (fname, "r"); + if (!fp) + { + if (errno != ENOENT) + { + err = gpg_error_from_syserror (); + log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); + } + return NULL; + } + + while (fgets (line, DIM(line)-1, fp)) + { + lnr++; + + if (!*line || line[strlen(line)-1] != '\n') + { + /* Eat until end of line. */ + while ( (c=getc (fp)) != EOF && c != '\n') + ; + err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + log_error (_("file '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + } + else + line[strlen(line)-1] = 0; /* Chop the LF. */ + + again: + if (!in_item) + { + /* Allow for empty lines and spaces while not in an item. */ + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '#') + continue; + if (*line != '.' || spacep(line+1)) + { + log_info (_("file '%s', line %d: %s\n"), + fname, lnr, _("ignoring garbage line")); + continue; + } + trim_trailing_spaces (line); + in_item = 1; + if (!strcmp (line+1, key)) + { + /* Found. Start collecting. */ + init_membuf (&mb, 1024); + } + continue; + } + + /* If in an item only allow for comments in the first column + and provide ". " as an escape sequence to allow for + leading dots and hash marks in the actual text. */ + if (*line == '#') + continue; + if (*line == '.') + { + if (spacep(line+1)) + p = line + 2; + else + { + trim_trailing_spaces (line); + in_item = 0; + if (is_membuf_ready (&mb)) + break; /* Yep, found and collected the item. */ + if (!line[1]) + continue; /* Just an end of text dot. */ + goto again; /* A new key line. */ + } + } + else + p = line; + + if (is_membuf_ready (&mb)) + { + put_membuf_str (&mb, p); + put_membuf (&mb, "\n", 1); + } + + } + if ( !err && ferror (fp) ) + { + err = gpg_error_from_syserror (); + log_error (_("error reading '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + } + + fclose (fp); + if (is_membuf_ready (&mb)) + { + /* We have collected something. */ + if (err) + { + xfree (get_membuf (&mb, NULL)); + return NULL; + } + else + { + put_membuf (&mb, "", 1); /* Terminate string. */ + return get_membuf (&mb, NULL); + } + } + else + return NULL; +} + + +/* Try the help files depending on the locale. */ +static char * +findkey_locale (const char *key, const char *locname, + int only_current_locale, const char *dirname) +{ + const char *s; + char *fname, *ext, *p; + char *result; + + fname = xtrymalloc (strlen (dirname) + 6 + strlen (locname) + 4 + 1); + if (!fname) + return NULL; + ext = stpcpy (stpcpy (fname, dirname), "/help."); + /* Search with locale name and territory. ("help.LL_TT.txt") */ + if (strchr (locname, '_')) + { + strcpy (stpcpy (ext, locname), ".txt"); + result = findkey_fname (key, fname); + } + else + result = NULL; /* No territory. */ + + if (!result) + { + /* Search with just the locale name - if any. ("help.LL.txt") */ + if (*locname) + { + for (p=ext, s=locname; *s && *s != '_';) + *p++ = *s++; + strcpy (p, ".txt"); + result = findkey_fname (key, fname); + } + else + result = NULL; + } + + if (!result && (!only_current_locale || !*locname) ) + { + /* Last try: Search in file without any locale info. ("help.txt") */ + strcpy (ext, "txt"); + result = findkey_fname (key, fname); + } + + xfree (fname); + return result; +} + + +/* Return a malloced help text as identified by KEY. The system takes + the string from an UTF-8 encoded file to be created by an + administrator or as distributed with GnuPG. On a GNU or Unix + system the entry is searched in these files: + + /etc/gnupg/help.LL.txt + /etc/gnupg/help.txt + /usr/share/gnupg/help.LL.txt + /usr/share/gnupg/help.txt + + Here LL denotes the two digit language code of the current locale. + If ONLY_CURRENT_LOCALE is set, the function won't fallback to the + english valiant ("help.txt") unless that locale has been requested. + + The help file needs to be encoded in UTF-8, lines with a '#' in the + first column are comment lines and entirely ignored. Help keys are + identified by a key consisting of a single word with a single dot + as the first character. All key lines listed without any + intervening lines (except for comment lines) lead to the same help + text. Lines following the key lines make up the actual hep texts. + +*/ + +char * +gnupg_get_help_string (const char *key, int only_current_locale) +{ + static const char *locname; + char *result; + + if (!locname) + { + char *buffer, *p; + int count = 0; + const char *s = gnupg_messages_locale_name (); + buffer = xtrystrdup (s); + if (!buffer) + locname = ""; + else + { + for (p = buffer; *p; p++) + if (*p == '.' || *p == '@' || *p == '/' /*(safeguard)*/) + *p = 0; + else if (*p == '_') + { + if (count++) + *p = 0; /* Also cut at a underscore in the territory. */ + } + locname = buffer; + } + } + + if (!key || !*key) + return NULL; + + result = findkey_locale (key, locname, only_current_locale, + gnupg_sysconfdir ()); + if (!result) + result = findkey_locale (key, locname, only_current_locale, + gnupg_datadir ()); + + if (result) + trim_trailing_spaces (result); + + return result; +} diff --git a/common/homedir.c b/common/homedir.c new file mode 100644 index 0000000..6b40bb6 --- /dev/null +++ b/common/homedir.c @@ -0,0 +1,1029 @@ +/* homedir.c - Setup the home directory. + * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2013, 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_W32_SYSTEM +#include /* Due to the stupid mingw64 requirement to + include this header before windows.h which + is often implicitly included. */ +#include +#ifndef CSIDL_APPDATA +#define CSIDL_APPDATA 0x001a +#endif +#ifndef CSIDL_LOCAL_APPDATA +#define CSIDL_LOCAL_APPDATA 0x001c +#endif +#ifndef CSIDL_COMMON_APPDATA +#define CSIDL_COMMON_APPDATA 0x0023 +#endif +#ifndef CSIDL_FLAG_CREATE +#define CSIDL_FLAG_CREATE 0x8000 +#endif +#endif /*HAVE_W32_SYSTEM*/ + +#ifdef HAVE_STAT +#include /* for stat() */ +#endif + + + +#include "util.h" +#include "sysutils.h" +#include "zb32.h" + +/* The GnuPG homedir. This is only accessed by the functions + * gnupg_homedir and gnupg_set_homedir. Malloced. */ +static char *the_gnupg_homedir; + +/* Flag indicating that home directory is not the default one. */ +static byte non_default_homedir; + + +#ifdef HAVE_W32_SYSTEM +/* A flag used to indicate that a control file for gpgconf has been + detected. Under Windows the presence of this file indicates a + portable installations and triggers several changes: + + - The GNUGHOME directory is fixed relative to installation + directory. All other means to set the home directory are ignore. + + - All registry variables will be ignored. + + This flag is not used on Unix systems. + */ +static byte w32_portable_app; +#endif /*HAVE_W32_SYSTEM*/ + +#ifdef HAVE_W32_SYSTEM +/* This flag is true if this process' binary has been installed under + bin and not in the root directory as often used before GnuPG 2.1. */ +static byte w32_bin_is_bin; +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +static const char *w32_rootdir (void); +#endif + + + +#ifdef HAVE_W32_SYSTEM +static void +w32_try_mkdir (const char *dir) +{ +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wdir = utf8_to_wchar (dir); + if (wdir) + { + CreateDirectory (wdir, NULL); + xfree (wdir); + } +#else + CreateDirectory (dir, NULL); +#endif +} +#endif + + +/* This is a helper function to load a Windows function from either of + one DLLs. */ +#ifdef HAVE_W32_SYSTEM +static HRESULT +w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e) +{ + static int initialized; + static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR); + + if (!initialized) + { + static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; + void *handle; + int i; + + initialized = 1; + + for (i=0, handle = NULL; !handle && dllnames[i]; i++) + { + handle = dlopen (dllnames[i], RTLD_LAZY); + if (handle) + { + func = dlsym (handle, "SHGetFolderPathA"); + if (!func) + { + dlclose (handle); + handle = NULL; + } + } + } + } + + if (func) + return func (a,b,c,d,e); + else + return -1; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Check whether DIR is the default homedir. */ +static int +is_gnupg_default_homedir (const char *dir) +{ + int result; + char *a = make_absfilename (dir, NULL); + char *b = make_absfilename (GNUPG_DEFAULT_HOMEDIR, NULL); + result = !compare_filenames (a, b); + xfree (b); + xfree (a); + return result; +} + + +/* Get the standard home directory. In general this function should + not be used as it does not consider a registry value (under W32) or + the GNUPGHOME environment variable. It is better to use + default_homedir(). */ +const char * +standard_homedir (void) +{ +#ifdef HAVE_W32_SYSTEM + static const char *dir; + + if (!dir) + { + const char *rdir; + + rdir = w32_rootdir (); + if (w32_portable_app) + { + dir = xstrconcat (rdir, DIRSEP_S "home", NULL); + } + else + { + char path[MAX_PATH]; + + /* It might be better to use LOCAL_APPDATA because this is + defined as "non roaming" and thus more likely to be kept + locally. For private keys this is desired. However, + given that many users copy private keys anyway forth and + back, using a system roaming services might be better + than to let them do it manually. A security conscious + user will anyway use the registry entry to have better + control. */ + if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0, path) >= 0) + { + char *tmp = xmalloc (strlen (path) + 6 +1); + strcpy (stpcpy (tmp, path), "\\gnupg"); + dir = tmp; + + /* Try to create the directory if it does not yet exists. */ + if (access (dir, F_OK)) + w32_try_mkdir (dir); + } + else + dir = GNUPG_DEFAULT_HOMEDIR; + } + } + return dir; +#else/*!HAVE_W32_SYSTEM*/ + return GNUPG_DEFAULT_HOMEDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + +/* Set up the default home directory. The usual --homedir option + should be parsed later. */ +const char * +default_homedir (void) +{ + const char *dir; + +#ifdef HAVE_W32_SYSTEM + /* For a portable application we only use the standard homedir. */ + w32_rootdir (); + if (w32_portable_app) + return standard_homedir (); +#endif /*HAVE_W32_SYSTEM*/ + + dir = getenv ("GNUPGHOME"); +#ifdef HAVE_W32_SYSTEM + if (!dir || !*dir) + { + static const char *saved_dir; + + if (!saved_dir) + { + if (!dir || !*dir) + { + char *tmp; + + tmp = read_w32_registry_string (NULL, + GNUPG_REGISTRY_DIR, + "HomeDir"); + if (tmp && !*tmp) + { + xfree (tmp); + tmp = NULL; + } + if (tmp) + saved_dir = tmp; + } + + if (!saved_dir) + saved_dir = standard_homedir (); + } + dir = saved_dir; + } +#endif /*HAVE_W32_SYSTEM*/ + if (!dir || !*dir) + dir = GNUPG_DEFAULT_HOMEDIR; + else if (!is_gnupg_default_homedir (dir)) + non_default_homedir = 1; + + return dir; +} + + +#ifdef HAVE_W32_SYSTEM +/* Check whether gpgconf is installed and if so read the gpgconf.ctl + file. */ +static void +check_portable_app (const char *dir) +{ + char *fname; + + fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); + if (!access (fname, F_OK)) + { + strcpy (fname + strlen (fname) - 3, "ctl"); + if (!access (fname, F_OK)) + { + /* gpgconf.ctl file found. Record this fact. */ + w32_portable_app = 1; + { + unsigned int flags; + log_get_prefix (&flags); + log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); + } + /* FIXME: We should read the file to detect special flags + and print a warning if we don't understand them */ + } + } + xfree (fname); +} + + +/* Determine the root directory of the gnupg installation on Windows. */ +static const char * +w32_rootdir (void) +{ + static int got_dir; + static char dir[MAX_PATH+5]; + + if (!got_dir) + { + char *p; + int rc; + wchar_t wdir [MAX_PATH+5]; + + rc = GetModuleFileNameW (NULL, wdir, MAX_PATH); + if (rc && WideCharToMultiByte (CP_UTF8, 0, wdir, -1, dir, MAX_PATH-4, + NULL, NULL) < 0) + rc = 0; + if (!rc) + { + log_debug ("GetModuleFileName failed: %s\n", w32_strerror (-1)); + *dir = 0; + } + got_dir = 1; + p = strrchr (dir, DIRSEP_C); + if (p) + { + *p = 0; + + check_portable_app (dir); + + /* If we are installed below "bin" we strip that and use + the top directory instead. */ + p = strrchr (dir, DIRSEP_C); + if (p && !strcmp (p+1, "bin")) + { + *p = 0; + w32_bin_is_bin = 1; + } + } + if (!p) + { + log_debug ("bad filename '%s' returned for this process\n", dir); + *dir = 0; + } + } + + if (*dir) + return dir; + /* Fallback to the hardwired value. */ + return GNUPG_LIBEXECDIR; +} + +static const char * +w32_commondir (void) +{ + static char *dir; + + if (!dir) + { + const char *rdir; + char path[MAX_PATH]; + + /* Make sure that w32_rootdir has been called so that we are + able to check the portable application flag. The common dir + is the identical to the rootdir. In that case there is also + no need to strdup its value. */ + rdir = w32_rootdir (); + if (w32_portable_app) + return rdir; + + if (w32_shgetfolderpath (NULL, CSIDL_COMMON_APPDATA, + NULL, 0, path) >= 0) + { + char *tmp = xmalloc (strlen (path) + 4 +1); + strcpy (stpcpy (tmp, path), "\\GNU"); + dir = tmp; + /* No auto create of the directory. Either the installer or + the admin has to create these directories. */ + } + else + { + /* Ooops: Not defined - probably an old Windows version. + Use the installation directory instead. */ + dir = xstrdup (rdir); + } + } + + return dir; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Change the homedir. Some care must be taken to set this early + * enough because previous calls to gnupg_homedir may else return a + * different string. */ +void +gnupg_set_homedir (const char *newdir) +{ + if (!newdir || !*newdir) + newdir = default_homedir (); + else if (!is_gnupg_default_homedir (newdir)) + non_default_homedir = 1; + xfree (the_gnupg_homedir); + the_gnupg_homedir = make_absfilename (newdir, NULL);; +} + + +/* Return the homedir. The returned string is valid until another + * gnupg-set-homedir call. This is always an absolute directory name. + * The function replaces the former global var opt.homedir. */ +const char * +gnupg_homedir (void) +{ + /* If a homedir has not been set, set it to the default. */ + if (!the_gnupg_homedir) + the_gnupg_homedir = make_absfilename (default_homedir (), NULL); + return the_gnupg_homedir; +} + + +/* Return whether the home dir is the default one. */ +int +gnupg_default_homedir_p (void) +{ + return !non_default_homedir; +} + + +/* Helper for gnupg-socketdir. This is a global function, so that + * gpgconf can use it for its --create-socketdir command. If + * SKIP_CHECKS is set permission checks etc. are not done. The + * function always returns a malloced directory name and stores these + * bit flags at R_INFO: + * + * 1 := Internal error, stat failed, out of core, etc. + * 2 := No /run/user directory. + * 4 := Directory not owned by the user, not a directory + * or wrong permissions. + * 8 := Same as 4 but for the subdir. + * 16 := mkdir failed + * 32 := Non default homedir; checking subdir. + * 64 := Subdir does not exist. + * 128 := Using homedir as fallback. + */ +char * +_gnupg_socketdir_internal (int skip_checks, unsigned *r_info) +{ +#if defined(HAVE_W32_SYSTEM) || !defined(HAVE_STAT) + + char *name; + + (void)skip_checks; + *r_info = 0; + name = xstrdup (gnupg_homedir ()); + +#else /* Unix and stat(2) available. */ + + static const char * const bases[] = { "/run", "/var/run", NULL}; + int i; + struct stat sb; + char prefix[13 + 1 + 20 + 6 + 1]; + const char *s; + char *name = NULL; + + *r_info = 0; + + /* First make sure that non_default_homedir can be set. */ + gnupg_homedir (); + + /* It has been suggested to first check XDG_RUNTIME_DIR envvar. + * However, the specs state that the lifetime of the directory MUST + * be bound to the user being logged in. Now GnuPG may also be run + * as a background process with no (desktop) user logged in. Thus + * we better don't do that. */ + + /* Check whether we have a /run/user dir. */ + for (i=0; bases[i]; i++) + { + snprintf (prefix, sizeof prefix, "%s/user/%u", + bases[i], (unsigned int)getuid ()); + if (!stat (prefix, &sb) && S_ISDIR(sb.st_mode)) + break; + } + if (!bases[i]) + { + *r_info |= 2; /* No /run/user directory. */ + goto leave; + } + + if (sb.st_uid != getuid ()) + { + *r_info |= 4; /* Not owned by the user. */ + if (!skip_checks) + goto leave; + } + + if (strlen (prefix) + 7 >= sizeof prefix) + { + *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ + goto leave; + } + strcat (prefix, "/gnupg"); + + /* Check whether the gnupg sub directory has proper permissions. */ + if (stat (prefix, &sb)) + { + if (errno != ENOENT) + { + *r_info |= 1; /* stat failed. */ + goto leave; + } + + /* Try to create the directory and check again. */ + if (gnupg_mkdir (prefix, "-rwx")) + { + *r_info |= 16; /* mkdir failed. */ + goto leave; + } + if (stat (prefix, &sb)) + { + *r_info |= 1; /* stat failed. */ + goto leave; + } + } + /* Check that it is a directory, owned by the user, and only the + * user has permissions to use it. */ + if (!S_ISDIR(sb.st_mode) + || sb.st_uid != getuid () + || (sb.st_mode & (S_IRWXG|S_IRWXO))) + { + *r_info |= 4; /* Bad permissions or not a directory. */ + if (!skip_checks) + goto leave; + } + + /* If a non default homedir is used, we check whether an + * corresponding sub directory below the socket dir is available + * and use that. We has the non default homedir to keep the new + * subdir short enough. */ + if (non_default_homedir) + { + char sha1buf[20]; + char *suffix; + + *r_info |= 32; /* Testing subdir. */ + s = gnupg_homedir (); + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, s, strlen (s)); + suffix = zb32_encode (sha1buf, 8*15); + if (!suffix) + { + *r_info |= 1; /* Out of core etc. */ + goto leave; + } + name = strconcat (prefix, "/d.", suffix, NULL); + xfree (suffix); + if (!name) + { + *r_info |= 1; /* Out of core etc. */ + goto leave; + } + + /* Stat that directory and check constraints. Note that we + * do not auto create such a directory because we would not + * have a way to remove it. Thus the directory needs to be + * pre-created. The command + * gpgconf --create-socketdir + * can be used tocreate that directory. */ + if (stat (name, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else + *r_info |= 64; /* Subdir does not exist. */ + if (!skip_checks) + { + xfree (name); + name = NULL; + goto leave; + } + } + else if (!S_ISDIR(sb.st_mode) + || sb.st_uid != getuid () + || (sb.st_mode & (S_IRWXG|S_IRWXO))) + { + *r_info |= 8; /* Bad permissions or subdir is not a directory. */ + if (!skip_checks) + { + xfree (name); + name = NULL; + goto leave; + } + } + } + else + name = xstrdup (prefix); + + leave: + /* If nothing works fall back to the homedir. */ + if (!name) + { + *r_info |= 128; /* Fallback. */ + name = xstrdup (gnupg_homedir ()); + } + +#endif /* Unix */ + + return name; +} + + +/* + * Return the name of the socket dir. That is the directory used for + * the IPC local sockets. This is an absolute directory name. + */ +const char * +gnupg_socketdir (void) +{ + static char *name; + + if (!name) + { + unsigned int dummy; + name = _gnupg_socketdir_internal (0, &dummy); + } + + return name; +} + + +/* Return the name of the sysconfdir. This is a static string. This + function is required because under Windows we can't simply compile + it in. */ +const char * +gnupg_sysconfdir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + { + const char *s1, *s2; + s1 = w32_commondir (); + s2 = DIRSEP_S "etc" DIRSEP_S "gnupg"; + name = xmalloc (strlen (s1) + strlen (s2) + 1); + strcpy (stpcpy (name, s1), s2); + } + return name; +#else /*!HAVE_W32_SYSTEM*/ + return GNUPG_SYSCONFDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +const char * +gnupg_bindir (void) +{ +#if defined (HAVE_W32CE_SYSTEM) + static char *name; + + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "bin", NULL); + return name; +#elif defined(HAVE_W32_SYSTEM) + const char *rdir; + + rdir = w32_rootdir (); + if (w32_bin_is_bin) + { + static char *name; + + if (!name) + name = xstrconcat (rdir, DIRSEP_S "bin", NULL); + return name; + } + else + return rdir; +#else /*!HAVE_W32_SYSTEM*/ + return GNUPG_BINDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Return the name of the libexec directory. The name is allocated in + a static area on the first use. This function won't fail. */ +const char * +gnupg_libexecdir (void) +{ +#ifdef HAVE_W32_SYSTEM + return gnupg_bindir (); +#else /*!HAVE_W32_SYSTEM*/ + return GNUPG_LIBEXECDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + +const char * +gnupg_libdir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "lib" DIRSEP_S "gnupg", NULL); + return name; +#else /*!HAVE_W32_SYSTEM*/ + return GNUPG_LIBDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + +const char * +gnupg_datadir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "gnupg", NULL); + return name; +#else /*!HAVE_W32_SYSTEM*/ + return GNUPG_DATADIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +const char * +gnupg_localedir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "locale", + NULL); + return name; +#else /*!HAVE_W32_SYSTEM*/ + return LOCALEDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Return the name of the cache directory. The name is allocated in a + static area on the first use. Windows only: If the directory does + not exist it is created. */ +const char * +gnupg_cachedir (void) +{ +#ifdef HAVE_W32_SYSTEM + static const char *dir; + + if (!dir) + { + const char *rdir; + + rdir = w32_rootdir (); + if (w32_portable_app) + { + dir = xstrconcat (rdir, + DIRSEP_S, "var", + DIRSEP_S, "cache", + DIRSEP_S, "gnupg", NULL); + } + else + { + char path[MAX_PATH]; + const char *s1[] = { "GNU", "cache", "gnupg", NULL }; + int s1_len; + const char **comp; + + s1_len = 0; + for (comp = s1; *comp; comp++) + s1_len += 1 + strlen (*comp); + + if (w32_shgetfolderpath (NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0, path) >= 0) + { + char *tmp = xmalloc (strlen (path) + s1_len + 1); + char *p; + + p = stpcpy (tmp, path); + for (comp = s1; *comp; comp++) + { + p = stpcpy (p, "\\"); + p = stpcpy (p, *comp); + + if (access (tmp, F_OK)) + w32_try_mkdir (tmp); + } + + dir = tmp; + } + else + { + dir = "c:\\temp\\cache\\gnupg"; +#ifdef HAVE_W32CE_SYSTEM + dir += 2; + w32_try_mkdir ("\\temp\\cache"); + w32_try_mkdir ("\\temp\\cache\\gnupg"); +#endif + } + } + } + return dir; +#else /*!HAVE_W32_SYSTEM*/ + return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Return the user socket name used by DirMngr. */ +const char * +dirmngr_socket_name (void) +{ + static char *name; + + if (!name) + name = make_filename (gnupg_socketdir (), DIRMNGR_SOCK_NAME, NULL); + return name; +} + + +/* Return the default pinentry name. If RESET is true the internal + cache is first flushed. */ +static const char * +get_default_pinentry_name (int reset) +{ + static struct { + const char *(*rfnc)(void); + const char *name; + } names[] = { + /* The first entry is what we return in case we found no + other pinentry. */ + { gnupg_bindir, DIRSEP_S "pinentry" EXEEXT_S }, +#ifdef HAVE_W32_SYSTEM + /* Try Gpg4win directory (with bin and without.) */ + { w32_rootdir, "\\..\\Gpg4win\\bin\\pinentry.exe" }, + { w32_rootdir, "\\..\\Gpg4win\\pinentry.exe" }, + /* Try old Gpgwin directory. */ + { w32_rootdir, "\\..\\GNU\\GnuPG\\pinentry.exe" }, + /* Try a Pinentry from the common GNU dir. */ + { w32_rootdir, "\\..\\GNU\\bin\\pinentry.exe" }, +#endif + /* Last chance is a pinentry-basic (which comes with the + GnuPG 2.1 Windows installer). */ + { gnupg_bindir, DIRSEP_S "pinentry-basic" EXEEXT_S } + }; + static char *name; + + if (reset) + { + xfree (name); + name = NULL; + } + + if (!name) + { + int i; + + for (i=0; i < DIM(names); i++) + { + char *name2; + + name2 = xstrconcat (names[i].rfnc (), names[i].name, NULL); + if (!access (name2, F_OK)) + { + /* Use that pinentry. */ + xfree (name); + name = name2; + break; + } + if (!i) /* Store the first as fallback return. */ + name = name2; + else + xfree (name2); + } + } + + return name; +} + + +/* If set, 'gnupg_module_name' returns modules from that build + * directory. */ +static char *gnupg_build_directory; + +/* For sanity checks. */ +static int gnupg_module_name_called; + + +/* Set NEWDIR as the new build directory. This will make + * 'gnupg_module_name' return modules from that build directory. Must + * be called before any invocation of 'gnupg_module_name', and must + * not be called twice. It can be used by test suites to make sure + * the components from the build directory are used instead of + * potentially outdated installed ones. */ +void +gnupg_set_builddir (const char *newdir) +{ + log_assert (! gnupg_module_name_called); + log_assert (! gnupg_build_directory); + gnupg_build_directory = xtrystrdup (newdir); +} + + +/* If no build directory has been configured, try to set it from the + * environment. We only do this in development builds to avoid + * increasing the set of influential environment variables and hence + * the attack surface of production builds. */ +static void +gnupg_set_builddir_from_env (void) +{ +#ifdef IS_DEVELOPMENT_VERSION + if (gnupg_build_directory) + return; + + gnupg_build_directory = getenv ("GNUPG_BUILDDIR"); +#endif +} + + +/* Return the file name of a helper tool. WHICH is one of the + GNUPG_MODULE_NAME_foo constants. */ +const char * +gnupg_module_name (int which) +{ + gnupg_set_builddir_from_env (); + gnupg_module_name_called = 1; + +#define X(a,b,c) do { \ + static char *name; \ + if (!name) \ + name = gnupg_build_directory \ + ? xstrconcat (gnupg_build_directory, \ + DIRSEP_S b DIRSEP_S c EXEEXT_S, NULL) \ + : xstrconcat (gnupg_ ## a (), DIRSEP_S c EXEEXT_S, NULL); \ + return name; \ + } while (0) + + switch (which) + { + case GNUPG_MODULE_NAME_AGENT: +#ifdef GNUPG_DEFAULT_AGENT + return GNUPG_DEFAULT_AGENT; +#else + X(bindir, "agent", "gpg-agent"); +#endif + + case GNUPG_MODULE_NAME_PINENTRY: +#ifdef GNUPG_DEFAULT_PINENTRY + return GNUPG_DEFAULT_PINENTRY; /* (Set by a configure option) */ +#else + return get_default_pinentry_name (0); +#endif + + case GNUPG_MODULE_NAME_SCDAEMON: +#ifdef GNUPG_DEFAULT_SCDAEMON + return GNUPG_DEFAULT_SCDAEMON; +#else + X(libexecdir, "scd", "scdaemon"); +#endif + + case GNUPG_MODULE_NAME_DIRMNGR: +#ifdef GNUPG_DEFAULT_DIRMNGR + return GNUPG_DEFAULT_DIRMNGR; +#else + X(bindir, "dirmngr", DIRMNGR_NAME); +#endif + + case GNUPG_MODULE_NAME_PROTECT_TOOL: +#ifdef GNUPG_DEFAULT_PROTECT_TOOL + return GNUPG_DEFAULT_PROTECT_TOOL; +#else + X(libexecdir, "agent", "gpg-protect-tool"); +#endif + + case GNUPG_MODULE_NAME_DIRMNGR_LDAP: +#ifdef GNUPG_DEFAULT_DIRMNGR_LDAP + return GNUPG_DEFAULT_DIRMNGR_LDAP; +#else + X(libexecdir, "dirmngr", "dirmngr_ldap"); +#endif + + case GNUPG_MODULE_NAME_CHECK_PATTERN: + X(libexecdir, "tools", "gpg-check-pattern"); + + case GNUPG_MODULE_NAME_GPGSM: + X(bindir, "sm", "gpgsm"); + + case GNUPG_MODULE_NAME_GPG: +#if USE_GPG2_HACK + if (! gnupg_build_directory) + X(bindir, "g10", GPG_NAME "2"); + else +#endif + X(bindir, "g10", GPG_NAME); + + case GNUPG_MODULE_NAME_GPGV: +#if USE_GPG2_HACK + if (! gnupg_build_directory) + X(bindir, "g10", GPG_NAME "v2"); + else +#endif + X(bindir, "g10", GPG_NAME "v"); + + case GNUPG_MODULE_NAME_CONNECT_AGENT: + X(bindir, "tools", "gpg-connect-agent"); + + case GNUPG_MODULE_NAME_GPGCONF: + X(bindir, "tools", "gpgconf"); + + default: + BUG (); + } +#undef X +} + + +/* Flush some of the cached module names. This is for example used by + gpg-agent to allow configuring a different pinentry. */ +void +gnupg_module_name_flush_some (void) +{ + (void)get_default_pinentry_name (1); +} diff --git a/common/host2net.h b/common/host2net.h new file mode 100644 index 0000000..9eeaf24 --- /dev/null +++ b/common/host2net.h @@ -0,0 +1,112 @@ +/* host2net.h - Endian conversion macros + * Copyright (C) 1998, 2014, 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_HOST2NET_H +#define GNUPG_COMMON_HOST2NET_H + +#include "types.h" + +#define ulongtobuf( p, a ) do { \ + ((byte*)p)[0] = a >> 24; \ + ((byte*)p)[1] = a >> 16; \ + ((byte*)p)[2] = a >> 8; \ + ((byte*)p)[3] = a ; \ + } while(0) +#define ushorttobuf( p, a ) do { \ + ((byte*)p)[0] = a >> 8; \ + ((byte*)p)[1] = a ; \ + } while(0) + + +static inline unsigned long +buf16_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 8) | p[1]); +} + +static inline unsigned int +buf16_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 8) | p[1]); +} + +static inline unsigned short +buf16_to_ushort (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned short)p[0] << 8) | p[1]); +} + +static inline u16 +buf16_to_u16 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u16)p[0] << 8) | p[1]); +} + +static inline size_t +buf32_to_size_t (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((size_t)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned long +buf32_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned int +buf32_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline u32 +buf32_to_u32 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u32)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + + +#endif /*GNUPG_COMMON_HOST2NET_H*/ diff --git a/common/i18n.c b/common/i18n.c new file mode 100644 index 0000000..b5a2864 --- /dev/null +++ b/common/i18n.c @@ -0,0 +1,237 @@ +/* i18n.c - gettext initialization + * Copyright (C) 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#ifdef HAVE_LOCALE_H +#include +#endif +#ifdef HAVE_LANGINFO_CODESET +#include +#endif + +#include "util.h" +#include "i18n.h" + + +#undef USE_MSGCACHE +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) \ + && !defined(USE_SIMPLE_GETTEXT) && defined(ENABLE_NLS) +# define USE_MSGCACHE 1 +#endif + + +#ifdef USE_MSGCACHE +/* An object to store pointers to static strings and their static + translations. A linked list is not optimal but given that we only + have a few dozen messages it should be acceptable. */ +struct msg_cache_s +{ + struct msg_cache_s *next; + const char *key; + const char *value; +}; + +/* A object to store an lc_messages string and a link to the cache + object. */ +struct msg_cache_heads_s +{ + struct msg_cache_heads_s *next; + struct msg_cache_s *cache; + char lc_messages[1]; +}; + +/* Out static cache of translated messages. We need this because + there is no gettext API to return a translation depending on the + locale. Switching the locale for each access to a translatable + string seems to be too expensive. Note that this is used only for + strings in gpg-agent which are passed to Pinentry. All other + strings are using the regular gettext interface. Note that we can + never release this memory because consumers take the result as + static strings. */ +static struct msg_cache_heads_s *msgcache; + +#endif /*USE_MSGCACHE*/ + + +void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + bindtextdomain (PACKAGE_GT, gnupg_localedir ()); + textdomain (PACKAGE_GT); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, "" ); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); +# endif +#endif +} + + +/* The Assuan agent protocol requires us to transmit utf-8 strings + thus we need a way to temporary switch gettext from native to + utf8. */ +char * +i18n_switchto_utf8 (void) +{ +#ifdef USE_SIMPLE_GETTEXT + /* Return an arbitrary pointer as true value. */ + return gettext_use_utf8 (1) ? (char*)(-1) : NULL; +#elif defined(ENABLE_NLS) + char *orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); +# ifdef HAVE_LANGINFO_CODESET + if (!orig_codeset) + orig_codeset = nl_langinfo (CODESET); +# endif + if (orig_codeset) + { /* We only switch when we are able to restore the codeset later. + Note that bind_textdomain_codeset does only return on memory + errors but not if a codeset is not available. Thus we don't + bother printing a diagnostic here. */ + orig_codeset = xstrdup (orig_codeset); + if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) + { + xfree (orig_codeset); + orig_codeset = NULL; + } + } + return orig_codeset; +#else + return NULL; +#endif +} + +/* Switch back to the saved codeset. */ +void +i18n_switchback (char *saved_codeset) +{ +#ifdef USE_SIMPLE_GETTEXT + gettext_use_utf8 (!!saved_codeset); +#elif defined(ENABLE_NLS) + if (saved_codeset) + { + bind_textdomain_codeset (PACKAGE_GT, saved_codeset); + xfree (saved_codeset); + } +#else + (void)saved_codeset; +#endif +} + + +/* Gettext variant which temporary switches to utf-8 for string. */ +const char * +i18n_utf8 (const char *string) +{ + char *saved = i18n_switchto_utf8 (); + const char *result = _(string); + i18n_switchback (saved); + return result; +} + + +/* A variant of gettext which allows the programmer to specify the + locale to use for translating the message. The function assumes + that utf-8 is used for the encoding. */ +const char * +i18n_localegettext (const char *lc_messages, const char *string) +{ +#if USE_MSGCACHE + const char *result = NULL; + char *saved = NULL; + struct msg_cache_heads_s *mh; + struct msg_cache_s *mc; + + if (!lc_messages) + goto leave; + + /* Lookup in the cache. */ + for (mh = msgcache; mh; mh = mh->next) + if (!strcmp (mh->lc_messages, lc_messages)) + break; + if (mh) + { + /* A cache entry for this local exists - find the string. + Because the system is designed for static strings it is + sufficient to compare the pointers. */ + for (mc = mh->cache; mc; mc = mc->next) + if (mc->key == string) + { + /* Cache hit. */ + result = mc->value; + goto leave; + } + } + + /* Cached miss. Change the locale, translate, reset locale. */ + saved = setlocale (LC_MESSAGES, NULL); + if (!saved) + goto leave; + saved = xtrystrdup (saved); + if (!saved) + goto leave; + if (!setlocale (LC_MESSAGES, lc_messages)) + goto leave; + + bindtextdomain (PACKAGE_GT, LOCALEDIR); + result = gettext (string); + setlocale (LC_MESSAGES, saved); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + + /* Cache the result. */ + if (!mh) + { + /* First use of this locale - create an entry. */ + mh = xtrymalloc (sizeof *mh + strlen (lc_messages)); + if (!mh) + goto leave; + strcpy (mh->lc_messages, lc_messages); + mh->cache = NULL; + mh->next = msgcache; + msgcache = mh; + } + mc = xtrymalloc (sizeof *mc); + if (!mc) + goto leave; + mc->key = string; + mc->value = result; + mc->next = mh->cache; + mh->cache = mc; + + leave: + xfree (saved); + return result? result : _(string); + +#else /*!USE_MSGCACHE*/ + + (void)lc_messages; + return _(string); + +#endif /*!USE_MSGCACHE*/ +} diff --git a/common/i18n.h b/common/i18n.h new file mode 100644 index 0000000..22e8a90 --- /dev/null +++ b/common/i18n.h @@ -0,0 +1,63 @@ +/* i18n.h + * Copyright (C) 1998, 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 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. + */ + +#ifndef GNUPG_COMMON_I18N_H +#define GNUPG_COMMON_I18N_H + + +#ifdef USE_SIMPLE_GETTEXT +# include "../common/w32help.h" +# define _(a) gettext (a) +# define N_(a) (a) +#else +# ifdef HAVE_LOCALE_H +# include +# endif +# ifdef ENABLE_NLS +# include +# define _(a) gettext (a) +# ifdef gettext_noop +# define N_(a) gettext_noop (a) +# else +# define N_(a) (a) +# endif +# else +# define _(a) (a) +# define N_(a) (a) +# define ngettext(a,b,c) ((c)==1? (a):(b)) +# endif +#endif /*!USE_SIMPLE_GETTEXT*/ + +#ifndef GNUPG_GCC_ATTR_FORMAT_ARG +#if __GNUC__ >= 3 /* Actually 2.8 but testing the major is easier. */ +# define GNUPG_GCC_ATTR_FORMAT_ARG(a) __attribute__ ((__format_arg__ (a))) +#else +# define GNUPG_GCC_ATTR_FORMAT_ARG(a) +#endif +#endif + +void i18n_init (void); +char *i18n_switchto_utf8 (void); +void i18n_switchback (char *saved_codeset); +const char *i18n_utf8 (const char *string); +const char *i18n_localegettext (const char *lc_messages, const char *string) + GNUPG_GCC_ATTR_FORMAT_ARG(2); + +/* If a module wants a local L_() function we define it here. */ +#ifdef LunderscoreIMPL +LunderscorePROTO +LunderscoreIMPL +#endif + + +#endif /*GNUPG_COMMON_I18N_H*/ diff --git a/common/init.c b/common/init.c new file mode 100644 index 0000000..86b71e5 --- /dev/null +++ b/common/init.c @@ -0,0 +1,289 @@ +/* init.c - Various initializations + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include + +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#endif +#ifdef HAVE_W32CE_SYSTEM +# include /* For _assuan_w32ce_finish_pipe. */ +#endif + +#include +#include "util.h" +#include "i18n.h" + +/* This object is used to register memory cleanup functions. + Technically they are not needed but they can avoid frequent + questions about un-released memory. Note that we use the system + malloc and not any wrappers. */ +struct mem_cleanup_item_s; +typedef struct mem_cleanup_item_s *mem_cleanup_item_t; + +struct mem_cleanup_item_s +{ + mem_cleanup_item_t next; + void (*func) (void); +}; + +static mem_cleanup_item_t mem_cleanup_list; + + +/* The default error source of the application. This is different + from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the + source file and thus is usable in code shared by applications. + Note that we need to initialize it because otherwise some linkers + (OS X at least) won't find the symbol when linking the t-*.c + files. */ +gpg_err_source_t default_errsource = 0; + + +#ifdef HAVE_W32CE_SYSTEM +static void parse_std_file_handles (int *argcp, char ***argvp); +static void +sleep_on_exit (void) +{ + /* The sshd on CE swallows some of the command output. Sleeping a + while usually helps. */ + Sleep (400); +} +#endif /*HAVE_W32CE_SYSTEM*/ + + +static void +run_mem_cleanup (void) +{ + mem_cleanup_item_t next; + + while (mem_cleanup_list) + { + next = mem_cleanup_list->next; + mem_cleanup_list->func (); + free (mem_cleanup_list); + mem_cleanup_list = next; + } +} + + +void +register_mem_cleanup_func (void (*func)(void)) +{ + mem_cleanup_item_t item; + + for (item = mem_cleanup_list; item; item = item->next) + if (item->func == func) + return; /* Function has already been registered. */ + + item = malloc (sizeof *item); + if (item) + { + item->func = func; + item->next = mem_cleanup_list; + mem_cleanup_list = item; + } +} + + +/* If STRING is not NULL write string to es_stdout or es_stderr. MODE + must be 1 or 2. If STRING is NULL flush the respective stream. */ +static int +writestring_via_estream (int mode, const char *string) +{ + if (mode == 1 || mode == 2) + { + if (string) + return es_fputs (string, mode == 1? es_stdout : es_stderr); + else + return es_fflush (mode == 1? es_stdout : es_stderr); + } + else + return -1; +} + + +/* This function should be the first called after main. */ +void +early_system_init (void) +{ +} + + +/* This function is to be used early at program startup to make sure + that some subsystems are initialized. This is in particular + important for W32 to initialize the sockets so that our socket + emulation code used directly as well as in libassuan may be used. + It should best be called before any I/O is done so that setup + required for logging is ready. ARGCP and ARGVP are the addresses + of the parameters given to main. This function may modify them. + + This function should be called only via the macro + init_common_subsystems. + + CAUTION: This might be called while running suid(root). */ +void +_init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp) +{ + /* Store the error source in a global variable. */ + default_errsource = errsource; + + atexit (run_mem_cleanup); + + /* Try to auto set the character set. */ + set_native_charset (NULL); + +#ifdef HAVE_W32_SYSTEM + /* For W32 we need to initialize the socket layer. This is because + we use recv and send in libassuan as well as at some other + places. */ + { + WSADATA wsadat; + + WSAStartup (0x202, &wsadat); + } +#endif + +#ifdef HAVE_W32CE_SYSTEM + /* Register the sleep exit function before the estream init so that + the sleep will be called after the estream registered atexit + function which flushes the left open estream streams and in + particular es_stdout. */ + atexit (sleep_on_exit); +#endif + + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION)) + { + log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt", + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL)); + } + + /* Initialize the Estream library. */ + gpgrt_init (); + gpgrt_set_alloc_func (gcry_realloc); + + /* Special hack for Windows CE: We extract some options from arg + to setup the standard handles. */ +#ifdef HAVE_W32CE_SYSTEM + parse_std_file_handles (argcp, argvp); +#else + (void)argcp; + (void)argvp; +#endif + + /* Access the standard estreams as early as possible. If we don't + do this the original stdio streams may have been closed when + _es_get_std_stream is first use and in turn it would connect to + the bit bucket. */ + { + int i; + for (i=0; i < 3; i++) + (void)_gpgrt_get_std_stream (i); + } + + /* --version et al shall use estream as well. */ + argparse_register_outfnc (writestring_via_estream); + + /* Logging shall use the standard socket directory as fallback. */ + log_set_socket_dir_cb (gnupg_socketdir); +} + + + +/* WindowsCE uses a very strange way of handling the standard streams. + There is a function SetStdioPath to associate a standard stream + with a file or a device but what we really want is to use pipes as + standard streams. Despite that we implement pipes using a device, + we would have some limitations on the number of open pipes due to + the 3 character limit of device file name. Thus we don't take this + path. Another option would be to install a file system driver with + support for pipes; this would allow us to get rid of the device + name length limitation. However, with GnuPG we can get away be + redefining the standard streams and passing the handles to be used + on the command line. This has also the advantage that it makes + creating a process much easier and does not require the + SetStdioPath set and restore game. The caller needs to pass the + rendezvous ids using up to three options: + + -&S0= -&S1= -&S2= + + They are all optional but they must be the first arguments on the + command line. Parsing stops as soon as an invalid option is found. + These rendezvous ids are then used to finish the pipe creation.*/ +#ifdef HAVE_W32CE_SYSTEM +static void +parse_std_file_handles (int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + const char *s; + assuan_fd_t fd; + int i; + int fixup = 0; + + if (!argc) + return; + + for (argc--, argv++; argc; argc--, argv++) + { + s = *argv; + if (*s == '-' && s[1] == '&' && s[2] == 'S' + && (s[3] == '0' || s[3] == '1' || s[3] == '2') + && s[4] == '=' + && (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null"))) + { + if (s[5] == 'n') + fd = ASSUAN_INVALID_FD; + else + fd = _assuan_w32ce_finish_pipe (atoi (s+5), s[3] != '0'); + _es_set_std_fd (s[3] - '0', (int)fd); + fixup++; + } + else + break; + } + + if (fixup) + { + argc = *argcp; + argc -= fixup; + *argcp = argc; + + argv = *argvp; + for (i=1; i < argc; i++) + argv[i] = argv[i + fixup]; + for (; i < argc + fixup; i++) + argv[i] = NULL; + } + + +} +#endif /*HAVE_W32CE_SYSTEM*/ diff --git a/common/init.h b/common/init.h new file mode 100644 index 0000000..28462a7 --- /dev/null +++ b/common/init.h @@ -0,0 +1,47 @@ +/* init.h - Definitions for init fucntions. + * Copyright (C) 2007, 2012 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_INIT_H +#define GNUPG_COMMON_INIT_H + +#ifndef GPG_ERR_SOURCE_DEFAULT +# error GPG_ERR_SOURCE_DEFAULT is not defined +#elseif GPG_ERR_SOURCE_DEFAULT == GPG_ERR_SOURCE_UNKNOWN +# error GPG_ERR_SOURCE_DEFAULT has default value +#endif + +void register_mem_cleanup_func (void (*func)(void)); + +void early_system_init (void); +void _init_common_subsystems (gpg_err_source_t errsource, + int *argcp, char ***argvp); +#define init_common_subsystems(a,b) \ + _init_common_subsystems (GPG_ERR_SOURCE_DEFAULT, (a), (b)) + +#endif /*GNUPG_COMMON_INIT_H*/ diff --git a/common/iobuf.c b/common/iobuf.c new file mode 100644 index 0000000..d346027 --- /dev/null +++ b/common/iobuf.c @@ -0,0 +1,2698 @@ +/* iobuf.c - File Handling for OpenPGP. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008, + * 2009, 2010, 2011 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#endif +#ifdef __riscos__ +# include +# include +#endif /* __riscos__ */ + +#include + +#include "util.h" +#include "sysutils.h" +#include "iobuf.h" + +/*-- Begin configurable part. --*/ + +/* The size of the internal buffers. + NOTE: If you change this value you MUST also adjust the regression + test "armored_key_8192" in armor.test! */ +#define IOBUF_BUFFER_SIZE 8192 + +/* To avoid a potential DoS with compression packets we better limit + the number of filters in a chain. */ +#define MAX_NESTING_FILTER 64 + +/*-- End configurable part. --*/ + + +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_W32CE_SYSTEM +# define FD_FOR_STDIN (es_fileno (es_stdin)) +# define FD_FOR_STDOUT (es_fileno (es_stdout)) +# else +# define FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE)) +# define FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE)) +# endif +#else /*!HAVE_W32_SYSTEM*/ +# define FD_FOR_STDIN (0) +# define FD_FOR_STDOUT (1) +#endif /*!HAVE_W32_SYSTEM*/ + + +/* The context used by the file filter. */ +typedef struct +{ + gnupg_fd_t fp; /* Open file pointer or handle. */ + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* Flags indicating that fname is not a real file. */ + char fname[1]; /* Name of the file. */ +} file_filter_ctx_t; + +/* The context used by the estream filter. */ +typedef struct +{ + estream_t fp; /* Open estream handle. */ + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* Flags indicating that fname is not a real file. */ + char fname[1]; /* Name of the file. */ +} file_es_filter_ctx_t; + + +/* Object to control the "close cache". */ +struct close_cache_s +{ + struct close_cache_s *next; + gnupg_fd_t fp; + char fname[1]; +}; +typedef struct close_cache_s *close_cache_t; +static close_cache_t close_cache; + + + +#ifdef HAVE_W32_SYSTEM +typedef struct +{ + int sock; + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* Flag indicating that fname is not a real file. */ + char fname[1]; /* Name of the file */ + +} sock_filter_ctx_t; +#endif /*HAVE_W32_SYSTEM*/ + +/* The first partial length header block must be of size 512 to make + * it easier (and more efficient) we use a min. block size of 512 for + * all chunks (but the last one) */ +#define OP_MIN_PARTIAL_CHUNK 512 +#define OP_MIN_PARTIAL_CHUNK_2POW 9 + +/* The context we use for the block filter (used to handle OpenPGP + length information header). */ +typedef struct +{ + int use; + size_t size; + size_t count; + int partial; /* 1 = partial header, 2 in last partial packet. */ + char *buffer; /* Used for partial header. */ + size_t buflen; /* Used size of buffer. */ + int first_c; /* First character of a partial header (which is > 0). */ + int eof; +} +block_filter_ctx_t; + + +/* Local prototypes. */ +static int underflow (iobuf_t a, int clear_pending_eof); +static int underflow_target (iobuf_t a, int clear_pending_eof, size_t target); +static int translate_file_handle (int fd, int for_write); + +/* Sends any pending data to the filter's FILTER function. Note: this + works on the filter and not on the whole pipeline. That is, + iobuf_flush doesn't necessarily cause data to be written to any + underlying file; it just causes any data buffered at the filter A + to be sent to A's filter function. + + If A is a IOBUF_OUTPUT_TEMP filter, then this also enlarges the + buffer by IOBUF_BUFFER_SIZE. + + May only be called on an IOBUF_OUTPUT or IOBUF_OUTPUT_TEMP filters. */ +static int filter_flush (iobuf_t a); + + + +/* This is a replacement for strcmp. Under W32 it does not + distinguish between backslash and slash. */ +static int +fd_cache_strcmp (const char *a, const char *b) +{ +#ifdef HAVE_DOSISH_SYSTEM + for (; *a && *b; a++, b++) + { + if (*a != *b && !((*a == '/' && *b == '\\') + || (*a == '\\' && *b == '/')) ) + break; + } + return *(const unsigned char *)a - *(const unsigned char *)b; +#else + return strcmp (a, b); +#endif +} + +/* + * Invalidate (i.e. close) a cached iobuf + */ +static int +fd_cache_invalidate (const char *fname) +{ + close_cache_t cc; + int rc = 0; + + assert (fname); + if (DBG_IOBUF) + log_debug ("fd_cache_invalidate (%s)\n", fname); + + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + if (DBG_IOBUF) + log_debug (" did (%s)\n", cc->fname); +#ifdef HAVE_W32_SYSTEM + if (!CloseHandle (cc->fp)) + rc = -1; +#else + rc = close (cc->fp); +#endif + cc->fp = GNUPG_INVALID_FD; + } + } + return rc; +} + + +/* Try to sync changes to the disk. This is to avoid data loss during + a system crash in write/close/rename cycle on some file + systems. */ +static int +fd_cache_synchronize (const char *fname) +{ + int err = 0; + +#ifdef HAVE_FSYNC + close_cache_t cc; + + if (DBG_IOBUF) + log_debug ("fd_cache_synchronize (%s)\n", fname); + + for (cc=close_cache; cc; cc = cc->next ) + { + if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + if (DBG_IOBUF) + log_debug (" did (%s)\n", cc->fname); + + err = fsync (cc->fp); + } + } +#else + (void)fname; +#endif /*HAVE_FSYNC*/ + + return err; +} + + +static gnupg_fd_t +direct_open (const char *fname, const char *mode, int mode700) +{ +#ifdef HAVE_W32_SYSTEM + unsigned long da, cd, sm; + HANDLE hfile; + + (void)mode700; + /* Note, that we do not handle all mode combinations */ + + /* According to the ReactOS source it seems that open() of the + * standard MSW32 crt does open the file in shared mode which is + * something new for MS applications ;-) + */ + if (strchr (mode, '+')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + da = GENERIC_READ | GENERIC_WRITE; + cd = OPEN_EXISTING; + sm = FILE_SHARE_READ | FILE_SHARE_WRITE; + } + else if (strchr (mode, 'w')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + da = GENERIC_WRITE; + cd = CREATE_ALWAYS; + sm = FILE_SHARE_WRITE; + } + else + { + da = GENERIC_READ; + cd = OPEN_EXISTING; + sm = FILE_SHARE_READ; + } + +#ifdef HAVE_W32CE_SYSTEM + { + wchar_t *wfname = utf8_to_wchar (fname); + if (wfname) + { + hfile = CreateFile (wfname, da, sm, NULL, cd, + FILE_ATTRIBUTE_NORMAL, NULL); + xfree (wfname); + } + else + hfile = INVALID_HANDLE_VALUE; + } +#else + hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + return hfile; + +#else /*!HAVE_W32_SYSTEM*/ + + int oflag; + int cflag = S_IRUSR | S_IWUSR; + + if (!mode700) + cflag |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + /* Note, that we do not handle all mode combinations */ + if (strchr (mode, '+')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + oflag = O_RDWR; + } + else if (strchr (mode, 'w')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + oflag = O_WRONLY | O_CREAT | O_TRUNC; + } + else + { + oflag = O_RDONLY; + } +#ifdef O_BINARY + if (strchr (mode, 'b')) + oflag |= O_BINARY; +#endif + +#ifdef __riscos__ + { + struct stat buf; + + /* Don't allow iobufs on directories */ + if (!stat (fname, &buf) && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode)) + return __set_errno (EISDIR); + } +#endif + return open (fname, oflag, cflag); + +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* + * Instead of closing an FD we keep it open and cache it for later reuse + * Note that this caching strategy only works if the process does not chdir. + */ +static void +fd_cache_close (const char *fname, gnupg_fd_t fp) +{ + close_cache_t cc; + + assert (fp); + if (!fname || !*fname) + { +#ifdef HAVE_W32_SYSTEM + CloseHandle (fp); +#else + close (fp); +#endif + if (DBG_IOBUF) + log_debug ("fd_cache_close (%d) real\n", (int)fp); + return; + } + /* try to reuse a slot */ + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp == GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + cc->fp = fp; + if (DBG_IOBUF) + log_debug ("fd_cache_close (%s) used existing slot\n", fname); + return; + } + } + /* add a new one */ + if (DBG_IOBUF) + log_debug ("fd_cache_close (%s) new slot created\n", fname); + cc = xcalloc (1, sizeof *cc + strlen (fname)); + strcpy (cc->fname, fname); + cc->fp = fp; + cc->next = close_cache; + close_cache = cc; +} + +/* + * Do a direct_open on FNAME but first try to reuse one from the fd_cache + */ +static gnupg_fd_t +fd_cache_open (const char *fname, const char *mode) +{ + close_cache_t cc; + + assert (fname); + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + gnupg_fd_t fp = cc->fp; + cc->fp = GNUPG_INVALID_FD; + if (DBG_IOBUF) + log_debug ("fd_cache_open (%s) using cached fp\n", fname); +#ifdef HAVE_W32_SYSTEM + if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff) + { + log_error ("rewind file failed on handle %p: ec=%d\n", + fp, (int) GetLastError ()); + fp = GNUPG_INVALID_FD; + } +#else + if (lseek (fp, 0, SEEK_SET) == (off_t) - 1) + { + log_error ("can't rewind fd %d: %s\n", fp, strerror (errno)); + fp = GNUPG_INVALID_FD; + } +#endif + return fp; + } + } + if (DBG_IOBUF) + log_debug ("fd_cache_open (%s) not cached\n", fname); + return direct_open (fname, mode, 0); +} + + +static int +file_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + file_filter_ctx_t *a = opaque; + gnupg_fd_t f = a->fp; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + (void)chain; /* Not used. */ + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* We need a buffer. */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { +#ifdef HAVE_W32_SYSTEM + unsigned long nread; + + nbytes = 0; + if (!ReadFile (f, buf, size, &nread, NULL)) + { + int ec = (int) GetLastError (); + if (ec != ERROR_BROKEN_PIPE) + { + rc = gpg_error_from_errno (ec); + log_error ("%s: read error: ec=%d\n", a->fname, ec); + } + } + else if (!nread) + { + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = nread; + } + +#else + + int n; + + nbytes = 0; + do + { + n = read (f, buf, size); + } + while (n == -1 && errno == EINTR); + if (n == -1) + { /* error */ + if (errno != EPIPE) + { + rc = gpg_error_from_syserror (); + log_error ("%s: read error: %s\n", + a->fname, strerror (errno)); + } + } + else if (!n) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = n; + } +#endif + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { +#ifdef HAVE_W32_SYSTEM + byte *p = buf; + unsigned long n; + + nbytes = size; + do + { + if (size && !WriteFile (f, p, nbytes, &n, NULL)) + { + int ec = (int) GetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("%s: write error: ec=%d\n", a->fname, ec); + break; + } + p += n; + nbytes -= n; + } + while (nbytes); + nbytes = p - buf; +#else + byte *p = buf; + int n; + + nbytes = size; + do + { + do + { + n = write (f, p, nbytes); + } + while (n == -1 && errno == EINTR); + if (n > 0) + { + p += n; + nbytes -= n; + } + } + while (n != -1 && nbytes); + if (n == -1) + { + rc = gpg_error_from_syserror (); + log_error ("%s: write error: %s\n", a->fname, strerror (errno)); + } + nbytes = p - buf; +#endif + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->keep_open = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "file_filter(fd)", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT) + { + if (DBG_IOBUF) + log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f)); + if (!a->keep_open) + fd_cache_close (a->no_cache ? NULL : a->fname, f); + } + xfree (a); /* We can free our context now. */ + } + + return rc; +} + + +/* Similar to file_filter but using the estream system. */ +static int +file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + file_es_filter_ctx_t *a = opaque; + estream_t f = a->fp; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + (void)chain; /* Not used. */ + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* We need a buffer. */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { + nbytes = 0; + rc = es_read (f, buf, size, &nbytes); + if (rc == -1) + { /* error */ + rc = gpg_error_from_syserror (); + log_error ("%s: read error: %s\n", a->fname, strerror (errno)); + } + else if (!nbytes) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { + byte *p = buf; + size_t nwritten; + + nbytes = size; + do + { + nwritten = 0; + if (es_write (f, p, nbytes, &nwritten)) + { + rc = gpg_error_from_syserror (); + log_error ("%s: write error: %s\n", + a->fname, strerror (errno)); + break; + } + p += nwritten; + nbytes -= nwritten; + } + while (nbytes); + nbytes = p - buf; + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "estream_filter", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (f != es_stdin && f != es_stdout) + { + if (DBG_IOBUF) + log_debug ("%s: es_fclose %p\n", a->fname, f); + if (!a->keep_open) + es_fclose (f); + } + f = NULL; + xfree (a); /* We can free our context now. */ + } + + return rc; +} + + +#ifdef HAVE_W32_SYSTEM +/* Because network sockets are special objects under Lose32 we have to + use a dedicated filter for them. */ +static int +sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + sock_filter_ctx_t *a = opaque; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + (void)chain; + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* need a buffer */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { + int nread; + + nread = recv (a->sock, buf, size, 0); + if (nread == SOCKET_ERROR) + { + int ec = (int) WSAGetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("socket read error: ec=%d\n", ec); + } + else if (!nread) + { + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = nread; + } + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { + byte *p = buf; + int n; + + nbytes = size; + do + { + n = send (a->sock, p, nbytes, 0); + if (n == SOCKET_ERROR) + { + int ec = (int) WSAGetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("socket write error: ec=%d\n", ec); + break; + } + p += n; + nbytes -= n; + } + while (nbytes); + nbytes = p - buf; + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->keep_open = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "sock_filter", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (!a->keep_open) + closesocket (a->sock); + xfree (a); /* we can free our context now */ + } + return rc; +} +#endif /*HAVE_W32_SYSTEM*/ + +/**************** + * This is used to implement the block write mode. + * Block reading is done on a byte by byte basis in readbyte(), + * without a filter + */ +static int +block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, + size_t * ret_len) +{ + block_filter_ctx_t *a = opaque; + char *buf = (char *)buffer; + size_t size = *ret_len; + int c, needed, rc = 0; + char *p; + + if (control == IOBUFCTRL_UNDERFLOW) + { + size_t n = 0; + + p = buf; + assert (size); /* need a buffer */ + if (a->eof) /* don't read any further */ + rc = -1; + while (!rc && size) + { + if (!a->size) + { /* get the length bytes */ + if (a->partial == 2) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + else if (a->partial) + { + /* These OpenPGP introduced huffman like encoded length + * bytes are really a mess :-( */ + if (a->first_c) + { + c = a->first_c; + a->first_c = 0; + } + else if ((c = iobuf_get (chain)) == -1) + { + log_error ("block_filter: 1st length byte missing\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + if (c < 192) + { + a->size = c; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else if (c < 224) + { + a->size = (c - 192) * 256; + if ((c = iobuf_get (chain)) == -1) + { + log_error + ("block_filter: 2nd length byte missing\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + a->size += c + 192; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else if (c == 255) + { + a->size = (size_t)iobuf_get (chain) << 24; + a->size |= iobuf_get (chain) << 16; + a->size |= iobuf_get (chain) << 8; + if ((c = iobuf_get (chain)) == -1) + { + log_error ("block_filter: invalid 4 byte length\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + a->size |= c; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else + { /* Next partial body length. */ + a->size = 1 << (c & 0x1f); + } + /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */ + } + else + BUG (); + } + + while (!rc && size && a->size) + { + needed = size < a->size ? size : a->size; + c = iobuf_read (chain, p, needed); + if (c < needed) + { + if (c == -1) + c = 0; + log_error + ("block_filter %p: read error (size=%lu,a->size=%lu)\n", + a, (ulong) size + c, (ulong) a->size + c); + rc = GPG_ERR_BAD_DATA; + } + else + { + size -= c; + a->size -= c; + p += c; + n += c; + } + } + } + *ret_len = n; + } + else if (control == IOBUFCTRL_FLUSH) + { + if (a->partial) + { /* the complicated openpgp scheme */ + size_t blen, n, nbytes = size + a->buflen; + + assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); + if (nbytes < OP_MIN_PARTIAL_CHUNK) + { + /* not enough to write a partial block out; so we store it */ + if (!a->buffer) + a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); + memcpy (a->buffer + a->buflen, buf, size); + a->buflen += size; + } + else + { /* okay, we can write out something */ + /* do this in a loop to use the most efficient block lengths */ + p = buf; + do + { + /* find the best matching block length - this is limited + * by the size of the internal buffering */ + for (blen = OP_MIN_PARTIAL_CHUNK * 2, + c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes; + blen *= 2, c++) + ; + blen /= 2; + c--; + /* write the partial length header */ + assert (c <= 0x1f); /*;-) */ + c |= 0xe0; + iobuf_put (chain, c); + if ((n = a->buflen)) + { /* write stuff from the buffer */ + assert (n == OP_MIN_PARTIAL_CHUNK); + if (iobuf_write (chain, a->buffer, n)) + rc = gpg_error_from_syserror (); + a->buflen = 0; + nbytes -= n; + } + if ((n = nbytes) > blen) + n = blen; + if (n && iobuf_write (chain, p, n)) + rc = gpg_error_from_syserror (); + p += n; + nbytes -= n; + } + while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK); + /* store the rest in the buffer */ + if (!rc && nbytes) + { + assert (!a->buflen); + assert (nbytes < OP_MIN_PARTIAL_CHUNK); + if (!a->buffer) + a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); + memcpy (a->buffer, p, nbytes); + a->buflen = nbytes; + } + } + } + else + BUG (); + } + else if (control == IOBUFCTRL_INIT) + { + if (DBG_IOBUF) + log_debug ("init block_filter %p\n", a); + if (a->partial) + a->count = 0; + else if (a->use == IOBUF_INPUT) + a->count = a->size = 0; + else + a->count = a->size; /* force first length bytes */ + a->eof = 0; + a->buffer = NULL; + a->buflen = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "block_filter", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (a->use == IOBUF_OUTPUT) + { /* write the end markers */ + if (a->partial) + { + u32 len; + /* write out the remaining bytes without a partial header + * the length of this header may be 0 - but if it is + * the first block we are not allowed to use a partial header + * and frankly we can't do so, because this length must be + * a power of 2. This is _really_ complicated because we + * have to check the possible length of a packet prior + * to it's creation: a chain of filters becomes complicated + * and we need a lot of code to handle compressed packets etc. + * :-((((((( + */ + /* construct header */ + len = a->buflen; + /*log_debug("partial: remaining length=%u\n", len ); */ + if (len < 192) + rc = iobuf_put (chain, len); + else if (len < 8384) + { + if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192))) + rc = iobuf_put (chain, ((len - 192) % 256)); + } + else + { /* use a 4 byte header */ + if (!(rc = iobuf_put (chain, 0xff))) + if (!(rc = iobuf_put (chain, (len >> 24) & 0xff))) + if (!(rc = iobuf_put (chain, (len >> 16) & 0xff))) + if (!(rc = iobuf_put (chain, (len >> 8) & 0xff))) + rc = iobuf_put (chain, len & 0xff); + } + if (!rc && len) + rc = iobuf_write (chain, a->buffer, len); + if (rc) + { + log_error ("block_filter: write error: %s\n", + strerror (errno)); + rc = gpg_error_from_syserror (); + } + xfree (a->buffer); + a->buffer = NULL; + a->buflen = 0; + } + else + BUG (); + } + else if (a->size) + { + log_error ("block_filter: pending bytes!\n"); + } + if (DBG_IOBUF) + log_debug ("free block_filter %p\n", a); + xfree (a); /* we can free our context now */ + } + + return rc; +} + +#define MAX_IOBUF_DESC 32 +/* + * Fill the buffer by the description of iobuf A. + * The buffer size should be MAX_IOBUF_DESC (or larger). + * Returns BUF as (const char *). + */ +static const char * +iobuf_desc (iobuf_t a, byte *buf) +{ + size_t len = MAX_IOBUF_DESC; + + if (! a || ! a->filter) + memcpy (buf, "?", 2); + else + a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, buf, &len); + + return buf; +} + +static void +print_chain (iobuf_t a) +{ + if (!DBG_IOBUF) + return; + for (; a; a = a->chain) + { + byte desc[MAX_IOBUF_DESC]; + + log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n", + a->no, a->subno, iobuf_desc (a, desc), a->filter_eof, + (int) a->d.start, (int) a->d.len); + } +} + +int +iobuf_print_chain (iobuf_t a) +{ + print_chain (a); + return 0; +} + +iobuf_t +iobuf_alloc (int use, size_t bufsize) +{ + iobuf_t a; + static int number = 0; + + assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP + || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP); + if (bufsize == 0) + { + log_bug ("iobuf_alloc() passed a bufsize of 0!\n"); + bufsize = IOBUF_BUFFER_SIZE; + } + + a = xcalloc (1, sizeof *a); + a->use = use; + a->d.buf = xmalloc (bufsize); + a->d.size = bufsize; + a->no = ++number; + a->subno = 0; + a->real_fname = NULL; + return a; +} + +int +iobuf_close (iobuf_t a) +{ + iobuf_t a_chain; + size_t dummy_len = 0; + int rc = 0; + + for (; a; a = a_chain) + { + byte desc[MAX_IOBUF_DESC]; + int rc2 = 0; + + a_chain = a->chain; + + if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a))) + log_error ("filter_flush failed on close: %s\n", gpg_strerror (rc)); + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: close '%s'\n", + a->no, a->subno, iobuf_desc (a, desc)); + + if (a->filter && (rc2 = a->filter (a->filter_ov, IOBUFCTRL_FREE, + a->chain, NULL, &dummy_len))) + log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc)); + if (! rc && rc2) + /* Whoops! An error occurred. Save it in RC if we haven't + already recorded an error. */ + rc = rc2; + + xfree (a->real_fname); + if (a->d.buf) + { + memset (a->d.buf, 0, a->d.size); /* erase the buffer */ + xfree (a->d.buf); + } + xfree (a); + } + return rc; +} + +int +iobuf_cancel (iobuf_t a) +{ + const char *s; + iobuf_t a2; + int rc; +#if defined(HAVE_W32_SYSTEM) || defined(__riscos__) + char *remove_name = NULL; +#endif + + if (a && a->use == IOBUF_OUTPUT) + { + s = iobuf_get_real_fname (a); + if (s && *s) + { +#if defined(HAVE_W32_SYSTEM) || defined(__riscos__) + remove_name = xstrdup (s); +#else + remove (s); +#endif + } + } + + /* send a cancel message to all filters */ + for (a2 = a; a2; a2 = a2->chain) + { + size_t dummy; + if (a2->filter) + a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy); + } + + rc = iobuf_close (a); +#if defined(HAVE_W32_SYSTEM) || defined(__riscos__) + if (remove_name) + { + /* Argg, MSDOS does not allow removing open files. So + * we have to do it here */ +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wtmp = utf8_to_wchar (remove_name); + if (wtmp) + DeleteFile (wtmp); + xfree (wtmp); +#else + remove (remove_name); +#endif + xfree (remove_name); + } +#endif + return rc; +} + + +iobuf_t +iobuf_temp (void) +{ + return iobuf_alloc (IOBUF_OUTPUT_TEMP, IOBUF_BUFFER_SIZE); +} + +iobuf_t +iobuf_temp_with_content (const char *buffer, size_t length) +{ + iobuf_t a; + int i; + + a = iobuf_alloc (IOBUF_INPUT_TEMP, length); + assert (length == a->d.size); + /* memcpy (a->d.buf, buffer, length); */ + for (i=0; i < length; i++) + a->d.buf[i] = buffer[i]; + a->d.len = length; + + return a; +} + + +int +iobuf_is_pipe_filename (const char *fname) +{ + if (!fname || (*fname=='-' && !fname[1]) ) + return 1; + return check_special_filename (fname, 0, 1) != -1; +} + + +static iobuf_t +do_open (const char *fname, int special_filenames, + int use, const char *opentype, int mode700) +{ + iobuf_t a; + gnupg_fd_t fp; + file_filter_ctx_t *fcx; + size_t len = 0; + int print_only = 0; + int fd; + byte desc[MAX_IOBUF_DESC]; + + assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT); + + if (special_filenames + /* NULL or '-'. */ + && (!fname || (*fname == '-' && !fname[1]))) + { + if (use == IOBUF_INPUT) + { + fp = FD_FOR_STDIN; + fname = "[stdin]"; + } + else + { + fp = FD_FOR_STDOUT; + fname = "[stdout]"; + } + print_only = 1; + } + else if (!fname) + return NULL; + else if (special_filenames + && (fd = check_special_filename (fname, 0, 1)) != -1) + return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1), + opentype); + else + { + if (use == IOBUF_INPUT) + fp = fd_cache_open (fname, opentype); + else + fp = direct_open (fname, opentype, mode700); + if (fp == GNUPG_INVALID_FD) + return NULL; + } + + a = iobuf_alloc (use, IOBUF_BUFFER_SIZE); + fcx = xmalloc (sizeof *fcx + strlen (fname)); + fcx->fp = fp; + fcx->print_only_name = print_only; + strcpy (fcx->fname, fname); + if (!print_only) + a->real_fname = xstrdup (fname); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n", + a->no, a->subno, fname, iobuf_desc (a, desc), FD2INT (fcx->fp)); + + return a; +} + +iobuf_t +iobuf_open (const char *fname) +{ + return do_open (fname, 1, IOBUF_INPUT, "rb", 0); +} + +iobuf_t +iobuf_create (const char *fname, int mode700) +{ + return do_open (fname, 1, IOBUF_OUTPUT, "wb", mode700); +} + +iobuf_t +iobuf_openrw (const char *fname) +{ + return do_open (fname, 0, IOBUF_OUTPUT, "r+b", 0); +} + + +static iobuf_t +do_iobuf_fdopen (int fd, const char *mode, int keep_open) +{ + iobuf_t a; + gnupg_fd_t fp; + file_filter_ctx_t *fcx; + size_t len; + + fp = INT2FD (fd); + + a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, + IOBUF_BUFFER_SIZE); + fcx = xmalloc (sizeof *fcx + 20); + fcx->fp = fp; + fcx->print_only_name = 1; + fcx->keep_open = keep_open; + sprintf (fcx->fname, "[fd %d]", fd); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: fdopen%s '%s'\n", + a->no, a->subno, keep_open? "_nc":"", fcx->fname); + iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); + return a; +} + + +iobuf_t +iobuf_fdopen (int fd, const char *mode) +{ + return do_iobuf_fdopen (fd, mode, 0); +} + +iobuf_t +iobuf_fdopen_nc (int fd, const char *mode) +{ + return do_iobuf_fdopen (fd, mode, 1); +} + + +iobuf_t +iobuf_esopen (estream_t estream, const char *mode, int keep_open) +{ + iobuf_t a; + file_es_filter_ctx_t *fcx; + size_t len = 0; + + a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, + IOBUF_BUFFER_SIZE); + fcx = xtrymalloc (sizeof *fcx + 30); + fcx->fp = estream; + fcx->print_only_name = 1; + fcx->keep_open = keep_open; + sprintf (fcx->fname, "[fd %p]", estream); + a->filter = file_es_filter; + a->filter_ov = fcx; + file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: esopen%s '%s'\n", + a->no, a->subno, keep_open? "_nc":"", fcx->fname); + return a; +} + + +iobuf_t +iobuf_sockopen (int fd, const char *mode) +{ + iobuf_t a; +#ifdef HAVE_W32_SYSTEM + sock_filter_ctx_t *scx; + size_t len; + + a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, + IOBUF_BUFFER_SIZE); + scx = xmalloc (sizeof *scx + 25); + scx->sock = fd; + scx->print_only_name = 1; + sprintf (scx->fname, "[sock %d]", fd); + a->filter = sock_filter; + a->filter_ov = scx; + sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname); + iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); +#else + a = iobuf_fdopen (fd, mode); +#endif + return a; +} + +int +iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval) +{ + byte desc[MAX_IOBUF_DESC]; + + if (cmd == IOBUF_IOCTL_KEEP_OPEN) + { + /* Keep system filepointer/descriptor open. This was used in + the past by http.c; this ioctl is not directly used + anymore. */ + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n", + a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc), + intval); + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + b->keep_open = intval; + return 0; + } +#ifdef HAVE_W32_SYSTEM + else if (!a->chain && a->filter == sock_filter) + { + sock_filter_ctx_t *b = a->filter_ov; + b->keep_open = intval; + return 0; + } +#endif + } + else if (cmd == IOBUF_IOCTL_INVALIDATE_CACHE) + { + if (DBG_IOBUF) + log_debug ("iobuf-*.*: ioctl '%s' invalidate\n", + ptrval ? (char *) ptrval : "?"); + if (!a && !intval && ptrval) + { + if (fd_cache_invalidate (ptrval)) + return -1; + return 0; + } + } + else if (cmd == IOBUF_IOCTL_NO_CACHE) + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n", + a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc), + intval); + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + b->no_cache = intval; + return 0; + } +#ifdef HAVE_W32_SYSTEM + else if (!a->chain && a->filter == sock_filter) + { + sock_filter_ctx_t *b = a->filter_ov; + b->no_cache = intval; + return 0; + } +#endif + } + else if (cmd == IOBUF_IOCTL_FSYNC) + { + /* Do a fsync on the open fd and return any errors to the caller + of iobuf_ioctl. Note that we work on a file name here. */ + if (DBG_IOBUF) + log_debug ("iobuf-*.*: ioctl '%s' fsync\n", + ptrval? (const char*)ptrval:""); + + if (!a && !intval && ptrval) + { + return fd_cache_synchronize (ptrval); + } + } + + + return -1; +} + + +/**************** + * Register an i/o filter. + */ +int +iobuf_push_filter (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov) +{ + return iobuf_push_filter2 (a, f, ov, 0); +} + +int +iobuf_push_filter2 (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov, int rel_ov) +{ + iobuf_t b; + size_t dummy_len = 0; + int rc = 0; + + if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a))) + return rc; + + if (a->subno >= MAX_NESTING_FILTER) + { + log_error ("i/o filter too deeply nested - corrupted data?\n"); + return GPG_ERR_BAD_DATA; + } + + /* We want to create a new filter and put it in front of A. A + simple implementation would do: + + b = iobuf_alloc (...); + b->chain = a; + return a; + + This is a bit problematic: A is the head of the pipeline and + there are potentially many pointers to it. Requiring the caller + to update all of these pointers is a burden. + + An alternative implementation would add a level of indirection. + For instance, we could use a pipeline object, which contains a + pointer to the first filter in the pipeline. This is not what we + do either. + + Instead, we allocate a new buffer (B) and copy the first filter's + state into that and use the initial buffer (A) for the new + filter. One limitation of this approach is that it is not + practical to maintain a pointer to a specific filter's state. + + Before: + + A + | + v 0x100 0x200 + +----------+ +----------+ + | filter x |--------->| filter y |---->.... + +----------+ +----------+ + + After: B + | + v 0x300 + +----------+ + A | filter x | + | +----------+ + v 0x100 ^ v 0x200 + +----------+ +----------+ + | filter w | | filter y |---->.... + +----------+ +----------+ + + Note: filter x's address changed from 0x100 to 0x300, but A still + points to the head of the pipeline. + */ + + b = xmalloc (sizeof *b); + memcpy (b, a, sizeof *b); + /* fixme: it is stupid to keep a copy of the name at every level + * but we need the name somewhere because the name known by file_filter + * may have been released when we need the name of the file */ + b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL; + /* remove the filter stuff from the new stream */ + a->filter = NULL; + a->filter_ov = NULL; + a->filter_ov_owner = 0; + a->filter_eof = 0; + if (a->use == IOBUF_OUTPUT_TEMP) + /* A TEMP filter buffers any data sent to it; it does not forward + any data down the pipeline. If we add a new filter to the + pipeline, it shouldn't also buffer data. It should send it + downstream to be buffered. Thus, the correct type for a filter + added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not + IOBUF_OUTPUT_TEMP. */ + { + a->use = IOBUF_OUTPUT; + + /* When pipeline is written to, the temp buffer's size is + increased accordingly. We don't need to allocate a 10 MB + buffer for a non-terminal filter. Just use the default + size. */ + a->d.size = IOBUF_BUFFER_SIZE; + } + else if (a->use == IOBUF_INPUT_TEMP) + /* Same idea as above. */ + { + a->use = IOBUF_INPUT; + a->d.size = IOBUF_BUFFER_SIZE; + } + + /* The new filter (A) gets a new buffer. + + If the pipeline is an output or temp pipeline, then giving the + buffer to the new filter means that data that was written before + the filter was pushed gets sent to the filter. That's clearly + wrong. + + If the pipeline is an input pipeline, then giving the buffer to + the new filter (A) means that data that has read from (B), but + not yet read from the pipeline won't be processed by the new + filter (A)! That's certainly not what we want. */ + a->d.buf = xmalloc (a->d.size); + a->d.len = 0; + a->d.start = 0; + + /* disable nlimit for the new stream */ + a->ntotal = b->ntotal + b->nbytes; + a->nlimit = a->nbytes = 0; + a->nofast = 0; + /* make a link from the new stream to the original stream */ + a->chain = b; + + /* setup the function on the new stream */ + a->filter = f; + a->filter_ov = ov; + a->filter_ov_owner = rel_ov; + + a->subno = b->subno + 1; + + if (DBG_IOBUF) + { + byte desc[MAX_IOBUF_DESC]; + log_debug ("iobuf-%d.%d: push '%s'\n", + a->no, a->subno, iobuf_desc (a, desc)); + print_chain (a); + } + + /* now we can initialize the new function if we have one */ + if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain, + NULL, &dummy_len))) + log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc)); + return rc; +} + +/**************** + * Remove an i/o filter. + */ +int +iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov) +{ + iobuf_t b; + size_t dummy_len = 0; + int rc = 0; + byte desc[MAX_IOBUF_DESC]; + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop '%s'\n", + a->no, a->subno, iobuf_desc (a, desc)); + if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP) + { + /* This should be the last filter in the pipeline. */ + assert (! a->chain); + return 0; + } + if (!a->filter) + { /* this is simple */ + b = a->chain; + assert (b); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + return 0; + } + for (b = a; b; b = b->chain) + if (b->filter == f && (!ov || b->filter_ov == ov)) + break; + if (!b) + log_bug ("iobuf_pop_filter(): filter function not found\n"); + + /* flush this stream if it is an output stream */ + if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b))) + { + log_error ("filter_flush failed in iobuf_pop_filter: %s\n", + gpg_strerror (rc)); + return rc; + } + /* and tell the filter to free it self */ + if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain, + NULL, &dummy_len))) + { + log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (b->filter_ov && b->filter_ov_owner) + { + xfree (b->filter_ov); + b->filter_ov = NULL; + } + + + /* and see how to remove it */ + if (a == b && !b->chain) + log_bug ("can't remove the last filter from the chain\n"); + else if (a == b) + { /* remove the first iobuf from the chain */ + /* everything from b is copied to a. This is save because + * a flush has been done on the to be removed entry + */ + b = a->chain; + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno); + } + else if (!b->chain) + { /* remove the last iobuf from the chain */ + log_bug ("Ohh jeee, trying to remove a head filter\n"); + } + else + { /* remove an intermediate iobuf from the chain */ + log_bug ("Ohh jeee, trying to remove an intermediate filter\n"); + } + + return rc; +} + + +/**************** + * read underflow: read at least one byte into the buffer and return + * the first byte or -1 on EOF. + */ +static int +underflow (iobuf_t a, int clear_pending_eof) +{ + return underflow_target (a, clear_pending_eof, 1); +} + + +/**************** + * read underflow: read TARGET bytes into the buffer and return + * the first byte or -1 on EOF. + */ +static int +underflow_target (iobuf_t a, int clear_pending_eof, size_t target) +{ + size_t len; + int rc; + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: buffer size: %d; still buffered: %d => space for %d bytes\n", + a->no, a->subno, + (int) a->d.size, (int) (a->d.len - a->d.start), + (int) (a->d.size - (a->d.len - a->d.start))); + + if (a->use == IOBUF_INPUT_TEMP) + /* By definition, there isn't more data to read into the + buffer. */ + return -1; + + assert (a->use == IOBUF_INPUT); + + /* If there is still some buffered data, then move it to the start + of the buffer and try to fill the end of the buffer. (This is + useful if we are called from iobuf_peek().) */ + assert (a->d.start <= a->d.len); + a->d.len -= a->d.start; + memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len); + a->d.start = 0; + + if (a->d.len < target && a->filter_eof) + /* The last time we tried to read from this filter, we got an EOF. + We couldn't return the EOF, because there was buffered data. + Since there is no longer any buffered data, return the + error. */ + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n", + a->no, a->subno); + if (! clear_pending_eof) + return -1; + + if (a->chain) + /* A filter follows this one. Free this filter. */ + { + iobuf_t b = a->chain; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n", + a->no, a->subno); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + print_chain (a); + } + else + a->filter_eof = 0; /* for the top level filter */ + return -1; /* return one(!) EOF */ + } + + if (a->d.len == 0 && a->error) + /* The last time we tried to read from this filter, we got an + error. We couldn't return the error, because there was + buffered data. Since there is no longer any buffered data, + return the error. */ + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pending error (%s) returned\n", + a->no, a->subno, gpg_strerror (a->error)); + return -1; + } + + if (a->filter && ! a->filter_eof && ! a->error) + /* We have a filter function and the last time we tried to read we + didn't get an EOF or an error. Try to fill the buffer. */ + { + /* Be careful to account for any buffered data. */ + len = a->d.size - a->d.len; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n", + a->no, a->subno, (ulong) len); + if (len == 0) + /* There is no space for more data. Don't bother calling + A->FILTER. */ + rc = 0; + else + rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, + &a->d.buf[a->d.len], &len); + a->d.len += len; + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes\n", + a->no, a->subno, + rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc), + (ulong) len); +/* if( a->no == 1 ) */ +/* log_hexdump (" data:", a->d.buf, len); */ + + if (rc == -1) + /* EOF. */ + { + size_t dummy_len = 0; + + /* Tell the filter to free itself */ + if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, + NULL, &dummy_len))) + log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); + + /* Free everything except for the internal buffer. */ + if (a->filter_ov && a->filter_ov_owner) + xfree (a->filter_ov); + a->filter_ov = NULL; + a->filter = NULL; + a->filter_eof = 1; + + if (clear_pending_eof && a->d.len == 0 && a->chain) + /* We don't need to keep this filter around at all: + + - we got an EOF + - we have no buffered data + - a filter follows this one. + + Unlink this filter. */ + { + iobuf_t b = a->chain; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n", + a->no, a->subno); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + + print_chain (a); + + return -1; + } + else if (a->d.len == 0) + /* We can't unlink this filter (it is the only one in the + pipeline), but we can immediately return EOF. */ + return -1; + } + else if (rc) + /* Record the error. */ + { + a->error = rc; + + if (a->d.len == 0) + /* There is no buffered data. Immediately return EOF. */ + return -1; + } + } + + assert (a->d.start <= a->d.len); + if (a->d.start < a->d.len) + return a->d.buf[a->d.start++]; + + /* EOF. */ + return -1; +} + + +static int +filter_flush (iobuf_t a) +{ + size_t len; + int rc; + + if (a->use == IOBUF_OUTPUT_TEMP) + { /* increase the temp buffer */ + size_t newsize = a->d.size + IOBUF_BUFFER_SIZE; + + if (DBG_IOBUF) + log_debug ("increasing temp iobuf from %lu to %lu\n", + (ulong) a->d.size, (ulong) newsize); + + a->d.buf = xrealloc (a->d.buf, newsize); + a->d.size = newsize; + return 0; + } + else if (a->use != IOBUF_OUTPUT) + log_bug ("flush on non-output iobuf\n"); + else if (!a->filter) + log_bug ("filter_flush: no filter\n"); + len = a->d.len; + rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len); + if (!rc && len != a->d.len) + { + log_info ("filter_flush did not write all!\n"); + rc = GPG_ERR_INTERNAL; + } + else if (rc) + a->error = rc; + a->d.len = 0; + + return rc; +} + + +int +iobuf_readbyte (iobuf_t a) +{ + int c; + + if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP) + { + log_bug ("iobuf_readbyte called on a non-INPUT pipeline!\n"); + return -1; + } + + assert (a->d.start <= a->d.len); + + if (a->nlimit && a->nbytes >= a->nlimit) + return -1; /* forced EOF */ + + if (a->d.start < a->d.len) + { + c = a->d.buf[a->d.start++]; + } + else if ((c = underflow (a, 1)) == -1) + return -1; /* EOF */ + + assert (a->d.start <= a->d.len); + + /* Note: if underflow doesn't return EOF, then it returns the first + byte that was read and advances a->d.start appropriately. */ + + a->nbytes++; + return c; +} + + +int +iobuf_read (iobuf_t a, void *buffer, unsigned int buflen) +{ + unsigned char *buf = (unsigned char *)buffer; + int c, n; + + if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP) + { + log_bug ("iobuf_read called on a non-INPUT pipeline!\n"); + return -1; + } + + if (a->nlimit) + { + /* Handle special cases. */ + for (n = 0; n < buflen; n++) + { + if ((c = iobuf_readbyte (a)) == -1) + { + if (!n) + return -1; /* eof */ + break; + } + + if (buf) + { + *buf = c; + buf++; + } + } + return n; + } + + n = 0; + do + { + if (n < buflen && a->d.start < a->d.len) + /* Drain the buffer. */ + { + unsigned size = a->d.len - a->d.start; + if (size > buflen - n) + size = buflen - n; + if (buf) + memcpy (buf, a->d.buf + a->d.start, size); + n += size; + a->d.start += size; + if (buf) + buf += size; + } + if (n < buflen) + /* Draining the internal buffer didn't fill BUFFER. Call + underflow to read more data into the filter's internal + buffer. */ + { + if ((c = underflow (a, 1)) == -1) + /* EOF. If we managed to read something, don't return EOF + now. */ + { + a->nbytes += n; + return n ? n : -1 /*EOF*/; + } + if (buf) + *buf++ = c; + n++; + } + } + while (n < buflen); + a->nbytes += n; + return n; +} + + + +int +iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) +{ + int n = 0; + + assert (buflen > 0); + assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP); + + if (buflen > a->d.size) + /* We can't peek more than we can buffer. */ + buflen = a->d.size; + + /* Try to fill the internal buffer with enough data to satisfy the + request. */ + while (buflen > a->d.len - a->d.start) + { + if (underflow_target (a, 0, buflen) == -1) + /* EOF. We can't read any more. */ + break; + + /* Underflow consumes the first character (it's the return + value). unget() it by resetting the "file position". */ + assert (a->d.start == 1); + a->d.start = 0; + } + + n = a->d.len - a->d.start; + if (n > buflen) + n = buflen; + + if (n == 0) + /* EOF. */ + return -1; + + memcpy (buf, &a->d.buf[a->d.start], n); + + return n; +} + + + + +int +iobuf_writebyte (iobuf_t a, unsigned int c) +{ + int rc; + + if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) + { + log_bug ("iobuf_writebyte called on an input pipeline!\n"); + return -1; + } + + if (a->d.len == a->d.size) + if ((rc=filter_flush (a))) + return rc; + + assert (a->d.len < a->d.size); + a->d.buf[a->d.len++] = c; + return 0; +} + + +int +iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen) +{ + const unsigned char *buf = (const unsigned char *)buffer; + int rc; + + if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) + { + log_bug ("iobuf_write called on an input pipeline!\n"); + return -1; + } + + do + { + if (buflen && a->d.len < a->d.size) + { + unsigned size = a->d.size - a->d.len; + if (size > buflen) + size = buflen; + memcpy (a->d.buf + a->d.len, buf, size); + buflen -= size; + buf += size; + a->d.len += size; + } + if (buflen) + { + rc = filter_flush (a); + if (rc) + return rc; + } + } + while (buflen); + return 0; +} + + +int +iobuf_writestr (iobuf_t a, const char *buf) +{ + if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) + { + log_bug ("iobuf_writestr called on an input pipeline!\n"); + return -1; + } + + return iobuf_write (a, buf, strlen (buf)); +} + + + +int +iobuf_write_temp (iobuf_t dest, iobuf_t source) +{ + assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); + assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP); + + iobuf_flush_temp (source); + return iobuf_write (dest, source->d.buf, source->d.len); +} + +size_t +iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen) +{ + byte desc[MAX_IOBUF_DESC]; + size_t n; + + while (1) + { + int rc = filter_flush (a); + if (rc) + log_bug ("Flushing iobuf %d.%d (%s) from iobuf_temp_to_buffer failed. Ignoring.\n", + a->no, a->subno, iobuf_desc (a, desc)); + if (! a->chain) + break; + a = a->chain; + } + + n = a->d.len; + if (n > buflen) + n = buflen; + memcpy (buffer, a->d.buf, n); + return n; +} + +/* Copies the data from the input iobuf SOURCE to the output iobuf + DEST until either an error is encountered or EOF is reached. + Returns the number of bytes copies. */ +size_t +iobuf_copy (iobuf_t dest, iobuf_t source) +{ + char *temp; + /* Use a 32 KB buffer. */ + const size_t temp_size = 32 * 1024; + + size_t nread; + size_t nwrote = 0; + int err; + + assert (source->use == IOBUF_INPUT || source->use == IOBUF_INPUT_TEMP); + assert (dest->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); + + if (iobuf_error (dest)) + return -1; + + temp = xmalloc (temp_size); + while (1) + { + nread = iobuf_read (source, temp, temp_size); + if (nread == -1) + /* EOF. */ + break; + + err = iobuf_write (dest, temp, nread); + if (err) + break; + nwrote += nread; + } + + /* Burn the buffer. */ + wipememory (temp, sizeof (temp)); + xfree (temp); + + return nwrote; +} + + +void +iobuf_flush_temp (iobuf_t temp) +{ + if (temp->use == IOBUF_INPUT || temp->use == IOBUF_INPUT_TEMP) + log_bug ("iobuf_flush_temp called on an input pipeline!\n"); + while (temp->chain) + iobuf_pop_filter (temp, temp->filter, NULL); +} + + +void +iobuf_set_limit (iobuf_t a, off_t nlimit) +{ + if (nlimit) + a->nofast = 1; + else + a->nofast = 0; + a->nlimit = nlimit; + a->ntotal += a->nbytes; + a->nbytes = 0; +} + + + +off_t +iobuf_get_filelength (iobuf_t a, int *overflow) +{ + if (overflow) + *overflow = 0; + + /* Hmmm: file_filter may have already been removed */ + for ( ; a->chain; a = a->chain ) + ; + + if (a->filter != file_filter) + return 0; + + { + file_filter_ctx_t *b = a->filter_ov; + gnupg_fd_t fp = b->fp; + +#if defined(HAVE_W32_SYSTEM) + ulong size; + static int (* __stdcall get_file_size_ex) (void *handle, + LARGE_INTEGER *r_size); + static int get_file_size_ex_initialized; + + if (!get_file_size_ex_initialized) + { + void *handle; + + handle = dlopen ("kernel32.dll", RTLD_LAZY); + if (handle) + { + get_file_size_ex = dlsym (handle, "GetFileSizeEx"); + if (!get_file_size_ex) + dlclose (handle); + } + get_file_size_ex_initialized = 1; + } + + if (get_file_size_ex) + { + /* This is a newer system with GetFileSizeEx; we use this + then because it seem that GetFileSize won't return a + proper error in case a file is larger than 4GB. */ + LARGE_INTEGER exsize; + + if (get_file_size_ex (fp, &exsize)) + { + if (!exsize.u.HighPart) + return exsize.u.LowPart; + if (overflow) + *overflow = 1; + return 0; + } + } + else + { + if ((size=GetFileSize (fp, NULL)) != 0xffffffff) + return size; + } + log_error ("GetFileSize for handle %p failed: %s\n", + fp, w32_strerror (0)); +#else /*!HAVE_W32_SYSTEM*/ + { + struct stat st; + + if ( !fstat (FD2INT (fp), &st) ) + return st.st_size; + log_error("fstat() failed: %s\n", strerror(errno) ); + } +#endif /*!HAVE_W32_SYSTEM*/ + } + + return 0; +} + + +int +iobuf_get_fd (iobuf_t a) +{ + for (; a->chain; a = a->chain) + ; + + if (a->filter != file_filter) + return -1; + + { + file_filter_ctx_t *b = a->filter_ov; + gnupg_fd_t fp = b->fp; + + return FD2INT (fp); + } +} + + +off_t +iobuf_tell (iobuf_t a) +{ + return a->ntotal + a->nbytes; +} + + +#if !defined(HAVE_FSEEKO) && !defined(fseeko) + +#ifdef HAVE_LIMITS_H +# include +#endif +#ifndef LONG_MAX +# define LONG_MAX ((long) ((unsigned long) -1 >> 1)) +#endif +#ifndef LONG_MIN +# define LONG_MIN (-1 - LONG_MAX) +#endif + +/**************** + * A substitute for fseeko, for hosts that don't have it. + */ +static int +fseeko (FILE * stream, off_t newpos, int whence) +{ + while (newpos != (long) newpos) + { + long pos = newpos < 0 ? LONG_MIN : LONG_MAX; + if (fseek (stream, pos, whence) != 0) + return -1; + newpos -= pos; + whence = SEEK_CUR; + } + return fseek (stream, (long) newpos, whence); +} +#endif + +int +iobuf_seek (iobuf_t a, off_t newpos) +{ + file_filter_ctx_t *b = NULL; + + if (a->use == IOBUF_OUTPUT || a->use == IOBUF_INPUT) + { + /* Find the last filter in the pipeline. */ + for (; a->chain; a = a->chain) + ; + + if (a->filter != file_filter) + return -1; + + b = a->filter_ov; + +#ifdef HAVE_W32_SYSTEM + if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff) + { + log_error ("SetFilePointer failed on handle %p: ec=%d\n", + b->fp, (int) GetLastError ()); + return -1; + } +#else + if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1) + { + log_error ("can't lseek: %s\n", strerror (errno)); + return -1; + } +#endif + /* Discard the buffer it is not a temp stream. */ + a->d.len = 0; + } + a->d.start = 0; + a->nbytes = 0; + a->nlimit = 0; + a->nofast = 0; + a->ntotal = newpos; + a->error = 0; + + /* It is impossible for A->CHAIN to be non-NULL. If A is an INPUT + or OUTPUT buffer, then we find the last filter, which is defined + as A->CHAIN being NULL. If A is a TEMP filter, then A must be + the only filter in the pipe: when iobuf_push_filter adds a filter + to the front of a pipeline, it sets the new filter to be an + OUTPUT filter if the pipeline is an OUTPUT or TEMP pipeline and + to be an INPUT filter if the pipeline is an INPUT pipeline. + Thus, only the last filter in a TEMP pipeline can be a */ + + /* remove filters, but the last */ + if (a->chain) + log_debug ("iobuf_pop_filter called in iobuf_seek - please report\n"); + while (a->chain) + iobuf_pop_filter (a, a->filter, NULL); + + return 0; +} + + +const char * +iobuf_get_real_fname (iobuf_t a) +{ + if (a->real_fname) + return a->real_fname; + + /* the old solution */ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + return b->print_only_name ? NULL : b->fname; + } + + return NULL; +} + +const char * +iobuf_get_fname (iobuf_t a) +{ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + return b->fname; + } + return NULL; +} + +const char * +iobuf_get_fname_nonnull (iobuf_t a) +{ + const char *fname; + + fname = iobuf_get_fname (a); + return fname? fname : "[?]"; +} + + +/**************** + * Enable or disable partial body length mode (RFC 4880 4.2.2.4). + * + * If LEN is 0, this disables partial block mode by popping the + * partial body length filter, which which must be the most recently + * added filter. + * + * If LEN is non-zero, it pushes a partial body length filter. If + * this is a read filter, LEN must be the length byte from the first + * chunk and A should be position just after this first partial body + * length header. + */ +void +iobuf_set_partial_body_length_mode (iobuf_t a, size_t len) +{ + if (!len) + /* Disable partial body length mode. */ + { + if (a->use == IOBUF_INPUT) + log_debug ("iobuf_pop_filter called in set_partial_block_mode" + " - please report\n"); + + log_assert (a->filter == block_filter); + iobuf_pop_filter (a, block_filter, NULL); + } + else + /* Enabled partial body length mode. */ + { + block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx); + ctx->use = a->use; + ctx->partial = 1; + ctx->size = 0; + ctx->first_c = len; + iobuf_push_filter (a, block_filter, ctx); + } +} + + + +unsigned int +iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, + unsigned *length_of_buffer, unsigned *max_length) +{ + int c; + char *buffer = (char *)*addr_of_buffer; + unsigned length = *length_of_buffer; + unsigned nbytes = 0; + unsigned maxlen = *max_length; + char *p; + + /* The code assumes that we have space for at least a newline and a + NUL character in the buffer. This requires at least 2 bytes. We + don't complicate the code by handling the stupid corner case, but + simply assert that it can't happen. */ + assert (length >= 2 || maxlen >= 2); + + if (!buffer || length <= 1) + /* must allocate a new buffer */ + { + length = 256 <= maxlen ? 256 : maxlen; + buffer = xrealloc (buffer, length); + *addr_of_buffer = (unsigned char *)buffer; + *length_of_buffer = length; + } + + p = buffer; + while ((c = iobuf_get (a)) != -1) + { + *p++ = c; + nbytes++; + if (c == '\n') + break; + + if (nbytes == length - 1) + /* We don't have enough space to add a \n and a \0. Increase + the buffer size. */ + { + if (length == maxlen) + /* We reached the buffer's size limit! */ + { + /* Skip the rest of the line. */ + while (c != '\n' && (c = iobuf_get (a)) != -1) + ; + + /* p is pointing at the last byte in the buffer. We + always terminate the line with "\n\0" so overwrite + the previous byte with a \n. */ + assert (p > buffer); + p[-1] = '\n'; + + /* Indicate truncation. */ + *max_length = 0; + break; + } + + length += length < 1024 ? 256 : 1024; + if (length > maxlen) + length = maxlen; + + buffer = xrealloc (buffer, length); + *addr_of_buffer = (unsigned char *)buffer; + *length_of_buffer = length; + p = buffer + nbytes; + } + } + /* Add the terminating NUL. */ + *p = 0; + + /* Return the number of characters written to the buffer including + the newline, but not including the terminating NUL. */ + return nbytes; +} + +static int +translate_file_handle (int fd, int for_write) +{ +#if defined(HAVE_W32CE_SYSTEM) + /* This is called only with one of the special filenames. Under + W32CE the FD here is not a file descriptor but a rendezvous id, + thus we need to finish the pipe first. */ + fd = _assuan_w32ce_finish_pipe (fd, for_write); +#elif defined(HAVE_W32_SYSTEM) + { + int x; + + (void)for_write; + + if (fd == 0) + x = (int) GetStdHandle (STD_INPUT_HANDLE); + else if (fd == 1) + x = (int) GetStdHandle (STD_OUTPUT_HANDLE); + else if (fd == 2) + x = (int) GetStdHandle (STD_ERROR_HANDLE); + else + x = fd; + + if (x == -1) + log_debug ("GetStdHandle(%d) failed: ec=%d\n", + fd, (int) GetLastError ()); + + fd = x; + } +#else + (void)for_write; +#endif + return fd; +} + + +void +iobuf_skip_rest (iobuf_t a, unsigned long n, int partial) +{ + if ( partial ) + { + for (;;) + { + if (a->nofast || a->d.start >= a->d.len) + { + if (iobuf_readbyte (a) == -1) + { + break; + } + } + else + { + unsigned long count = a->d.len - a->d.start; + a->nbytes += count; + a->d.start = a->d.len; + } + } + } + else + { + unsigned long remaining = n; + while (remaining > 0) + { + if (a->nofast || a->d.start >= a->d.len) + { + if (iobuf_readbyte (a) == -1) + { + break; + } + --remaining; + } + else + { + unsigned long count = a->d.len - a->d.start; + if (count > remaining) + { + count = remaining; + } + a->nbytes += count; + a->d.start += count; + remaining -= count; + } + } + } +} diff --git a/common/iobuf.h b/common/iobuf.h new file mode 100644 index 0000000..22e02da --- /dev/null +++ b/common/iobuf.h @@ -0,0 +1,622 @@ +/* iobuf.h - I/O buffer + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_IOBUF_H +#define GNUPG_COMMON_IOBUF_H + +/* An iobuf is basically a filter in a pipeline. + + Consider the following command, which consists of three filters + that are chained together: + + $ cat file | base64 --decode | gunzip + + The first filter reads the file from the file system and sends that + data to the second filter. The second filter decodes + base64-encoded data and sends the data to the third and last + filter. The last filter decompresses the data and the result is + displayed on the terminal. The iobuf system works in the same way + where each iobuf is a filter and the individual iobufs can be + chained together. + + There are number of predefined filters. iobuf_open(), for + instance, creates a filter that reads from a specified file. And, + iobuf_temp_with_content() creates a filter that returns some + specified contents. There are also filters for writing content. + iobuf_openrw opens a file for writing. iobuf_temp creates a filter + that writes data to a fixed-sized buffer. + + To chain filters together, you use the iobuf_push_filter() + function. The filters are chained together using the chain field + in the iobuf_t. + + A pipeline can only be used for reading (IOBUF_INPUT) or for + writing (IOBUF_OUTPUT / IOBUF_OUTPUT_TEMP). When reading, data + flows from the last filter towards the first. That is, the user + calls iobuf_read(), the module reads from the first filter, which + gets its input from the second filter, etc. When writing, data + flows from the first filter towards the last. In this case, when + the user calls iobuf_write(), the data is written to the first + filter, which writes the transformed data to the second filter, + etc. + + An iobuf_t contains some state about the filter. For instance, it + indicates if the filter has already returned EOF (filter_eof) and + the next filter in the pipeline, if any (chain). It also contains + a function pointer, filter. This is a generic function. It is + called when input is needed or output is available. In this case + it is passed a pointer to some filter-specific persistent state + (filter_ov), the actual operation, the next filter in the chain, if + any, and a buffer that either contains the contents to write, if + the pipeline is setup to write data, or is the place to store data, + if the pipeline is setup to read data. + + + Unlike a Unix pipeline, an IOBUF pipeline can return EOF multiple + times. This is similar to the following: + + { cat file1; cat file2; } | grep foo + + However, instead of grep seeing a single stream, grep would see + each byte stream followed by an EOF marker. (When a filter returns + EOF, the EOF is returned to the user exactly once and then the + filter is removed from the pipeline.) */ + +/* For estream_t. */ +#include + +#include "../common/types.h" +#include "../common/sysutils.h" + +#define DBG_IOBUF iobuf_debug_mode + +/* Filter control modes. */ +enum + { + IOBUFCTRL_INIT = 1, + IOBUFCTRL_FREE = 2, + IOBUFCTRL_UNDERFLOW = 3, + IOBUFCTRL_FLUSH = 4, + IOBUFCTRL_DESC = 5, + IOBUFCTRL_CANCEL = 6, + IOBUFCTRL_USER = 16 + }; + + +/* Command codes for iobuf_ioctl. */ +typedef enum + { + IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */ + IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */ + IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */ + IOBUF_IOCTL_FSYNC = 4 /* Uses ptrval. */ + } iobuf_ioctl_t; + +enum iobuf_use + { + /* Pipeline is in input mode. The data flows from the end to the + beginning. That is, when reading from the pipeline, the first + filter gets its input from the second filter, etc. */ + IOBUF_INPUT, + /* Pipeline is in input mode. The last filter in the pipeline is + a temporary buffer from which the data is "read". */ + IOBUF_INPUT_TEMP, + /* Pipeline is in output mode. The data flows from the beginning + to the end. That is, when writing to the pipeline, the user + writes to the first filter, which transforms the data and sends + it to the second filter, etc. */ + IOBUF_OUTPUT, + /* Pipeline is in output mode. The last filter in the pipeline is + a temporary buffer that grows as necessary. */ + IOBUF_OUTPUT_TEMP + }; + + +typedef struct iobuf_struct *iobuf_t; +typedef struct iobuf_struct *IOBUF; /* Compatibility with gpg 1.4. */ + +/* fixme: we should hide most of this stuff */ +struct iobuf_struct +{ + /* The type of filter. Either IOBUF_INPUT, IOBUF_OUTPUT or + IOBUF_OUTPUT_TEMP. */ + enum iobuf_use use; + + /* nlimit can be changed using iobuf_set_limit. If non-zero, it is + the number of additional bytes that can be read from the filter + before EOF is forcefully returned. */ + off_t nlimit; + /* nbytes if the number of bytes that have been read (using + iobuf_get / iobuf_readbyte / iobuf_read) since the last call to + iobuf_set_limit. */ + off_t nbytes; + + /* The number of bytes read prior to the last call to + iobuf_set_limit. Thus, the total bytes read (i.e., the position + of stream) is ntotal + nbytes. */ + off_t ntotal; + + /* Whether we need to read from the filter one byte at a time or + whether we can do bulk reads. We need to read one byte at a time + if a limit (set via iobuf_set_limit) is active. */ + int nofast; + + /* A buffer for unread/unwritten data. + + For an output pipeline (IOBUF_OUTPUT), this is the data that has + not yet been written to the filter. Consider a simple pipeline + consisting of a single stage, which writes to a file. When you + write to the pipeline (iobuf_writebyte or iobuf_write), the data + is first stored in this buffer. Only when the buffer is full or + you call iobuf_flush() is FILTER actually called and the data + written to the file. + + For an input pipeline (IOBUF_INPUT), this is the data that has + been read from this filter, but not yet been read from the + preceding filter (or the user, if this filter is the head of the + pipeline). Again, consider a simple pipeline consisting of a + single stage. This stage reads from a file. If you read a + single byte (iobuf_get) and the buffer is empty, then FILTER is + called to fill the buffer. In this case, a single byte is not + requested, but the whole buffer is filled (if possible). */ + struct + { + /* Size of the buffer. */ + size_t size; + /* Number of bytes at the beginning of the buffer that have + already been consumed. (In other words: the index of the first + byte that hasn't been consumed.) This is only non-zero for + input filters. */ + size_t start; + /* The number of bytes in the buffer including any bytes that have + been consumed. */ + size_t len; + /* The buffer itself. */ + byte *buf; + } d; + + /* When FILTER is called to read some data, it may read some data + and then return EOF. We can't return the EOF immediately. + Instead, we note that we observed the EOF and when the buffer is + finally empty, we return the EOF. */ + int filter_eof; + /* Like filter_eof, when FILTER is called to read some data, it may + read some data and then return an error. We can't return the + error (in the form of an EOF) immediately. Instead, we note that + we observed the error and when the buffer is finally empty, we + return the EOF. */ + int error; + + /* The callback function to read data from the filter, etc. See + iobuf_filter_push for details. */ + int (*filter) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len); + /* An opaque pointer that can be used for local filter state. This + is passed as the first parameter to FILTER. */ + void *filter_ov; + /* Whether the iobuf code should free(filter_ov) when destroying the + filter. */ + int filter_ov_owner; + + /* When using iobuf_open, iobuf_create, iobuf_openrw to open a file, + the file's name is saved here. This is used to delete the file + when an output pipeline (IOBUF_OUPUT) is canceled + (iobuf_cancel). */ + char *real_fname; + + /* The next filter in the pipeline. */ + iobuf_t chain; + + /* This field is for debugging. Each time a filter is allocated + (via iobuf_alloc()), a monotonically increasing counter is + incremented and this field is set to the new value. This field + should only be accessed via the iobuf_io macro. */ + int no; + + /* The number of filters in the pipeline following (not including) + this one. When you call iobuf_push_filter or iobuf_push_filter2, + this value is used to check the length of the pipeline if the + pipeline already contains 65 stages then these functions fail. + This amount of nesting typically indicates corrupted data or an + active denial of service attack. */ + int subno; +}; + +#ifndef EXTERN_UNLESS_MAIN_MODULE +#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE) +#define EXTERN_UNLESS_MAIN_MODULE extern +#else +#define EXTERN_UNLESS_MAIN_MODULE +#endif +#endif +EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode; + + +/* Returns whether the specified filename corresponds to a pipe. In + particular, this function checks if FNAME is "-" and, if special + filenames are enabled (see check_special_filename), whether + FNAME is a special filename. */ +int iobuf_is_pipe_filename (const char *fname); + +/* Allocate a new filter. This filter doesn't have a function + assigned to it. Thus you need to manually set IOBUF->FILTER and + IOBUF->FILTER_OV, if required. This function is intended to help + create a new primary source or primary sink, i.e., the last filter + in the pipeline. + + USE is IOBUF_INPUT, IOBUF_INPUT_TEMP, IOBUF_OUTPUT or + IOBUF_OUTPUT_TEMP. + + BUFSIZE is the desired internal buffer size (that is, the size of + the typical read / write request). */ +iobuf_t iobuf_alloc (int use, size_t bufsize); + +/* Create an output filter that simply buffers data written to it. + This is useful for collecting data for later processing. The + buffer can be written to in the usual way (iobuf_write, etc.). The + data can later be extracted using iobuf_write_temp() or + iobuf_temp_to_buffer(). */ +iobuf_t iobuf_temp (void); + +/* Create an input filter that contains some data for reading. */ +iobuf_t iobuf_temp_with_content (const char *buffer, size_t length); + +/* Create an input file filter that reads from a file. If FNAME is + '-', reads from stdin. If special filenames are enabled + (iobuf_enable_special_filenames), then interprets special + filenames. */ +iobuf_t iobuf_open (const char *fname); + +/* Create an output file filter that writes to a file. If FNAME is + NULL or '-', writes to stdout. If special filenames are enabled + (iobuf_enable_special_filenames), then interprets special + filenames. If FNAME is not NULL, '-' or a special filename, the + file is opened for writing. If the file exists, it is truncated. + If MODE700 is TRUE, the file is created with mode 600. Otherwise, + mode 666 is used. */ +iobuf_t iobuf_create (const char *fname, int mode700); + +/* Create an output file filter that writes to a specified file. + Neither '-' nor special file names are recognized. */ +iobuf_t iobuf_openrw (const char *fname); + +/* Create a file filter using an existing file descriptor. If MODE + contains the letter 'w', creates an output filter. Otherwise, + creates an input filter. Note: MODE must reflect the file + descriptors actual mode! When the filter is destroyed, the file + descriptor is closed. */ +iobuf_t iobuf_fdopen (int fd, const char *mode); + +/* Like iobuf_fdopen, but doesn't close the file descriptor when the + filter is destroyed. */ +iobuf_t iobuf_fdopen_nc (int fd, const char *mode); + +/* Create a filter using an existing estream. If MODE contains the + letter 'w', creates an output filter. Otherwise, creates an input + filter. If KEEP_OPEN is TRUE, then the stream is not closed when + the filter is destroyed. Otherwise, the stream is closed when the + filter is destroyed. */ +iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open); + +/* Create a filter using an existing socket. On Windows creates a + special socket filter. On non-Windows systems simply, this simply + calls iobuf_fdopen. */ +iobuf_t iobuf_sockopen (int fd, const char *mode); + +/* Set various options / perform different actions on a PIPELINE. See + the IOBUF_IOCTL_* macros above. */ +int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval); + +/* Close a pipeline. The filters in the pipeline are first flushed + using iobuf_flush, if they are output filters, and then + IOBUFCTRL_FREE is called on each filter. + + If any filter returns a non-zero value in response to the + IOBUFCTRL_FREE, that first such non-zero value is returned. Note: + processing is not aborted in this case. If all filters are freed + successfully, 0 is returned. */ +int iobuf_close (iobuf_t iobuf); + +/* Calls IOBUFCTRL_CANCEL on each filter in the pipeline. Then calls + io_close() on the pipeline. Finally, if the pipeline is an output + pipeline, deletes the file. Returns the result of calling + iobuf_close on the pipeline. */ +int iobuf_cancel (iobuf_t iobuf); + +/* Add a new filter to the front of a pipeline. A is the head of the + pipeline. F is the filter implementation. OV is an opaque pointer + that is passed to F and is normally used to hold any internal + state, such as a file pointer. + + Note: you may only maintain a reference to an iobuf_t as a + reference to the head of the pipeline. That is, don't think about + setting a pointer in OV to point to the filter's iobuf_t. This is + because when we add a new filter to a pipeline, we memcpy the state + in A into new buffer. This has the advantage that there is no need + to update any references to the pipeline when a filter is added or + removed, but it also means that a filter's state moves around in + memory. + + The behavior of the filter function is determined by the value of + the control parameter: + + IOBUFCTRL_INIT: Called this value just before the filter is + linked into the pipeline. This can be used to initialize + internal data structures. + + IOBUFCTRL_FREE: Called with this value just before the filter is + removed from the pipeline. Normally used to release internal + data structures, close a file handle, etc. + + IOBUFCTRL_UNDERFLOW: Called with this value to fill the passed + buffer with more data. *LEN is the size of the buffer. Before + returning, it should be set to the number of bytes which were + written into the buffer. The function must return 0 to + indicate success, -1 on EOF and a GPG_ERR_xxxxx code for any + error. + + Note: this function may both return data and indicate an error + or EOF. In this case, it simply writes the data to BUF, sets + *LEN and returns the appropriate return code. The implication + is that if an error occurs and no data has yet been written, it + is essential that *LEN be set to 0! + + IOBUFCTRL_FLUSH: Called with this value to write out any + collected data. *LEN is the number of bytes in BUF that need + to be written out. Returns 0 on success and a GPG_ERR_* code + otherwise. *LEN must be set to the number of bytes that were + written out. + + IOBUFCTRL_CANCEL: Called with this value when iobuf_cancel() is + called on the pipeline. + + IOBUFCTRL_DESC: Called with this value to get a human-readable + description of the filter. *LEN is the size of the buffer. + The description is filled into BUF, NUL-terminated. Always + returns 0. + */ +int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, + size_t * len), void *ov); +/* This variant of iobuf_push_filter allows the called to indicate + that OV should be freed when this filter is freed. That is, if + REL_OV is TRUE, then when the filter is popped or freed OV will be + freed after the filter function is called with control set to + IOBUFCTRL_FREE. */ +int iobuf_push_filter2 (iobuf_t a, + int (*f) (void *opaque, int control, iobuf_t chain, + byte * buf, size_t * len), void *ov, + int rel_ov); + +/* Pop the top filter. The top filter must have the filter function F + and the cookie OV. The cookie check is ignored if OV is NULL. */ +int iobuf_pop_filter (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov); + +/* Used for debugging. Prints out the chain using log_debug if + IOBUF_DEBUG_MODE is not 0. */ +int iobuf_print_chain (iobuf_t a); + +/* Indicate that some error occurred on the specified filter. */ +#define iobuf_set_error(a) do { (a)->error = 1; } while(0) + +/* Return any pending error on filter A. */ +#define iobuf_error(a) ((a)->error) + +/* Limit the amount of additional data that may be read from the + filter. That is, if you've already read 100 bytes from A and you + set the limit to 50, then you can read up to an additional 50 bytes + (i.e., a total of 150 bytes) before EOF is forcefully returned. + Setting NLIMIT to 0 removes any active limit. + + Note: using iobuf_seek removes any currently enforced limit! */ +void iobuf_set_limit (iobuf_t a, off_t nlimit); + +/* Returns the number of bytes that have been read from the pipeline. + Note: the result is undefined for IOBUF_OUTPUT and IOBUF_OUTPUT_TEMP + pipelines! */ +off_t iobuf_tell (iobuf_t a); + +/* There are two cases: + + - If A is an INPUT or OUTPUT pipeline, then the last filter in the + pipeline is found. If that is not a file filter, -1 is returned. + Otherwise, an fseek(..., SEEK_SET) is performed on the file + descriptor. + + - If A is a TEMP pipeline and the *first* (and thus only filter) is + a TEMP filter, then the "file position" is effectively unchanged. + That is, data is appended to the buffer and the seek does not + cause the size of the buffer to grow. + + If no error occurred, then any limit previous set by + iobuf_set_limit() is cleared. Further, any error on the filter + (the file filter or the temp filter) is cleared. + + Returns 0 on success and -1 if an error occurs. */ +int iobuf_seek (iobuf_t a, off_t newpos); + +/* Read a single byte. If a filter has no more data, returns -1 to + indicate the EOF. Generally, you don't want to use this function, + but instead prefer the iobuf_get macro, which is faster if there is + data in the internal buffer. */ +int iobuf_readbyte (iobuf_t a); + +/* Get a byte from the iobuf; must check for eof prior to this + function. This function returns values in the range 0 .. 255 or -1 + to indicate EOF. iobuf_get_noeof() does not return -1 to indicate + EOF, but masks the returned value to be in the range 0 .. 255. */ +#define iobuf_get(a) \ + ( ((a)->nofast || (a)->d.start >= (a)->d.len )? \ + iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) ) +#define iobuf_get_noeof(a) (iobuf_get((a))&0xff) + +/* Fill BUF with up to BUFLEN bytes. If a filter has no more data, + returns -1 to indicate the EOF. Otherwise returns the number of + bytes read. */ +int iobuf_read (iobuf_t a, void *buf, unsigned buflen); + +/* Read a line of input (including the '\n') from the pipeline. + + The semantics are the same as for fgets(), but if the buffer is too + short a larger one will be allocated up to *MAX_LENGTH and the end + of the line except the trailing '\n' discarded. (Thus, + *ADDR_OF_BUFFER must be allocated using malloc().) If the buffer + is enlarged, then *LENGTH_OF_BUFFER will be updated to reflect the + new size. If the line is truncated, then *MAX_LENGTH will be set + to 0. If *ADDR_OF_BUFFER is NULL, a buffer is allocated using + malloc(). + + A line is considered a byte stream ending in a '\n'. Returns the + number of characters written to the buffer (i.e., excluding any + discarded characters due to truncation). Thus, use this instead of + strlen(buffer) to determine the length of the string as this is + unreliable if the input contains NUL characters. + + EOF is indicated by a line of length zero. + + The last LF may be missing due to an EOF. */ +unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, + unsigned *length_of_buffer, unsigned *max_length); + +/* Read up to BUFLEN bytes from pipeline A. Note: this function can't + return more than the pipeline's internal buffer size. The return + value is the number of bytes actually written to BUF. If the + filter returns EOF, then this function returns -1. + + This function does not clear any pending EOF. That is, if the + pipeline consists of two filters and the first one returns EOF + during the peek, then the subsequent iobuf_read* will still return + EOF before returning the data from the second filter. */ +int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen); + +/* Write a byte to the pipeline. Returns 0 on success and an error + code otherwise. */ +int iobuf_writebyte (iobuf_t a, unsigned c); + +/* Alias for iobuf_writebyte. */ +#define iobuf_put(a,c) iobuf_writebyte(a,c) + +/* Write a sequence of bytes to the pipeline. Returns 0 on success + and an error code otherwise. */ +int iobuf_write (iobuf_t a, const void *buf, unsigned buflen); + +/* Write a string (not including the NUL terminator) to the pipeline. + Returns 0 on success and an error code otherwise. */ +int iobuf_writestr (iobuf_t a, const char *buf); + +/* Flushes the pipeline removing all filters but the sink (the last + filter) in the process. */ +void iobuf_flush_temp (iobuf_t temp); + +/* Flushes the pipeline SOURCE removing all filters but the sink (the + last filter) in the process (i.e., it calls + iobuf_flush_temp(source)) and then writes the data to the pipeline + DEST. Note: this doesn't free (iobuf_close()) SOURCE. Both SOURCE + and DEST must be output pipelines. */ +int iobuf_write_temp (iobuf_t dest, iobuf_t source); + +/* Flushes each filter in the pipeline (i.e., sends any buffered data + to the filter by calling IOBUFCTRL_FLUSH). Then, copies up to the + first BUFLEN bytes from the last filter's internal buffer (which + will only be non-empty if it is a temp filter) to the buffer + BUFFER. Returns the number of bytes actually copied. */ +size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen); + +/* Copies the data from the input iobuf SOURCE to the output iobuf + DEST until either an error is encountered or EOF is reached. + Returns the number of bytes successfully written. If an error + occurred, then any buffered bytes are not returned to SOURCE and are + effectively lost. To check if an error occurred, use + iobuf_error. */ +size_t iobuf_copy (iobuf_t dest, iobuf_t source); + +/* Return the size of any underlying file. This only works with + file_filter based pipelines. + + On Win32, it is sometimes not possible to determine the size of + files larger than 4GB. In this case, *OVERFLOW (if not NULL) is + set to 1. Otherwise, *OVERFLOW is set to 0. */ +off_t iobuf_get_filelength (iobuf_t a, int *overflow); +#define IOBUF_FILELENGTH_LIMIT 0xffffffff + +/* Return the file descriptor designating the underlying file. This + only works with file_filter based pipelines. */ +int iobuf_get_fd (iobuf_t a); + +/* Return the real filename, if available. This only supports + pipelines that end in file filters. Returns NULL if not + available. */ +const char *iobuf_get_real_fname (iobuf_t a); + +/* Return the filename or a description thereof. For instance, for + iobuf_open("-"), this will return "[stdin]". This only supports + pipelines that end in file filters. Returns NULL if not + available. */ +const char *iobuf_get_fname (iobuf_t a); + +/* Like iobuf_getfname, but instead of returning NULL if no + description is available, return "[?]". */ +const char *iobuf_get_fname_nonnull (iobuf_t a); + +/* Pushes a filter on the pipeline that interprets the datastream as + an OpenPGP data block whose length is encoded using partial body + length headers (see Section 4.2.2.4 of RFC 4880). Concretely, it + just returns / writes the data and finishes the packet with an + EOF. */ +void iobuf_set_partial_body_length_mode (iobuf_t a, size_t len); + +/* If PARTIAL is set, then read from the pipeline until the first EOF + is returned. + + If PARTIAL is 0, then read up to N bytes or until the first EOF is + returned. + + Recall: a filter can return EOF. In this case, it and all + preceding filters are popped from the pipeline and the next read is + from the following filter (which may or may not return EOF). */ +void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial); + +#define iobuf_where(a) "[don't know]" + +/* Each time a filter is allocated (via iobuf_alloc()), a + monotonically increasing counter is incremented and this field is + set to the new value. This macro returns that number. */ +#define iobuf_id(a) ((a)->no) + +#define iobuf_get_temp_buffer(a) ( (a)->d.buf ) +#define iobuf_get_temp_length(a) ( (a)->d.len ) + +/* Whether the filter uses an in-memory buffer. */ +#define iobuf_is_temp(a) ( (a)->use == IOBUF_OUTPUT_TEMP ) + +#endif /*GNUPG_COMMON_IOBUF_H*/ diff --git a/common/keyserver.h b/common/keyserver.h new file mode 100644 index 0000000..850798e --- /dev/null +++ b/common/keyserver.h @@ -0,0 +1,73 @@ +/* keyserver.h - Public definitions for gpg keyserver helpers. + * Copyright (C) 2001, 2002, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_KEYSERVER_H +#define GNUPG_COMMON_KEYSERVER_H + +#define KEYSERVER_PROTO_VERSION 1 + +/* These are usable for return codes for the gpgkeys_ process, and + also KEY FAILED codes. */ +#define KEYSERVER_OK 0 /* not an error */ +#define KEYSERVER_INTERNAL_ERROR 1 /* gpgkeys_ internal error */ +#define KEYSERVER_NOT_SUPPORTED 2 /* operation not supported */ +#define KEYSERVER_VERSION_ERROR 3 /* VERSION mismatch */ +#define KEYSERVER_GENERAL_ERROR 4 /* keyserver internal error */ +#define KEYSERVER_NO_MEMORY 5 /* out of memory */ +#define KEYSERVER_KEY_NOT_FOUND 6 /* key not found */ +#define KEYSERVER_KEY_EXISTS 7 /* key already exists */ +#define KEYSERVER_KEY_INCOMPLETE 8 /* key incomplete (EOF) */ +#define KEYSERVER_UNREACHABLE 9 /* unable to contact keyserver */ + +/* Must be 127 due to shell internal magic. */ +#define KEYSERVER_SCHEME_NOT_FOUND 127 + +/* Object to hold information pertaining to a keyserver; it also + allows building a list of keyservers. Note that g10/options.h has + a typedef for this. FIXME: We should make use of the + parse_uri_t. */ +struct keyserver_spec +{ + struct keyserver_spec *next; + char *uri; + char *scheme; + char *auth; + char *host; + char *port; + char *path; + char *opaque; + strlist_t options; + struct + { + unsigned int direct_uri:1; + } flags; +}; + + +#endif /*GNUPG_COMMON_KEYSERVER_H*/ diff --git a/common/localename.c b/common/localename.c new file mode 100644 index 0000000..2650ea7 --- /dev/null +++ b/common/localename.c @@ -0,0 +1,126 @@ +/* localename.c - Determine the current selected locale. + * Copyright (C) 1995-1999, 2000-2003, 2007, + * 2008 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ +/* Written by Ulrich Drepper , 1995. */ +/* Win32 code written by Tor Lillqvist . */ +/* Modified for GpgOL use by Werner Koch , 2005. */ +/* Modified for GnuPG use by Werner Koch , 2007 */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif +#include /* We need gettext_localename for W32. */ + +#include "../common/w32help.h" + +/* XPG3 defines the result of 'setlocale (category, NULL)' as: + "Directs 'setlocale()' to query 'category' and return the current + setting of 'local'." + However it does not specify the exact format. Neither do SUSV2 and + ISO C 99. So we can use this feature only on selected systems (e.g. + those using GNU C Library). */ +#if defined _LIBC || (defined __GNU_LIBRARY__ && __GNU_LIBRARY__ >= 2) +# define HAVE_LOCALE_NULL +#endif + +/* Use a dummy value for LC_MESSAGES in case it is not defined. This + works because we always test for HAVE_LC_MESSAGES and the core + function takes the category as a string as well. */ +#ifndef HAVE_LC_MESSAGES +#define LC_MESSAGES 0 +#endif + + +/* Determine the current locale's name, and canonicalize it into XPG syntax + language[_territory[.codeset]][@modifier] + The codeset part in the result is not reliable; the locale_charset() + should be used for codeset information instead. + The result must not be freed; it is statically allocated. */ + +#ifndef HAVE_W32_SYSTEM +static const char * +do_nl_locale_name (int category, const char *categoryname) +{ + const char *retval; + + /* Use the POSIX methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'. + On some systems this can be done by the 'setlocale' function itself. */ +# if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL + (void)categoryname; + retval = setlocale (category, NULL); +# else + /* Setting of LC_ALL overwrites all other. */ + retval = getenv ("LC_ALL"); + if (retval == NULL || retval[0] == '\0') + { + /* Next comes the name of the desired category. */ + retval = getenv (categoryname); + if (retval == NULL || retval[0] == '\0') + { + /* Last possibility is the LANG environment variable. */ + retval = getenv ("LANG"); + if (retval == NULL || retval[0] == '\0') + /* We use C as the default domain. POSIX says this is + implementation defined. */ + retval = "C"; + } + } +# endif + + return retval; +} +#endif /* HAVE_W32_SYSTEM */ + + + +/* Return the locale used for translatable messages. The standard C + and POSIX are locale names are mapped to an empty string. If a + locale can't be found an empty string will be returned. */ +const char * +gnupg_messages_locale_name (void) +{ + const char *s; + +#ifdef HAVE_W32_SYSTEM + /* We use the localename function libgpg-error. */ + s = gettext_localename (); +#else + s = do_nl_locale_name (LC_MESSAGES, "LC_MESSAGES"); +#endif + if (!s) + s = ""; + else if (!strcmp (s, "C") || !strcmp (s, "POSIX")) + s = ""; + + return s; +} diff --git a/common/logging.c b/common/logging.c new file mode 100644 index 0000000..ca1341c --- /dev/null +++ b/common/logging.c @@ -0,0 +1,1013 @@ +/* logging.c - Useful logging functions + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, + * 2009, 2010 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else /*!HAVE_W32_SYSTEM*/ +# include +# include +# include +# include +#endif /*!HAVE_W32_SYSTEM*/ +#include +#include +#include +/* #include */ + +#define GNUPG_COMMON_NEED_AFLOCAL 1 +#include "util.h" +#include "i18n.h" +#include "common-defs.h" +#include "logging.h" + +#ifdef HAVE_W32_SYSTEM +# define S_IRGRP S_IRUSR +# define S_IROTH S_IRUSR +# define S_IWGRP S_IWUSR +# define S_IWOTH S_IWUSR +#endif + + +#ifdef HAVE_W32CE_SYSTEM +# define isatty(a) (0) +#endif + +#undef WITH_IPV6 +#if defined (AF_INET6) && defined(PF_INET) \ + && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) +# define WITH_IPV6 1 +#endif + +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT EINVAL +#endif +#ifndef INADDR_NONE /* Slowaris is missing that. */ +#define INADDR_NONE ((unsigned long)(-1)) +#endif /*INADDR_NONE*/ + +#ifdef HAVE_W32_SYSTEM +#define sock_close(a) closesocket(a) +#else +#define sock_close(a) close(a) +#endif + + +static estream_t logstream; +static int log_socket = -1; +static char prefix_buffer[80]; +static int with_time; +static int with_prefix; +static int with_pid; +#ifdef HAVE_W32_SYSTEM +static int no_registry; +#endif +static int (*get_pid_suffix_cb)(unsigned long *r_value); +static const char * (*socket_dir_cb)(void); +static int running_detached; +static int force_prefixes; + +static int missing_lf; +static int errorcount; + + +int +log_get_errorcount (int clear) +{ + int n = errorcount; + if( clear ) + errorcount = 0; + return n; +} + +void +log_inc_errorcount (void) +{ + errorcount++; +} + + +/* The following 3 functions are used by es_fopencookie to write logs + to a socket. */ +struct fun_cookie_s +{ + int fd; + int quiet; + int want_socket; + int is_socket; +#ifdef HAVE_W32CE_SYSTEM + int use_writefile; +#endif + char name[1]; +}; + + +/* Write NBYTES of BUFFER to file descriptor FD. */ +static int +writen (int fd, const void *buffer, size_t nbytes, int is_socket) +{ + const char *buf = buffer; + size_t nleft = nbytes; + int nwritten; +#ifndef HAVE_W32_SYSTEM + (void)is_socket; /* Not required. */ +#endif + + while (nleft > 0) + { +#ifdef HAVE_W32_SYSTEM + if (is_socket) + nwritten = send (fd, buf, nleft, 0); + else +#endif + nwritten = write (fd, buf, nleft); + + if (nwritten < 0 && errno == EINTR) + continue; + if (nwritten < 0) + return -1; + nleft -= nwritten; + buf = buf + nwritten; + } + + return 0; +} + + +/* Returns true if STR represents a valid port number in decimal + notation and no garbage is following. */ +static int +parse_portno (const char *str, unsigned short *r_port) +{ + unsigned int value; + + for (value=0; *str && (*str >= '0' && *str <= '9'); str++) + { + value = value * 10 + (*str - '0'); + if (value > 65535) + return 0; + } + if (*str || !value) + return 0; + + *r_port = value; + return 1; +} + + +static gpgrt_ssize_t +fun_writer (void *cookie_arg, const void *buffer, size_t size) +{ + struct fun_cookie_s *cookie = cookie_arg; + + /* FIXME: Use only estream with a callback for socket writing. This + avoids the ugly mix of fd and estream code. */ + + /* Note that we always try to reconnect to the socket but print + error messages only the first time an error occurred. If + RUNNING_DETACHED is set we don't fall back to stderr and even do + not print any error messages. This is needed because detached + processes often close stderr and by writing to file descriptor 2 + we might send the log message to a file not intended for logging + (e.g. a pipe or network connection). */ + if (cookie->want_socket && cookie->fd == -1) + { +#ifdef WITH_IPV6 + struct sockaddr_in6 srvr_addr_in6; +#endif + struct sockaddr_in srvr_addr_in; +#ifndef HAVE_W32_SYSTEM + struct sockaddr_un srvr_addr_un; +#endif + const char *name_for_err = ""; + size_t addrlen; + struct sockaddr *srvr_addr = NULL; + unsigned short port = 0; + int af = AF_LOCAL; + int pf = PF_LOCAL; + const char *name = cookie->name; + + /* Not yet open or meanwhile closed due to an error. */ + cookie->is_socket = 0; + + /* Check whether this is a TCP socket or a local socket. */ + if (!strncmp (name, "tcp://", 6) && name[6]) + { + name += 6; + af = AF_INET; + pf = PF_INET; + } +#ifndef HAVE_W32_SYSTEM + else if (!strncmp (name, "socket://", 9)) + name += 9; +#endif + + if (af == AF_LOCAL) + { + addrlen = 0; +#ifndef HAVE_W32_SYSTEM + memset (&srvr_addr, 0, sizeof srvr_addr); + srvr_addr_un.sun_family = af; + if (!*name && (name = socket_dir_cb ()) && *name) + { + if (strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1) + { + strncpy (srvr_addr_un.sun_path, + name, sizeof (srvr_addr_un.sun_path)-1); + strcat (srvr_addr_un.sun_path, "/S.log"); + srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; + srvr_addr = (struct sockaddr *)&srvr_addr_un; + addrlen = SUN_LEN (&srvr_addr_un); + name_for_err = srvr_addr_un.sun_path; + } + } + else + { + if (*name && strlen (name) < sizeof (srvr_addr_un.sun_path)-1) + { + strncpy (srvr_addr_un.sun_path, + name, sizeof (srvr_addr_un.sun_path)-1); + srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; + srvr_addr = (struct sockaddr *)&srvr_addr_un; + addrlen = SUN_LEN (&srvr_addr_un); + } + } +#endif /*!HAVE_W32SYSTEM*/ + } + else + { + char *addrstr, *p; +#ifdef HAVE_INET_PTON + void *addrbuf = NULL; +#endif /*HAVE_INET_PTON*/ + + addrstr = xtrymalloc (strlen (name) + 1); + if (!addrstr) + addrlen = 0; /* This indicates an error. */ + else if (*name == '[') + { + /* Check for IPv6 literal address. */ + strcpy (addrstr, name+1); + p = strchr (addrstr, ']'); + if (!p || p[1] != ':' || !parse_portno (p+2, &port)) + { + gpg_err_set_errno (EINVAL); + addrlen = 0; + } + else + { + *p = 0; +#ifdef WITH_IPV6 + af = AF_INET6; + pf = PF_INET6; + memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); + srvr_addr_in6.sin6_family = af; + srvr_addr_in6.sin6_port = htons (port); +#ifdef HAVE_INET_PTON + addrbuf = &srvr_addr_in6.sin6_addr; +#endif /*HAVE_INET_PTON*/ + srvr_addr = (struct sockaddr *)&srvr_addr_in6; + addrlen = sizeof srvr_addr_in6; +#else + gpg_err_set_errno (EAFNOSUPPORT); + addrlen = 0; +#endif + } + } + else + { + /* Check for IPv4 literal address. */ + strcpy (addrstr, name); + p = strchr (addrstr, ':'); + if (!p || !parse_portno (p+1, &port)) + { + gpg_err_set_errno (EINVAL); + addrlen = 0; + } + else + { + *p = 0; + memset (&srvr_addr_in, 0, sizeof srvr_addr_in); + srvr_addr_in.sin_family = af; + srvr_addr_in.sin_port = htons (port); +#ifdef HAVE_INET_PTON + addrbuf = &srvr_addr_in.sin_addr; +#endif /*HAVE_INET_PTON*/ + srvr_addr = (struct sockaddr *)&srvr_addr_in; + addrlen = sizeof srvr_addr_in; + } + } + + if (addrlen) + { +#ifdef HAVE_INET_PTON + if (inet_pton (af, addrstr, addrbuf) != 1) + addrlen = 0; +#else /*!HAVE_INET_PTON*/ + /* We need to use the old function. If we are here v6 + support isn't enabled anyway and thus we can do fine + without. Note that Windows has a compatible inet_pton + function named inetPton, but only since Vista. */ + srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); + if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) + addrlen = 0; +#endif /*!HAVE_INET_PTON*/ + } + + xfree (addrstr); + } + + cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1; + if (cookie->fd == -1) + { + if (!cookie->quiet && !running_detached + && isatty (es_fileno (es_stderr))) + es_fprintf (es_stderr, "failed to create socket for logging: %s\n", + strerror(errno)); + } + else + { + if (connect (cookie->fd, srvr_addr, addrlen) == -1) + { + if (!cookie->quiet && !running_detached + && isatty (es_fileno (es_stderr))) + es_fprintf (es_stderr, "can't connect to '%s%s': %s\n", + cookie->name, name_for_err, strerror(errno)); + sock_close (cookie->fd); + cookie->fd = -1; + } + } + + if (cookie->fd == -1) + { + if (!running_detached) + { + /* Due to all the problems with apps not running + detached but being called with stderr closed or used + for a different purposes, it does not make sense to + switch to stderr. We therefore disable it. */ + if (!cookie->quiet) + { + /* fputs ("switching logging to stderr\n", stderr);*/ + cookie->quiet = 1; + } + cookie->fd = -1; /*fileno (stderr);*/ + } + } + else /* Connection has been established. */ + { + cookie->quiet = 0; + cookie->is_socket = 1; + } + } + + log_socket = cookie->fd; + if (cookie->fd != -1) + { +#ifdef HAVE_W32CE_SYSTEM + if (cookie->use_writefile) + { + DWORD nwritten; + + WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL); + return (gpgrt_ssize_t)size; /* Okay. */ + } +#endif + if (!writen (cookie->fd, buffer, size, cookie->is_socket)) + return (gpgrt_ssize_t)size; /* Okay. */ + } + + if (!running_detached && cookie->fd != -1 + && isatty (es_fileno (es_stderr))) + { + if (*cookie->name) + es_fprintf (es_stderr, "error writing to '%s': %s\n", + cookie->name, strerror(errno)); + else + es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n", + cookie->fd, strerror(errno)); + } + if (cookie->is_socket && cookie->fd != -1) + { + sock_close (cookie->fd); + cookie->fd = -1; + log_socket = -1; + } + + return (gpgrt_ssize_t)size; +} + + +static int +fun_closer (void *cookie_arg) +{ + struct fun_cookie_s *cookie = cookie_arg; + + if (cookie->fd != -1 && cookie->fd != 2) + sock_close (cookie->fd); + xfree (cookie); + log_socket = -1; + return 0; +} + + +/* Common function to either set the logging to a file or a file + descriptor. */ +static void +set_file_fd (const char *name, int fd) +{ + estream_t fp; + int want_socket; +#ifdef HAVE_W32CE_SYSTEM + int use_writefile = 0; +#endif + struct fun_cookie_s *cookie; + + /* Close an open log stream. */ + if (logstream) + { + es_fclose (logstream); + logstream = NULL; + } + + /* Figure out what kind of logging we want. */ + if (name && !strcmp (name, "-")) + { + name = NULL; + fd = es_fileno (es_stderr); + } + + want_socket = 0; + if (name && !strncmp (name, "tcp://", 6) && name[6]) + want_socket = 1; +#ifndef HAVE_W32_SYSTEM + else if (name && !strncmp (name, "socket://", 9)) + want_socket = 2; +#endif /*HAVE_W32_SYSTEM*/ +#ifdef HAVE_W32CE_SYSTEM + else if (name && !strcmp (name, "GPG2:")) + { + HANDLE hd; + + ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0); + /* Ignore a filename and write the debug output to the GPG2: + device. */ + hd = CreateFile (L"GPG2:", GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd; + name = NULL; + force_prefixes = 1; + use_writefile = 1; + } +#endif /*HAVE_W32CE_SYSTEM*/ + + /* Setup a new stream. */ + + /* The xmalloc below is justified because we can expect that this + function is called only during initialization and there is no + easy way out of this error condition. */ + cookie = xmalloc (sizeof *cookie + (name? strlen (name):0)); + strcpy (cookie->name, name? name:""); + cookie->quiet = 0; + cookie->is_socket = 0; + cookie->want_socket = want_socket; +#ifdef HAVE_W32CE_SYSTEM + cookie->use_writefile = use_writefile; +#endif + if (!name) + cookie->fd = fd; + else if (want_socket) + cookie->fd = -1; + else + { + do + cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT, + (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)); + while (cookie->fd == -1 && errno == EINTR); + } + log_socket = cookie->fd; + + { + es_cookie_io_functions_t io = { NULL }; + io.func_write = fun_writer; + io.func_close = fun_closer; + + fp = es_fopencookie (cookie, "w", io); + } + + /* On error default to a stderr based estream. */ + if (!fp) + fp = es_stderr; + + es_setvbuf (fp, NULL, _IOLBF, 0); + + logstream = fp; + + /* We always need to print the prefix and the pid for socket mode, + so that the server reading the socket can do something + meaningful. */ + force_prefixes = want_socket; + + missing_lf = 0; +} + + +/* Set the file to write log to. The special names NULL and "-" may + be used to select stderr and names formatted like + "socket:///home/foo/mylogs" may be used to write the logging to the + socket "/home/foo/mylogs". If the connection to the socket fails + or a write error is detected, the function writes to stderr and + tries the next time again to connect the socket. + */ +void +log_set_file (const char *name) +{ + set_file_fd (name? name: "-", -1); +} + +void +log_set_fd (int fd) +{ + set_file_fd (NULL, fd); +} + + +/* Set a function to retrieve the directory name of a socket if + * only "socket://" has been given to log_set_file. */ +void +log_set_socket_dir_cb (const char *(*fnc)(void)) +{ + socket_dir_cb = fnc; +} + + +void +log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value)) +{ + get_pid_suffix_cb = cb; +} + + +void +log_set_prefix (const char *text, unsigned int flags) +{ + if (text) + { + strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1); + prefix_buffer[sizeof (prefix_buffer)-1] = 0; + } + + with_prefix = (flags & GPGRT_LOG_WITH_PREFIX); + with_time = (flags & GPGRT_LOG_WITH_TIME); + with_pid = (flags & GPGRT_LOG_WITH_PID); + running_detached = (flags & GPGRT_LOG_RUN_DETACHED); +#ifdef HAVE_W32_SYSTEM + no_registry = (flags & GPGRT_LOG_NO_REGISTRY); +#endif +} + + +const char * +log_get_prefix (unsigned int *flags) +{ + if (flags) + { + *flags = 0; + if (with_prefix) + *flags |= GPGRT_LOG_WITH_PREFIX; + if (with_time) + *flags |= GPGRT_LOG_WITH_TIME; + if (with_pid) + *flags |= GPGRT_LOG_WITH_PID; + if (running_detached) + *flags |= GPGRT_LOG_RUN_DETACHED; +#ifdef HAVE_W32_SYSTEM + if (no_registry) + *flags |= GPGRT_LOG_NO_REGISTRY; +#endif + } + return prefix_buffer; +} + +/* This function returns true if the file descriptor FD is in use for + logging. This is preferable over a test using log_get_fd in that + it allows the logging code to use more then one file descriptor. */ +int +log_test_fd (int fd) +{ + if (logstream) + { + int tmp = es_fileno (logstream); + if ( tmp != -1 && tmp == fd) + return 1; + } + if (log_socket != -1 && log_socket == fd) + return 1; + return 0; +} + +int +log_get_fd () +{ + return logstream? es_fileno(logstream) : -1; +} + +estream_t +log_get_stream () +{ + if (!logstream) + { + log_set_file (NULL); /* Make sure a log stream has been set. */ + assert (logstream); + } + return logstream; +} + +static void +do_logv (int level, int ignore_arg_ptr, const char *fmt, va_list arg_ptr) +{ + if (!logstream) + { +#ifdef HAVE_W32_SYSTEM + char *tmp; + + tmp = (no_registry + ? NULL + : read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, + "DefaultLogFile")); + log_set_file (tmp && *tmp? tmp : NULL); + xfree (tmp); +#else + log_set_file (NULL); /* Make sure a log stream has been set. */ +#endif + assert (logstream); + } + + es_flockfile (logstream); + if (missing_lf && level != GPGRT_LOG_CONT) + es_putc_unlocked ('\n', logstream ); + missing_lf = 0; + + if (level != GPGRT_LOG_CONT) + { /* Note this does not work for multiple line logging as we would + * need to print to a buffer first */ + if (with_time && !force_prefixes) + { + struct tm *tp; + time_t atime = time (NULL); + + tp = localtime (&atime); + es_fprintf_unlocked (logstream, "%04d-%02d-%02d %02d:%02d:%02d ", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec ); + } + if (with_prefix || force_prefixes) + es_fputs_unlocked (prefix_buffer, logstream); + if (with_pid || force_prefixes) + { + unsigned long pidsuf; + int pidfmt; + + if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf))) + es_fprintf_unlocked (logstream, pidfmt == 1? "[%u.%lu]":"[%u.%lx]", + (unsigned int)getpid (), pidsuf); + else + es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ()); + } + if ((!with_time && (with_prefix || with_pid)) || force_prefixes) + es_putc_unlocked (':', logstream); + /* A leading backspace suppresses the extra space so that we can + correctly output, programname, filename and linenumber. */ + if (fmt && *fmt == '\b') + fmt++; + else + if (with_time || with_prefix || with_pid || force_prefixes) + es_putc_unlocked (' ', logstream); + } + + switch (level) + { + case GPGRT_LOG_BEGIN: break; + case GPGRT_LOG_CONT: break; + case GPGRT_LOG_INFO: break; + case GPGRT_LOG_WARN: break; + case GPGRT_LOG_ERROR: break; + case GPGRT_LOG_FATAL: es_fputs_unlocked ("Fatal: ",logstream ); break; + case GPGRT_LOG_BUG: es_fputs_unlocked ("Ohhhh jeeee: ", logstream); break; + case GPGRT_LOG_DEBUG: es_fputs_unlocked ("DBG: ", logstream ); break; + default: + es_fprintf_unlocked (logstream,"[Unknown log level %d]: ", level); + break; + } + + if (fmt) + { + if (ignore_arg_ptr) + { /* This is used by log_string and comes with the extra + * feature that after a LF the next line is indent at the + * length of the prefix. Note that we do not yet include + * the length of the timestamp and pid in the indent + * computation. */ + const char *p, *pend; + + for (p = fmt; (pend = strchr (p, '\n')); p = pend+1) + es_fprintf_unlocked (logstream, "%*s%.*s", + (int)((p != fmt + && (with_prefix || force_prefixes)) + ?strlen (prefix_buffer)+2:0), "", + (int)(pend - p)+1, p); + es_fputs_unlocked (p, logstream); + } + else + es_vfprintf_unlocked (logstream, fmt, arg_ptr); + if (*fmt && fmt[strlen(fmt)-1] != '\n') + missing_lf = 1; + } + + if (level == GPGRT_LOG_FATAL) + { + if (missing_lf) + es_putc_unlocked ('\n', logstream); + es_funlockfile (logstream); + exit (2); + } + else if (level == GPGRT_LOG_BUG) + { + if (missing_lf) + es_putc_unlocked ('\n', logstream ); + es_funlockfile (logstream); + /* Using backtrace requires a configure test and to pass + * -rdynamic to gcc. Thus we do not enable it now. */ + /* { */ + /* void *btbuf[20]; */ + /* int btidx, btlen; */ + /* char **btstr; */ + + /* btlen = backtrace (btbuf, DIM (btbuf)); */ + /* btstr = backtrace_symbols (btbuf, btlen); */ + /* if (btstr) */ + /* for (btidx=0; btidx < btlen; btidx++) */ + /* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */ + /* } */ + abort (); + } + else + es_funlockfile (logstream); +} + + +void +log_log (int level, const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt) ; + do_logv (level, 0, fmt, arg_ptr); + va_end (arg_ptr); +} + + +void +log_logv (int level, const char *fmt, va_list arg_ptr) +{ + do_logv (level, 0, fmt, arg_ptr); +} + + +static void +do_log_ignore_arg (int level, const char *str, ...) +{ + va_list arg_ptr; + va_start (arg_ptr, str); + do_logv (level, 1, str, arg_ptr); + va_end (arg_ptr); +} + + +/* Log STRING at LEVEL but indent from the second line on by the + * length of the prefix. */ +void +log_string (int level, const char *string) +{ + /* We need a dummy arg_ptr, but there is no portable way to create + * one. So we call the do_logv function through a variadic wrapper. */ + do_log_ignore_arg (level, string); +} + + +void +log_info (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_INFO, 0, fmt, arg_ptr); + va_end (arg_ptr); +} + + +void +log_error (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_ERROR, 0, fmt, arg_ptr); + va_end (arg_ptr); + /* Protect against counter overflow. */ + if (errorcount < 30000) + errorcount++; +} + + +void +log_fatal (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_FATAL, 0, fmt, arg_ptr); + va_end (arg_ptr); + abort (); /* Never called; just to make the compiler happy. */ +} + + +void +log_bug (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_BUG, 0, fmt, arg_ptr); + va_end (arg_ptr); + abort (); /* Never called; just to make the compiler happy. */ +} + + +void +log_debug (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_DEBUG, 0, fmt, arg_ptr); + va_end (arg_ptr); +} + + +void +log_printf (const char *fmt, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, fmt); + do_logv (fmt ? GPGRT_LOG_CONT : GPGRT_LOG_BEGIN, 0, fmt, arg_ptr); + va_end (arg_ptr); +} + + +/* Flush the log - this is useful to make sure that the trailing + linefeed has been printed. */ +void +log_flush (void) +{ + do_log_ignore_arg (GPGRT_LOG_CONT, NULL); +} + + +/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw + dump, with TEXT just an empty string, print a trailing linefeed, + otherwise print an entire debug line. */ +void +log_printhex (const char *text, const void *buffer, size_t length) +{ + if (text && *text) + log_debug ("%s ", text); + if (length) + { + const unsigned char *p = buffer; + log_printf ("%02X", *p); + for (length--, p++; length--; p++) + log_printf (" %02X", *p); + } + if (text) + log_printf ("\n"); +} + + +/* +void +log_printcanon () {} +is found in sexputils.c +*/ + +/* +void +log_printsexp () {} +is found in sexputils.c +*/ + + +void +log_clock (const char *string) +{ +#if 0 + static unsigned long long initial; + struct timespec tv; + unsigned long long now; + + if (clock_gettime (CLOCK_REALTIME, &tv)) + { + log_debug ("error getting the realtime clock value\n"); + return; + } + now = tv.tv_sec * 1000000000ull; + now += tv.tv_nsec; + + if (!initial) + initial = now; + + log_debug ("[%6llu] %s", (now - initial)/1000, string); +#else + /* You need to link with -ltr to enable the above code. */ + log_debug ("[not enabled in the source] %s", string); +#endif +} + + +#ifdef GPGRT_HAVE_MACRO_FUNCTION +void +bug_at( const char *file, int line, const char *func ) +{ + log_log (GPGRT_LOG_BUG, "... this is a bug (%s:%d:%s)\n", file, line, func); + abort (); /* Never called; just to make the compiler happy. */ +} +#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ +void +bug_at( const char *file, int line ) +{ + log_log (GPGRT_LOG_BUG, "you found a bug ... (%s:%d)\n", file, line); + abort (); /* Never called; just to make the compiler happy. */ +} +#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ + + +#ifdef GPGRT_HAVE_MACRO_FUNCTION +void +_log_assert (const char *expr, const char *file, int line, const char *func) +{ + log_log (GPGRT_LOG_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n", + expr, func, file, line); + abort (); /* Never called; just to make the compiler happy. */ +} +#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ +void +_log_assert (const char *expr, const char *file, int line) +{ + log_log (GPGRT_LOG_BUG, "Assertion \"%s\" failed (%s:%d)\n", + file, line, func); + abort (); /* Never called; just to make the compiler happy. */ +} +#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ diff --git a/common/logging.h b/common/logging.h new file mode 100644 index 0000000..64b999d --- /dev/null +++ b/common/logging.h @@ -0,0 +1,111 @@ +/* logging.h + * Copyright (C) 1999, 2000, 2001, 2004, 2006, + * 2010 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_LOGGING_H +#define GNUPG_COMMON_LOGGING_H + +#include +#include +#include +#include "mischelp.h" +#include "w32help.h" + +int log_get_errorcount (int clear); +void log_inc_errorcount (void); +void log_set_file( const char *name ); +void log_set_fd (int fd); +void log_set_socket_dir_cb (const char *(*fnc)(void)); +void log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value)); +void log_set_prefix (const char *text, unsigned int flags); +const char *log_get_prefix (unsigned int *flags); +int log_test_fd (int fd); +int log_get_fd(void); +estream_t log_get_stream (void); + +#ifdef GPGRT_HAVE_MACRO_FUNCTION + void bug_at (const char *file, int line, const char *func) + GPGRT_ATTR_NORETURN; + void _log_assert (const char *expr, const char *file, int line, + const char *func) GPGRT_ATTR_NORETURN; +# define BUG() bug_at( __FILE__ , __LINE__, __FUNCTION__) +# define log_assert(expr) do { \ + if (!(expr)) \ + _log_assert (#expr, __FILE__, __LINE__, __FUNCTION__); \ + } while (0) +#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ + void bug_at (const char *file, int line); + void _log_assert (const char *expr, const char *file, int line; +# define BUG() bug_at( __FILE__ , __LINE__ ) +# define log_assert(expr) do { \ + if (!(expr)) \ + _log_assert (#expr, __FILE__, __LINE__); \ + } while (0) +#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ + +/* Flag values for log_set_prefix. */ +#define GPGRT_LOG_WITH_PREFIX 1 +#define GPGRT_LOG_WITH_TIME 2 +#define GPGRT_LOG_WITH_PID 4 +#define GPGRT_LOG_RUN_DETACHED 256 +#define GPGRT_LOG_NO_REGISTRY 512 + +/* Log levels as used by log_log. */ +enum jnlib_log_levels { + GPGRT_LOG_BEGIN, + GPGRT_LOG_CONT, + GPGRT_LOG_INFO, + GPGRT_LOG_WARN, + GPGRT_LOG_ERROR, + GPGRT_LOG_FATAL, + GPGRT_LOG_BUG, + GPGRT_LOG_DEBUG +}; +void log_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3); +void log_logv (int level, const char *fmt, va_list arg_ptr); +void log_string (int level, const char *string); +void log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2); +void log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2); +void log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_flush (void); + +/* Print a hexdump of BUFFER. With TEXT passes as NULL print just the + raw dump, with TEXT being an empty string, print a trailing + linefeed, otherwise print an entire debug line with TEXT followed + by the hexdump and a final LF. */ +void log_printhex (const char *text, const void *buffer, size_t length); + +void log_clock (const char *string); + + +#endif /*GNUPG_COMMON_LOGGING_H*/ diff --git a/common/mapstrings.c b/common/mapstrings.c new file mode 100644 index 0000000..614fddd --- /dev/null +++ b/common/mapstrings.c @@ -0,0 +1,167 @@ +/* mapstrings.c - Static string mapping + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include + +#include "util.h" +#include "stringhelp.h" +#include "membuf.h" + + +static struct { + const char *name; + const char *value; +} macros[] = { +#ifdef PACKAGE_BUGREPORT + { "EMAIL", PACKAGE_BUGREPORT }, +#else + { "EMAIL", "bug@example.org" }, +#endif + { "GNUPG", GNUPG_NAME }, + { "GPG", GPG_NAME }, + { "GPGSM", GPGSM_NAME }, + { "GPG_AGENT", GPG_AGENT_NAME }, + { "SCDAEMON", SCDAEMON_NAME }, + { "DIRMNGR", DIRMNGR_NAME }, + { "G13", G13_NAME }, + { "GPGCONF", GPGCONF_NAME }, + { "GPGTAR", GPGTAR_NAME } +}; + + + +/* A list to remember already done mappings. */ +struct mapping_s +{ + struct mapping_s *next; + const char *key; + const char *value; +}; +static struct mapping_s *mappings; + + +/* If STRING has already been mapped, return the mapped string. If + not return NULL. */ +static const char * +already_mapped (const char *string) +{ + struct mapping_s *m; + + for (m=mappings; m; m = m->next) + if (m->key == string && !strcmp (m->key, string)) + return m->value; + return NULL; +} + + +/* Store NEWSTRING under key STRING and return NEWSTRING. */ +static const char * +store_mapping (const char *string, char *newstring) +{ + struct mapping_s *m; + + m = xmalloc (sizeof *m); + m->key = string; + m->value = newstring; + m->next = mappings; + mappings = m; + return newstring; +} + + +/* Find the first macro in STRING. Return a pointer to the + replacement value, set BEGPTR to the leading '@', and set ENDPTR to + the terminating '@'. If no macro is found return NULL. */ +const char * +find_macro (const char *string, const char **begptr, + const char **endptr) +{ + const char *s, *s2, *s3; + int idx; + + s = string; + if (!s) + return NULL; + + for (; (s2 = strchr (s, '@')); s = s2) + { + s2++; + if (*s2 >= 'A' && *s2 <= 'Z' && (s3 = (strchr (s2, '@')))) + { + for (idx=0; idx < DIM (macros); idx++) + if (strlen (macros[idx].name) == (s3 - s2) + && !memcmp (macros[idx].name, s2, (s3 - s2))) + { + *begptr = s2 - 1; + *endptr = s3; + return macros[idx].value; + } + } + } + return NULL; +} + + +/* If STRING includes known @FOO@ macros, replace these macros and + return a new static string. Warning: STRING must have been + allocated statically. Note that this function allocates memory + which will not be released (similar to gettext). */ +const char * +map_static_macro_string (const char *string) +{ + const char *s, *s2, *s3, *value; + membuf_t mb; + char *p; + + if ((s = already_mapped (string))) + return s; + s = string; + value = find_macro (s, &s2, &s3); + if (!value) + return string; /* No macros at all. */ + + init_membuf (&mb, strlen (string) + 100); + do + { + put_membuf (&mb, s, s2 - s); + put_membuf_str (&mb, value); + s = s3 + 1; + } + while ((value = find_macro (s, &s2, &s3))); + put_membuf_str (&mb, s); + put_membuf (&mb, "", 1); + + p = get_membuf_shrink (&mb, NULL); + if (!p) + log_fatal ("map_static_macro_string failed: %s\n", strerror (errno)); + + return store_mapping (string, p); +} diff --git a/common/mbox-util.c b/common/mbox-util.c new file mode 100644 index 0000000..c1f05b8 --- /dev/null +++ b/common/mbox-util.c @@ -0,0 +1,243 @@ +/* mbox-util.c - Mail address helper functions + * Copyright (C) 1998-2010 Free Software Foundation, Inc. + * Copyright (C) 1998-2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file 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 file 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 Lesser General Public License + * along with this program; if not, see . + */ + +/* NB: GPGME uses the same code to reflect our idea on how to extract + * a mail address from a user id. + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "mbox-util.h" + + +static int +string_count_chr (const char *string, int c) +{ + int count; + + for (count=0; *string; string++ ) + if ( *string == c ) + count++; + return count; +} + +static int +mem_count_chr (const void *buffer, int c, size_t length) +{ + const char *s = buffer; + int count; + + for (count=0; length; length--, s++) + if (*s == c) + count++; + return count; +} + + +/* This is a case-sensitive version of our memistr. I wonder why no + standard function memstr exists but I better do not use the name + memstr to avoid future conflicts. */ +static const char * +my_memstr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if (*t == *s) + { + for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--) + ; + if (!*s) + return (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + + + +static int +string_has_ctrl_or_space (const char *string) +{ + for (; *string; string++ ) + if (!(*string & 0x80) && *string <= 0x20) + return 1; + return 0; +} + + +/* Return true if STRING has two consecutive '.' after an '@' + sign. */ +static int +has_dotdot_after_at (const char *string) +{ + string = strchr (string, '@'); + if (!string) + return 0; /* No at-sign. */ + string++; + return !!strstr (string, ".."); +} + + +/* Check whether BUFFER has characters not valid in an RFC-822 + address. LENGTH gives the length of BUFFER. + + To cope with OpenPGP we ignore non-ascii characters so that for + example umlauts are legal in an email address. An OpenPGP user ID + must be utf-8 encoded but there is no strict requirement for + RFC-822. Thus to avoid IDNA encoding we put the address verbatim + as utf-8 into the user ID under the assumption that mail programs + handle IDNA at a lower level and take OpenPGP user IDs as utf-8. + Note that we can't do an utf-8 encoding checking here because in + keygen.c this function is called with the native encoding and + native to utf-8 encoding is only done later. */ +int +has_invalid_email_chars (const void *buffer, size_t length) +{ + const unsigned char *s = buffer; + int at_seen=0; + const char *valid_chars= + "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for ( ; length && *s; length--, s++ ) + { + if ((*s & 0x80)) + continue; /* We only care about ASCII. */ + if (*s == '@') + at_seen=1; + else if (!at_seen && !(strchr (valid_chars, *s) + || strchr ("!#$%&'*+/=?^`{|}~", *s))) + return 1; + else if (at_seen && !strchr (valid_chars, *s)) + return 1; + } + return 0; +} + + +/* Same as is_valid_mailbox (see below) but operates on non-nul + terminated buffer. */ +int +is_valid_mailbox_mem (const void *name_arg, size_t namelen) +{ + const char *name = name_arg; + + return !( !name + || !namelen + || has_invalid_email_chars (name, namelen) + || mem_count_chr (name, '@', namelen) != 1 + || *name == '@' + || name[namelen-1] == '@' + || name[namelen-1] == '.' + || my_memstr (name, namelen, "..")); +} + + +/* Check whether NAME represents a valid mailbox according to + RFC822. Returns true if so. */ +int +is_valid_mailbox (const char *name) +{ + return name? is_valid_mailbox_mem (name, strlen (name)) : 0; +} + + +/* Return the mailbox (local-part@domain) form a standard user id. + All plain ASCII characters in the result are converted to + lowercase. Caller must free the result. Returns NULL if no valid + mailbox was found (or we are out of memory). */ +char * +mailbox_from_userid (const char *userid) +{ + const char *s, *s_end; + size_t len; + char *result = NULL; + + s = strchr (userid, '<'); + if (s) + { + /* Seems to be a standard user id. */ + s++; + s_end = strchr (s, '>'); + if (s_end && s_end > s) + { + len = s_end - s; + result = xtrymalloc (len + 1); + if (!result) + return NULL; /* Ooops - out of core. */ + strncpy (result, s, len); + result[len] = 0; + /* Apply some basic checks on the address. We do not use + is_valid_mailbox because those checks are too strict. */ + if (string_count_chr (result, '@') != 1 /* Need exactly one '@. */ + || *result == '@' /* local-part missing. */ + || result[len-1] == '@' /* domain missing. */ + || result[len-1] == '.' /* ends with a dot. */ + || string_has_ctrl_or_space (result) + || has_dotdot_after_at (result)) + { + xfree (result); + result = NULL; + errno = EINVAL; + } + } + else + errno = EINVAL; + } + else if (is_valid_mailbox (userid)) + { + /* The entire user id is a mailbox. Return that one. Note that + this fallback method has some restrictions on the valid + syntax of the mailbox. However, those who want weird + addresses should know about it and use the regular <...> + syntax. */ + result = xtrystrdup (userid); + } + else + errno = EINVAL; + + return result? ascii_strlwr (result): NULL; +} + + +/* Check whether UID is a valid standard user id of the form + "Heinrich Heine " + and return true if this is the case. */ +int +is_valid_user_id (const char *uid) +{ + if (!uid || !*uid) + return 0; + + return 1; +} diff --git a/common/mbox-util.h b/common/mbox-util.h new file mode 100644 index 0000000..bce003f --- /dev/null +++ b/common/mbox-util.h @@ -0,0 +1,29 @@ +/* mbox-util.h - Defs for mail address helper functions + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file 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 file 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 Lesser General Public License + * along with this program; if not, see . + */ +#ifndef GNUPG_COMMON_MBOX_UTIL_H +#define GNUPG_COMMON_MBOX_UTIL_H + +int has_invalid_email_chars (const void *buffer, size_t length); +int is_valid_mailbox (const char *name); +int is_valid_mailbox_mem (const void *buffer, size_t length); +char *mailbox_from_userid (const char *userid); +int is_valid_user_id (const char *uid); + + +#endif /*GNUPG_COMMON_MBOX_UTIL_H*/ diff --git a/common/membuf.c b/common/membuf.c new file mode 100644 index 0000000..4c1a844 --- /dev/null +++ b/common/membuf.c @@ -0,0 +1,230 @@ +/* membuf.c - A simple implementation of a dynamic buffer. + * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "membuf.h" + + +/* A simple implementation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +void +init_membuf (membuf_t *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = errno; +} + +/* Same as init_membuf but allocates the buffer in secure memory. */ +void +init_membuf_secure (membuf_t *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc_secure (initiallen); + if (!mb->buf) + mb->out_of_core = errno; +} + + +/* Shift the the content of the membuf MB by AMOUNT bytes. The next + operation will then behave as if AMOUNT bytes had not been put into + the buffer. If AMOUNT is greater than the actual accumulated + bytes, the membuf is basically reset to its initial state. */ +void +clear_membuf (membuf_t *mb, size_t amount) +{ + /* No need to clear if we are already out of core. */ + if (mb->out_of_core) + return; + if (amount >= mb->len) + mb->len = 0; + else + { + mb->len -= amount; + memmove (mb->buf, mb->buf+amount, mb->len); + } +} + + +void +put_membuf (membuf_t *mb, const void *buf, size_t len) +{ + if (mb->out_of_core || !len) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = errno ? errno : ENOMEM; + /* Wipe out what we already accumulated. This is required + in case we are storing sensitive data here. The membuf + API does not provide another way to cleanup after an + error. */ + wipememory (mb->buf, mb->len); + return; + } + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + + +/* A variant of put_membuf accepting a void * and returning a + gpg_error_t (which will always return 0) to be used as a generic + callback handler. This function also allows buffer to be NULL. */ +gpg_error_t +put_membuf_cb (void *opaque, const void *buf, size_t len) +{ + membuf_t *data = opaque; + + if (buf) + put_membuf (data, buf, len); + return 0; +} + + +void +put_membuf_str (membuf_t *mb, const char *string) +{ + put_membuf (mb, string, strlen (string)); +} + + +void +put_membuf_printf (membuf_t *mb, const char *format, ...) +{ + int rc; + va_list arg_ptr; + char *buf; + + va_start (arg_ptr, format); + rc = gpgrt_vasprintf (&buf, format, arg_ptr); + if (rc < 0) + mb->out_of_core = errno ? errno : ENOMEM; + va_end (arg_ptr); + if (rc >= 0) + { + put_membuf (mb, buf, strlen (buf)); + xfree (buf); + } +} + + +void * +get_membuf (membuf_t *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + if (mb->buf) + { + wipememory (mb->buf, mb->len); + xfree (mb->buf); + mb->buf = NULL; + } + gpg_err_set_errno (mb->out_of_core); + return NULL; + } + + p = mb->buf; + if (len) + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ + return p; +} + + +/* Same as get_membuf but shrinks the reallocated space to the + required size. */ +void * +get_membuf_shrink (membuf_t *mb, size_t *len) +{ + void *p, *pp; + size_t dummylen; + + if (!len) + len = &dummylen; + + p = get_membuf (mb, len); + if (!p) + return NULL; + if (*len) + { + pp = xtryrealloc (p, *len); + if (pp) + p = pp; + } + + return p; +} + + +/* Peek at the membuf MB. On success a pointer to the buffer is + returned which is valid until the next operation on MB. If LEN is + not NULL the current LEN of the buffer is stored there. On error + NULL is returned and ERRNO is set. */ +const void * +peek_membuf (membuf_t *mb, size_t *len) +{ + const char *p; + + if (mb->out_of_core) + { + gpg_err_set_errno (mb->out_of_core); + return NULL; + } + + p = mb->buf; + if (len) + *len = mb->len; + return p; +} diff --git a/common/membuf.h b/common/membuf.h new file mode 100644 index 0000000..1497bcd --- /dev/null +++ b/common/membuf.h @@ -0,0 +1,64 @@ +/* membuf.h - A simple implementation of a dynamic buffer + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_MEMBUF_H +#define GNUPG_COMMON_MEMBUF_H + +#include "mischelp.h" + +/* The definition of the structure is private, we only need it here, + so it can be allocated on the stack. */ +struct private_membuf_s +{ + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + +typedef struct private_membuf_s membuf_t; + +/* Return the current length of the membuf. */ +#define get_membuf_len(a) ((a)->len) +#define is_membuf_ready(a) ((a)->buf || (a)->out_of_core) +#define MEMBUF_ZERO { 0, 0, NULL, 0} + +void init_membuf (membuf_t *mb, int initiallen); +void init_membuf_secure (membuf_t *mb, int initiallen); +void clear_membuf (membuf_t *mb, size_t amount); +void put_membuf (membuf_t *mb, const void *buf, size_t len); +gpg_error_t put_membuf_cb (void *opaque, const void *buf, size_t len); +void put_membuf_str (membuf_t *mb, const char *string); +void put_membuf_printf (membuf_t *mb, const char *format, + ...) GPGRT_ATTR_PRINTF(2,3); +void *get_membuf (membuf_t *mb, size_t *len); +void *get_membuf_shrink (membuf_t *mb, size_t *len); +const void *peek_membuf (membuf_t *mb, size_t *len); + +#endif /*GNUPG_COMMON_MEMBUF_H*/ diff --git a/common/miscellaneous.c b/common/miscellaneous.c new file mode 100644 index 0000000..c988975 --- /dev/null +++ b/common/miscellaneous.c @@ -0,0 +1,565 @@ +/* miscellaneous.c - Stuff not fitting elsewhere + * Copyright (C) 2003, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "iobuf.h" +#include "i18n.h" + +/* Used by libgcrypt for logging. */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + (void)dummy; + + /* Map the log levels. */ + switch (level) + { + case GCRY_LOG_CONT: level = GPGRT_LOG_CONT; break; + case GCRY_LOG_INFO: level = GPGRT_LOG_INFO; break; + case GCRY_LOG_WARN: level = GPGRT_LOG_WARN; break; + case GCRY_LOG_ERROR:level = GPGRT_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = GPGRT_LOG_FATAL; break; + case GCRY_LOG_BUG: level = GPGRT_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = GPGRT_LOG_DEBUG; break; + default: level = GPGRT_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +/* This function is called by libgcrypt on a fatal error. */ +static void +my_gcry_fatalerror_handler (void *opaque, int rc, const char *text) +{ + (void)opaque; + + log_fatal ("libgcrypt problem: %s\n", text ? text : gpg_strerror (rc)); + abort (); +} + + +/* This function is called by libgcrypt if it ran out of core and + there is no way to return that error to the caller. We do our own + function here to make use of our logging functions. */ +static int +my_gcry_outofcore_handler (void *opaque, size_t req_n, unsigned int flags) +{ + static int been_here; /* Used to protect against recursive calls. */ + + (void)opaque; + + if (!been_here) + { + been_here = 1; + if ( (flags & 1) ) + log_fatal (_("out of core in secure memory " + "while allocating %lu bytes"), (unsigned long)req_n); + else + log_fatal (_("out of core while allocating %lu bytes"), + (unsigned long)req_n); + } + return 0; /* Let libgcrypt call its own fatal error handler. + Actually this will turn out to be + my_gcry_fatalerror_handler. */ +} + + +/* Setup libgcrypt to use our own logging functions. Should be used + early at startup. */ +void +setup_libgcrypt_logging (void) +{ + gcry_set_log_handler (my_gcry_logger, NULL); + gcry_set_fatalerror_handler (my_gcry_fatalerror_handler, NULL); + gcry_set_outofcore_handler (my_gcry_outofcore_handler, NULL); +} + + +/* Print an out of core message and let the process die. The printed + * error is taken from ERRNO. */ +void +xoutofcore (void) +{ + gpg_error_t err = gpg_error_from_syserror (); + log_fatal (_("error allocating enough memory: %s\n"), gpg_strerror (err)); + abort (); /* Never called; just to make the compiler happy. */ +} + + +/* A wrapper around gcry_cipher_algo_name to return the string + "AES-128" instead of "AES". Given that we have an alias in + libgcrypt for it, it does not harm to too much to return this other + string. Some users complained that we print "AES" but "AES192" + and "AES256". We can't fix that in libgcrypt but it is pretty + safe to do it in an application. */ +const char * +gnupg_cipher_algo_name (int algo) +{ + const char *s; + + s = gcry_cipher_algo_name (algo); + if (!strcmp (s, "AES")) + s = "AES128"; + return s; +} + + +void +obsolete_option (const char *configname, unsigned int configlineno, + const char *name) +{ + if (configname) + log_info (_("%s:%u: obsolete option \"%s\" - it has no effect\n"), + configname, configlineno, name); + else + log_info (_("WARNING: \"%s%s\" is an obsolete option - it has no effect\n"), + "--", name); +} + + +/* Decide whether the filename is stdout or a real filename and return + * an appropriate string. */ +const char * +print_fname_stdout (const char *s) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdout]"; + return s; +} + + +/* Decide whether the filename is stdin or a real filename and return + * an appropriate string. */ +const char * +print_fname_stdin (const char *s) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdin]"; + return s; +} + + +static int +do_print_utf8_buffer (estream_t stream, + const void *buffer, size_t length, + const char *delimiters, size_t *bytes_written) +{ + const char *p = buffer; + size_t i; + + /* We can handle plain ascii simpler, so check for it first. */ + for (i=0; i < length; i++ ) + { + if ( (p[i] & 0x80) ) + break; + } + if (i < length) + { + int delim = delimiters? *delimiters : 0; + char *buf; + int ret; + + /*(utf8 conversion already does the control character quoting). */ + buf = utf8_to_native (p, length, delim); + if (bytes_written) + *bytes_written = strlen (buf); + ret = es_fputs (buf, stream); + xfree (buf); + return ret == EOF? ret : (int)i; + } + else + return es_write_sanitized (stream, p, length, delimiters, bytes_written); +} + + +void +print_utf8_buffer3 (estream_t stream, const void *p, size_t n, + const char *delim) +{ + do_print_utf8_buffer (stream, p, n, delim, NULL); +} + + +void +print_utf8_buffer2 (estream_t stream, const void *p, size_t n, int delim) +{ + char tmp[2]; + + tmp[0] = delim; + tmp[1] = 0; + do_print_utf8_buffer (stream, p, n, tmp, NULL); +} + + +void +print_utf8_buffer (estream_t stream, const void *p, size_t n) +{ + do_print_utf8_buffer (stream, p, n, NULL, NULL); +} + +/* Write LENGTH bytes of BUFFER to FP as a hex encoded string. + RESERVED must be 0. */ +void +print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved) +{ +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + const unsigned char *s; + + (void)reserved; + + for (s = buffer; length; s++, length--) + { + putc ( tohex ((*s>>4)&15), fp); + putc ( tohex (*s&15), fp); + } +#undef tohex +} + + +/* Create a string from the buffer P_ARG of length N which is suitable + * for printing. Caller must release the created string using xfree. + * On error ERRNO is set and NULL returned. Errors are only possible + * due to malloc failure. */ +char * +try_make_printable_string (const void *p_arg, size_t n, int delim) +{ + const unsigned char *p = p_arg; + size_t save_n, buflen; + const unsigned char *save_p; + char *buffer, *d; + + /* First count length. */ + for (save_n = n, save_p = p, buflen=1 ; n; n--, p++ ) + { + if ( *p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) + { + if ( *p=='\n' || *p=='\r' || *p=='\f' + || *p=='\v' || *p=='\b' || !*p ) + buflen += 2; + else + buflen += 5; + } + else + buflen++; + } + p = save_p; + n = save_n; + /* And now make the string */ + d = buffer = xtrymalloc (buflen); + for ( ; n; n--, p++ ) + { + if (*p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) { + *d++ = '\\'; + if( *p == '\n' ) + *d++ = 'n'; + else if( *p == '\r' ) + *d++ = 'r'; + else if( *p == '\f' ) + *d++ = 'f'; + else if( *p == '\v' ) + *d++ = 'v'; + else if( *p == '\b' ) + *d++ = 'b'; + else if( !*p ) + *d++ = '0'; + else { + sprintf(d, "x%02x", *p ); + d += 3; + } + } + else + *d++ = *p; + } + *d = 0; + return buffer; +} + + +/* Same as try_make_printable_string but terminates the process on + * memory shortage. */ +char * +make_printable_string (const void *p, size_t n, int delim ) +{ + char *string = try_make_printable_string (p, n, delim); + if (!string) + xoutofcore (); + return string; +} + + +/* + * Check if the file is compressed. + */ +int +is_file_compressed (const char *s, int *ret_rc) +{ + iobuf_t a; + byte buf[4]; + int i, rc = 0; + int overflow; + + struct magic_compress_s { + size_t len; + byte magic[4]; + } magic[] = { + { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ + { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ + { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ + }; + + if ( iobuf_is_pipe_filename (s) || !ret_rc ) + return 0; /* We can't check stdin or no file was given */ + + a = iobuf_open( s ); + if ( a == NULL ) { + *ret_rc = gpg_error_from_syserror (); + return 0; + } + + if ( iobuf_get_filelength( a, &overflow ) < 4 && !overflow) { + *ret_rc = 0; + goto leave; + } + + if ( iobuf_read( a, buf, 4 ) == -1 ) { + *ret_rc = a->error; + goto leave; + } + + for ( i = 0; i < DIM( magic ); i++ ) { + if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) { + *ret_rc = 0; + rc = 1; + break; + } + } + +leave: + iobuf_close( a ); + return rc; +} + + +/* Try match against each substring of multistr, delimited by | */ +int +match_multistr (const char *multistr,const char *match) +{ + do + { + size_t seglen = strcspn (multistr,"|"); + if (!seglen) + break; + /* Using the localized strncasecmp! */ + if (strncasecmp(multistr,match,seglen)==0) + return 1; + multistr += seglen; + if (*multistr == '|') + multistr++; + } + while (*multistr); + + return 0; +} + + + +/* Parse the first portion of the version number S and store it at + NUMBER. On success, the function returns a pointer into S starting + with the first character, which is not part of the initial number + portion; on failure, NULL is returned. */ +static const char* +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && digitp (s+1)) + return NULL; /* Leading zeros are not allowed. */ + for (; digitp (s); s++ ) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0? NULL : s; +} + +/* Break up the complete string representation of the version number S, + which is expected to have this format: + + ... + + The major, minor and micro number components will be stored at + MAJOR, MINOR and MICRO. On success, a pointer to the last + component, the patch level, will be returned; on failure, NULL will + be returned. */ +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + return s; /* Patchlevel. */ +} + +/* Return true if version string is at least version B. */ +int +gnupg_compare_version (const char *a, const char *b) +{ + int a_major, a_minor, a_micro; + int b_major, b_minor, b_micro; + const char *a_plvl, *b_plvl; + + if (!a || !b) + return 0; + + /* Parse version A. */ + a_plvl = parse_version_string (a, &a_major, &a_minor, &a_micro); + if (!a_plvl ) + return 0; /* Invalid version number. */ + + /* Parse version B. */ + b_plvl = parse_version_string (b, &b_major, &b_minor, &b_micro); + if (!b_plvl ) + return 0; /* Invalid version number. */ + + /* Compare version numbers. */ + return (a_major > b_major + || (a_major == b_major && a_minor > b_minor) + || (a_major == b_major && a_minor == b_minor + && a_micro > b_micro) + || (a_major == b_major && a_minor == b_minor + && a_micro == b_micro + && strcmp (a_plvl, b_plvl) >= 0)); +} + + + +/* Parse an --debug style argument. We allow the use of number values + * in the usual C notation or a string with comma separated keywords. + * + * Returns: 0 on success or -1 and ERRNO set on error. On success the + * supplied variable is updated by the parsed flags. + * + * If STRING is NULL the enabled debug flags are printed. + * + * See doc/DETAILS for a summary of used debug options. + */ +int +parse_debug_flag (const char *string, unsigned int *debugvar, + const struct debug_flags_s *flags) + +{ + unsigned long result = 0; + int i, j; + + if (!string) + { + if (debugvar) + { + log_info ("enabled debug flags:"); + for (i=0; flags[i].name; i++) + if ((*debugvar & flags[i].flag)) + log_printf (" %s", flags[i].name); + log_printf ("\n"); + } + return 0; + } + + while (spacep (string)) + string++; + if (*string == '-') + { + errno = EINVAL; + return -1; + } + + if (!strcmp (string, "?") || !strcmp (string, "help")) + { + log_info ("available debug flags:\n"); + for (i=0; flags[i].name; i++) + log_info (" %5u %s\n", flags[i].flag, flags[i].name); + if (flags[i].flag != 77) + exit (0); + } + else if (digitp (string)) + { + errno = 0; + result = strtoul (string, NULL, 0); + if (result == ULONG_MAX && errno == ERANGE) + return -1; + } + else + { + char **words; + words = strtokenize (string, ","); + if (!words) + return -1; + for (i=0; words[i]; i++) + { + if (*words[i]) + { + for (j=0; flags[j].name; j++) + if (!strcmp (words[i], flags[j].name)) + { + result |= flags[j].flag; + break; + } + if (!flags[j].name) + { + if (!strcmp (words[i], "none")) + { + *debugvar = 0; + result = 0; + } + else if (!strcmp (words[i], "all")) + result = ~0; + else + log_info (_("unknown debug flag '%s' ignored\n"), words[i]); + } + } + } + xfree (words); + } + + *debugvar |= result; + return 0; +} diff --git a/common/mischelp.c b/common/mischelp.c new file mode 100644 index 0000000..fd8f675 --- /dev/null +++ b/common/mischelp.c @@ -0,0 +1,194 @@ +/* mischelp.c - Miscellaneous helper functions + * Copyright (C) 1998, 2000, 2001, 2006, 2007 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include +#else /*!HAVE_W32_SYSTEM*/ +# include +# include +# include +#endif /*!HAVE_W32_SYSTEM*/ +#include + +#include "util.h" +#include "common-defs.h" +#include "stringhelp.h" +#include "utf8conv.h" +#include "mischelp.h" + + +/* Check whether the files NAME1 and NAME2 are identical. This is for + example achieved by comparing the inode numbers of the files. */ +int +same_file_p (const char *name1, const char *name2) +{ + int yes; + + /* First try a shortcut. */ + if (!compare_filenames (name1, name2)) + yes = 1; + else + { +#ifdef HAVE_W32_SYSTEM + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION info1, info2; + +#ifdef HAVE_W32CE_SYSTEM + { + wchar_t *wname = utf8_to_wchar (name1); + if (wname) + file1 = CreateFile (wname, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + else + file1 = INVALID_HANDLE_VALUE; + xfree (wname); + } +#else + file1 = CreateFile (name1, 0, 0, NULL, OPEN_EXISTING, 0, NULL); +#endif + if (file1 == INVALID_HANDLE_VALUE) + yes = 0; /* If we can't open the file, it is not the same. */ + else + { +#ifdef HAVE_W32CE_SYSTEM + { + wchar_t *wname = utf8_to_wchar (name2); + if (wname) + file2 = CreateFile (wname, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + else + file2 = INVALID_HANDLE_VALUE; + xfree (wname); + } +#else + file2 = CreateFile (name2, 0, 0, NULL, OPEN_EXISTING, 0, NULL); +#endif + if (file2 == INVALID_HANDLE_VALUE) + yes = 0; /* If we can't open the file, it is not the same. */ + else + { + yes = (GetFileInformationByHandle (file1, &info1) + && GetFileInformationByHandle (file2, &info2) + && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber + && info1.nFileIndexHigh == info2.nFileIndexHigh + && info1.nFileIndexLow == info2.nFileIndexLow); + CloseHandle (file2); + } + CloseHandle (file1); + } +#else /*!HAVE_W32_SYSTEM*/ + struct stat info1, info2; + + yes = (!stat (name1, &info1) && !stat (name2, &info2) + && info1.st_dev == info2.st_dev && info1.st_ino == info2.st_ino); +#endif /*!HAVE_W32_SYSTEM*/ + } + return yes; +} + + +/* + timegm() is a GNU function that might not be available everywhere. + It's basically the inverse of gmtime() - you give it a struct tm, + and get back a time_t. It differs from mktime() in that it handles + the case where the struct tm is UTC and the local environment isn't. + + Note, that this replacement implementation might not be thread-safe! + + Some BSDs don't handle the putenv("foo") case properly, so we use + unsetenv if the platform has it to remove environment variables. +*/ +#ifndef HAVE_TIMEGM +time_t +timegm (struct tm *tm) +{ +#ifdef HAVE_W32_SYSTEM + /* This one is thread safe. */ + SYSTEMTIME st; + FILETIME ft; + unsigned long long cnsecs; + + st.wYear = tm->tm_year + 1900; + st.wMonth = tm->tm_mon + 1; + st.wDay = tm->tm_mday; + st.wHour = tm->tm_hour; + st.wMinute = tm->tm_min; + st.wSecond = tm->tm_sec; + st.wMilliseconds = 0; /* Not available. */ + st.wDayOfWeek = 0; /* Ignored. */ + + /* System time is UTC thus the conversion is pretty easy. */ + if (!SystemTimeToFileTime (&st, &ft)) + { + gpg_err_set_errno (EINVAL); + return (time_t)(-1); + } + + cnsecs = (((unsigned long long)ft.dwHighDateTime << 32) + | ft.dwLowDateTime); + cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */ + return (time_t)(cnsecs / 10000000ULL); + +#else /* (Non thread safe implementation!) */ + + time_t answer; + char *zone; + + zone=getenv("TZ"); + putenv("TZ=UTC"); + tzset(); + answer=mktime(tm); + if(zone) + { + static char *old_zone; + + if (!old_zone) + { + old_zone = malloc(3+strlen(zone)+1); + if (old_zone) + { + strcpy(old_zone,"TZ="); + strcat(old_zone,zone); + } + } + if (old_zone) + putenv (old_zone); + } + else + gnupg_unsetenv("TZ"); + + tzset(); + return answer; +#endif +} +#endif /*!HAVE_TIMEGM*/ diff --git a/common/mischelp.h b/common/mischelp.h new file mode 100644 index 0000000..1ad146e --- /dev/null +++ b/common/mischelp.h @@ -0,0 +1,98 @@ +/* mischelp.h - Miscellaneous helper macros and functions + * Copyright (C) 1999, 2000, 2001, 2002, 2003, + * 2006, 2007, 2009 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_MISCHELP_H +#define GNUPG_COMMON_MISCHELP_H + + +/* Check whether the files NAME1 and NAME2 are identical. This is for + example achieved by comparing the inode numbers of the files. */ +int same_file_p (const char *name1, const char *name2); + + +#ifndef HAVE_TIMEGM +#include +time_t timegm (struct tm *tm); +#endif /*!HAVE_TIMEGM*/ + + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) + +/* To avoid that a compiler optimizes certain memset calls away, these + macros may be used instead. */ +#define wipememory2(_ptr,_set,_len) do { \ + volatile char *_vptr=(volatile char *)(_ptr); \ + size_t _vlen=(_len); \ + while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ + } while(0) +#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) + + +/* Include hacks which are mainly required for Slowaris. */ +#ifdef GNUPG_COMMON_NEED_AFLOCAL +#ifndef HAVE_W32_SYSTEM +# include +# include +#else +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#endif + +#ifndef PF_LOCAL +# ifdef PF_UNIX +# define PF_LOCAL PF_UNIX +# else +# define PF_LOCAL AF_UNIX +# endif +#endif /*PF_LOCAL*/ +#ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +#endif /*AF_UNIX*/ + +/* We used to avoid this macro in GnuPG and inlined the AF_LOCAL name + length computation directly with the little twist of adding 1 extra + byte. It seems that this was needed once on an old HP/UX box and + there are also rumours that 4.3 Reno and DEC systems need it. This + one-off buglet did not harm any current system until it came to Mac + OS X where the kernel (as of May 2009) exhibited a strange bug: The + systems basically froze in the connect call if the passed name + contained an invalid directory part. Ignore the old Unices. */ +#ifndef SUN_LEN +# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif /*SUN_LEN*/ +#endif /*GNUPG_COMMON_NEED_AFLOCAL*/ + + +#endif /*GNUPG_COMMON_MISCHELP_H*/ diff --git a/common/mkdir_p.c b/common/mkdir_p.c new file mode 100644 index 0000000..c26cfee --- /dev/null +++ b/common/mkdir_p.c @@ -0,0 +1,187 @@ +/* mkdir_p.c - Create a directory and any missing parents. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "stringhelp.h" +#include "logging.h" +#include "sysutils.h" +#include "mkdir_p.h" + + +gpg_error_t +gnupg_amkdir_p (const char **directory_components) +{ + gpg_error_t err = 0; + int count; + char **dirs; + int i; + + for (count = 0; directory_components[count]; count ++) + ; + + /* log_debug ("%s: %d directory components.\n", __func__, count); */ + + dirs = xtrycalloc (count, sizeof *dirs); + if (!dirs) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + for (i = 0; directory_components[i]; i ++) + { + if (i == 0) + dirs[i] = make_filename_try (directory_components[i], NULL); + else + dirs[i] = make_filename_try (dirs[i-1], directory_components[i], NULL); + if (!dirs[i]) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto out; + } + + /* log_debug ("%s: Directory %d: `%s'.\n", __func__, i, dirs[i]); */ + } + + for (i = count - 1; i >= 0; i --) + { + struct stat s; + + /* log_debug ("%s: stat(%s)\n", __func__, dirs[i]); */ + + if (!stat (dirs[i], &s)) + { + if ( ! S_ISDIR (s.st_mode)) + { + /* log_debug ("%s: %s exists, but is not a directory!\n", */ + /* __func__, dirs[i]); */ + err = gpg_err_make (default_errsource, GPG_ERR_ENOTDIR); + goto out; + } + else + { + /* Got a directory. */ + /* log_debug ("%s: %s exists and is a directory!\n", */ + /* __func__, dirs[i]); */ + err = 0; + break; + } + } + else if (errno == ENOENT) + /* This directory does not exist yet. Continue walking up the + hierarchy. */ + { + /* log_debug ("%s: %s does not exist!\n", */ + /* __func__, dirs[i]); */ + continue; + } + else + /* Some other error code. Die. Note: this could be ENOTDIR + (we return this above), which means that a component of the + path prefix is not a directory. */ + { + /* log_debug ("%s: stat(%s) => %s!\n", */ + /* __func__, dirs[i], strerror (errno)); */ + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto out; + } + } + + assert (i >= -1); + /* DIRS[I] exists. Start with the following entry. */ + i ++; + + for (; i < count; i ++) + { + /* log_debug ("Creating directory: %s\n", dirs[i]); */ + + if (gnupg_mkdir (dirs[i], "-rwx")) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto out; + } + } + + out: + for (i = 0; i < count; i ++) + xfree (dirs[i]); + xfree (dirs); + + /* log_debug ("%s: Returning %s\n", __func__, gpg_strerror (rc)); */ + + return err; +} + + +gpg_error_t +gnupg_mkdir_p (const char *directory_component, ...) +{ + va_list ap; + gpg_error_t err = 0; + int i; + int space = 1; + const char **dirs; + + dirs = xtrymalloc (space * sizeof (char *)); + if (!dirs) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + dirs[0] = directory_component; + + va_start (ap, directory_component); + for (i = 1; dirs[i - 1]; i ++) + { + if (i == space) + { + const char **tmp_dirs; + + space = 2 * space; + tmp_dirs = xtryrealloc (dirs, space * sizeof (char *)); + if (!tmp_dirs) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + break; + } + dirs = tmp_dirs; + } + dirs[i] = va_arg (ap, char *); + } + va_end (ap); + + if (!err) + err = gnupg_amkdir_p (dirs); + + xfree (dirs); + + return err; +} diff --git a/common/mkdir_p.h b/common/mkdir_p.h new file mode 100644 index 0000000..1e939b3 --- /dev/null +++ b/common/mkdir_p.h @@ -0,0 +1,52 @@ +/* mkdir_p.h - Create a directory and any missing parents. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef MKDIR_P_H +#define MKDIR_P_H + +#include "types.h" + +/* Create a directory as well as any missing parents. + + The arguments must be NULL termianted. If DIRECTORY_COMPONENTS... + consists of two elements, "foo/bar" and "xyzzy", this function will + first try to create the directory "foo/bar" and then the directory + "foo/bar/xyzzy". On success returns 0, otherwise an error code is + returned. */ +gpg_error_t gnupg_mkdir_p (const char *directory_component, ...) GPGRT_ATTR_SENTINEL(0); + +/* Like mkdir_p, but DIRECTORY_COMPONENTS is a NULL terminated + array, e.g.: + + char **dirs = { "foo", "bar", NULL }; + amkdir_p (dirs); + */ +gpg_error_t gnupg_amkdir_p (const char **directory_components); + +#endif diff --git a/common/mkstrtable.awk b/common/mkstrtable.awk new file mode 100644 index 0000000..b5d4ef0 --- /dev/null +++ b/common/mkstrtable.awk @@ -0,0 +1,185 @@ +# mkstrtable.awk +# 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 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, see . +# +# As a special exception, g10 Code GmbH gives unlimited permission to +# copy, distribute and modify the C source files that are the output +# of mkstrtable.awk. You need not follow the terms of the GNU General +# Public License when using or distributing such scripts, even though +# portions of the text of mkstrtable.awk appear in them. The GNU +# General Public License (GPL) does govern all other use of the material +# that constitutes the mkstrtable.awk program. +# +# Certain portions of the mkstrtable.awk source text are designed to be +# copied (in certain cases, depending on the input) into the output of +# mkstrtable.awk. We call these the "data" portions. The rest of the +# mkstrtable.awk source text consists of comments plus executable code +# that decides which of the data portions to output in any given case. +# We call these comments and executable code the "non-data" portions. +# mkstrtable.h never copies any of the non-data portions into its output. +# +# This special exception to the GPL applies to versions of mkstrtable.awk +# released by g10 Code GmbH. When you make and distribute a modified version +# of mkstrtable.awk, you may extend this special exception to the GPL to +# apply to your modified version as well, *unless* your modified version +# has the potential to copy into its output some of the text that was the +# non-data portion of the version that you started with. (In other words, +# unless your change moves or copies text from the non-data portions to the +# data portions.) If your modification has such potential, you must delete +# any notice of this special exception to the GPL from your modified version. + +# This script outputs a source file that does define the following +# symbols: +# +# static const char msgstr[]; +# A string containing all messages in the list. +# +# static const int msgidx[]; +# A list of index numbers, one for each message, that points to the +# beginning of the string in msgstr. +# +# msgidxof (code); +# A macro that maps code numbers to idx numbers. If a DEFAULT MESSAGE +# is provided (see below), its index will be returned for unknown codes. +# Otherwise -1 is returned for codes that do not appear in the list. +# You can lookup the message with code CODE with: +# msgstr + msgidx[msgidxof (code)]. +# +# The input file has the following format: +# CODE1 ... MESSAGE1 (code nr, , something, , msg) +# CODE2 ... MESSAGE2 (code nr, , something, , msg) +# ... +# CODEn ... MESSAGEn (code nr, , something, , msg) +# ... DEFAULT-MESSAGE (, something, , fall-back msg) +# +# Comments (starting with # and ending at the end of the line) are removed, +# as is trailing whitespace. The last line is optional; if no DEFAULT +# MESSAGE is given, msgidxof will return the number -1 for unknown +# index numbers. +# +# The field to be used is specified with the variable "textidx" on +# the command line. It defaults to 2. +# +# The variable nogettext can be set to 1 to suppress gettext markers. +# +# The variable prefix can be used to prepend a string to each message. +# +# The variable namespace can be used to prepend a string to each +# variable and macro name. + +BEGIN { + FS = "[\t]+"; +# cpos holds the current position in the message string. + cpos = 0; +# msg holds the number of messages. + msg = 0; + print "/* Output of mkstrtable.awk. DO NOT EDIT. */"; + print ""; + header = 1; + if (textidx == 0) + textidx = 2; +# nogettext can be set to 1 to suppress gettext noop markers. +} + +/^#/ { next; } + +header { + if ($1 ~ /^[0123456789]+$/) + { + print "/* The purpose of this complex string table is to produce"; + print " optimal code with a minimum of relocations. */"; + print ""; + print "static const char " namespace "msgstr[] = "; + header = 0; + } + else + print; +} + +!header { + sub (/\#.+/, ""); + sub (/[ ]+$/, ""); # Strip trailing space and tab characters. + + if (/^$/) + next; + +# Print the string msgstr line by line. We delay output by one line to be able +# to treat the last line differently (see END). + if (last_msgstr) + { + if (nogettext) + print " \"" last_msgstr "\" \"\\0\""; + else + print " gettext_noop (\"" last_msgstr "\") \"\\0\""; + } + last_msgstr = prefix $textidx; + +# Remember the error code and msgidx of each error message. + code[msg] = $1; + pos[msg] = cpos; + cpos += length (last_msgstr) + 1; + msg++; + + if ($1 == "") + { + has_default = 1; + exit; + } +} +END { + if (has_default) + coded_msgs = msg - 1; + else + coded_msgs = msg; + + if (nogettext) + print " \"" prefix last_msgstr "\";"; + else + print " gettext_noop (\"" prefix last_msgstr "\");"; + print ""; + print "static const int " namespace "msgidx[] ="; + print " {"; + for (i = 0; i < coded_msgs; i++) + print " " pos[i] ","; + print " " pos[coded_msgs]; + print " };"; + print ""; + print "#define " namespace "msgidxof(code) (0 ? -1 \\"; + +# Gather the ranges. + skip = code[0]; + start = code[0]; + stop = code[0]; + for (i = 1; i < coded_msgs; i++) + { + if (code[i] == stop + 1) + stop++; + else + { + print " : ((code >= " start ") && (code <= " stop ")) ? (code - " \ + skip ") \\"; + skip += code[i] - stop - 1; + start = code[i]; + stop = code[i]; + } + } + print " : ((code >= " start ") && (code <= " stop ")) ? (code - " \ + skip ") \\"; + if (has_default) + print " : " stop + 1 " - " skip ")"; + else + print " : -1)"; + + } diff --git a/common/name-value.c b/common/name-value.c new file mode 100644 index 0000000..1018668 --- /dev/null +++ b/common/name-value.c @@ -0,0 +1,806 @@ +/* name-value.c - Parser and writer for a name-value format. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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, see . + */ + +/* + * This module aso provides features for the extended private key + * format of gpg-agent. + */ + +#include +#include +#include +#include +#include + +#include "mischelp.h" +#include "strlist.h" +#include "util.h" +#include "name-value.h" + +struct name_value_container +{ + struct name_value_entry *first; + struct name_value_entry *last; + unsigned int private_key_mode:1; +}; + + +struct name_value_entry +{ + struct name_value_entry *prev; + struct name_value_entry *next; + + /* The name. Comments and blank lines have NAME set to NULL. */ + char *name; + + /* The value as stored in the file. We store it when when we parse + a file so that we can reproduce it. */ + strlist_t raw_value; + + /* The decoded value. */ + char *value; +}; + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + + +static inline gpg_error_t +my_error (gpg_err_code_t ec) +{ + return gpg_err_make (default_errsource, ec); +} + + + + +/* Allocation and deallocation. */ + +/* Allocate a private key container structure. */ +nvc_t +nvc_new (void) +{ + return xtrycalloc (1, sizeof (struct name_value_container)); +} + + +/* Allocate a private key container structure for use with private keys. */ +nvc_t +nvc_new_private_key (void) +{ + nvc_t nvc = nvc_new (); + if (nvc) + nvc->private_key_mode = 1; + return nvc; +} + + +static void +nve_release (nve_t entry, int private_key_mode) +{ + if (entry == NULL) + return; + + xfree (entry->name); + if (entry->value && private_key_mode) + wipememory (entry->value, strlen (entry->value)); + xfree (entry->value); + if (private_key_mode) + free_strlist_wipe (entry->raw_value); + else + free_strlist (entry->raw_value); + xfree (entry); +} + + +/* Release a private key container structure. */ +void +nvc_release (nvc_t pk) +{ + nve_t e, next; + + if (pk == NULL) + return; + + for (e = pk->first; e; e = next) + { + next = e->next; + nve_release (e, pk->private_key_mode); + } + + xfree (pk); +} + + + +/* Dealing with names and values. */ + +/* Check whether the given name is valid. Valid names start with a + letter, end with a colon, and contain only alphanumeric characters + and the hyphen. */ +static int +valid_name (const char *name) +{ + size_t i, len = strlen (name); + + if (! alphap (name) || len == 0 || name[len - 1] != ':') + return 0; + + for (i = 1; i < len - 1; i++) + if (! alnump (&name[i]) && name[i] != '-') + return 0; + + return 1; +} + + +/* Makes sure that ENTRY has a RAW_VALUE. */ +static gpg_error_t +assert_raw_value (nve_t entry) +{ + gpg_error_t err = 0; + size_t len, offset; +#define LINELEN 70 + char buf[LINELEN+3]; + + if (entry->raw_value) + return 0; + + len = strlen (entry->value); + offset = 0; + while (len) + { + size_t amount, linelen = LINELEN; + + /* On the first line we need to subtract space for the name. */ + if (entry->raw_value == NULL && strlen (entry->name) < linelen) + linelen -= strlen (entry->name); + + /* See if the rest of the value fits in this line. */ + if (len <= linelen) + amount = len; + else + { + size_t i; + + /* Find a suitable space to break on. */ + for (i = linelen - 1; linelen - i < 30 && linelen - i > offset; i--) + if (ascii_isspace (entry->value[i])) + break; + + if (ascii_isspace (entry->value[i])) + { + /* Found one. */ + amount = i; + } + else + { + /* Just induce a hard break. */ + amount = linelen; + } + } + + snprintf (buf, sizeof buf, " %.*s\n", (int) amount, + &entry->value[offset]); + if (append_to_strlist_try (&entry->raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + offset += amount; + len -= amount; + } + + leave: + if (err) + { + free_strlist_wipe (entry->raw_value); + entry->raw_value = NULL; + } + + return err; +#undef LINELEN +} + + +/* Computes the length of the value encoded as continuation. If + *SWALLOW_WS is set, all whitespace at the beginning of S is + swallowed. If START is given, a pointer to the beginning of the + value is stored there. */ +static size_t +continuation_length (const char *s, int *swallow_ws, const char **start) +{ + size_t len; + + if (*swallow_ws) + { + /* The previous line was a blank line and we inserted a newline. + Swallow all whitespace at the beginning of this line. */ + while (ascii_isspace (*s)) + s++; + } + else + { + /* Iff a continuation starts with more than one space, it + encodes a space. */ + if (ascii_isspace (*s)) + s++; + } + + /* Strip whitespace at the end. */ + len = strlen (s); + while (len > 0 && ascii_isspace (s[len-1])) + len--; + + if (len == 0) + { + /* Blank lines encode newlines. */ + len = 1; + s = "\n"; + *swallow_ws = 1; + } + else + *swallow_ws = 0; + + if (start) + *start = s; + + return len; +} + + +/* Makes sure that ENTRY has a VALUE. */ +static gpg_error_t +assert_value (nve_t entry) +{ + size_t len; + int swallow_ws; + strlist_t s; + char *p; + + if (entry->value) + return 0; + + len = 0; + swallow_ws = 0; + for (s = entry->raw_value; s; s = s->next) + len += continuation_length (s->d, &swallow_ws, NULL); + + /* Add one for the terminating zero. */ + len += 1; + + entry->value = p = xtrymalloc (len); + if (entry->value == NULL) + return my_error_from_syserror (); + + swallow_ws = 0; + for (s = entry->raw_value; s; s = s->next) + { + const char *start; + size_t l = continuation_length (s->d, &swallow_ws, &start); + + memcpy (p, start, l); + p += l; + } + + *p++ = 0; + assert (p - entry->value == len); + + return 0; +} + + +/* Get the name. */ +char * +nve_name (nve_t pke) +{ + return pke->name; +} + + +/* Get the value. */ +char * +nve_value (nve_t pke) +{ + if (assert_value (pke)) + return NULL; + return pke->value; +} + + + +/* Adding and modifying values. */ + +/* Add (NAME, VALUE, RAW_VALUE) to PK. NAME may be NULL for comments + and blank lines. At least one of VALUE and RAW_VALUE must be + given. If PRESERVE_ORDER is not given, entries with the same name + are grouped. NAME, VALUE and RAW_VALUE is consumed. */ +static gpg_error_t +_nvc_add (nvc_t pk, char *name, char *value, strlist_t raw_value, + int preserve_order) +{ + gpg_error_t err = 0; + nve_t e; + + assert (value || raw_value); + + if (name && ! valid_name (name)) + { + err = my_error (GPG_ERR_INV_NAME); + goto leave; + } + + if (name + && pk->private_key_mode + && !ascii_strcasecmp (name, "Key:") + && nvc_lookup (pk, "Key:")) + { + err = my_error (GPG_ERR_INV_NAME); + goto leave; + } + + e = xtrycalloc (1, sizeof *e); + if (e == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + e->name = name; + e->value = value; + e->raw_value = raw_value; + + if (pk->first) + { + nve_t last; + + if (preserve_order || name == NULL) + last = pk->last; + else + { + /* See if there is already an entry with NAME. */ + last = nvc_lookup (pk, name); + + /* If so, find the last in that block. */ + if (last) + { + while (last->next) + { + nve_t next = last->next; + + if (next->name && ascii_strcasecmp (next->name, name) == 0) + last = next; + else + break; + } + } + else /* Otherwise, just find the last entry. */ + last = pk->last; + } + + if (last->next) + { + e->prev = last; + e->next = last->next; + last->next = e; + e->next->prev = e; + } + else + { + e->prev = last; + last->next = e; + pk->last = e; + } + } + else + pk->first = pk->last = e; + + leave: + if (err) + { + xfree (name); + if (value) + wipememory (value, strlen (value)); + xfree (value); + free_strlist_wipe (raw_value); + } + + return err; +} + + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is not updated but the new entry is appended. */ +gpg_error_t +nvc_add (nvc_t pk, const char *name, const char *value) +{ + char *k, *v; + + k = xtrystrdup (name); + if (k == NULL) + return my_error_from_syserror (); + + v = xtrystrdup (value); + if (v == NULL) + { + xfree (k); + return my_error_from_syserror (); + } + + return _nvc_add (pk, k, v, NULL, 0); +} + + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is updated with VALUE. If multiple entries with NAME exist, the + first entry is updated. */ +gpg_error_t +nvc_set (nvc_t pk, const char *name, const char *value) +{ + nve_t e; + + if (! valid_name (name)) + return GPG_ERR_INV_NAME; + + e = nvc_lookup (pk, name); + if (e) + { + char *v; + + v = xtrystrdup (value); + if (v == NULL) + return my_error_from_syserror (); + + free_strlist_wipe (e->raw_value); + e->raw_value = NULL; + if (e->value) + wipememory (e->value, strlen (e->value)); + xfree (e->value); + e->value = v; + + return 0; + } + else + return nvc_add (pk, name, value); +} + + +/* Delete the given entry from PK. */ +void +nvc_delete (nvc_t pk, nve_t entry) +{ + if (entry->prev) + entry->prev->next = entry->next; + else + pk->first = entry->next; + + if (entry->next) + entry->next->prev = entry->prev; + else + pk->last = entry->prev; + + nve_release (entry, pk->private_key_mode); +} + + + +/* Lookup and iteration. */ + +/* Get the first non-comment entry. */ +nve_t +nvc_first (nvc_t pk) +{ + nve_t entry; + for (entry = pk->first; entry; entry = entry->next) + if (entry->name) + return entry; + return NULL; +} + + +/* Get the first entry with the given name. */ +nve_t +nvc_lookup (nvc_t pk, const char *name) +{ + nve_t entry; + for (entry = pk->first; entry; entry = entry->next) + if (entry->name && ascii_strcasecmp (entry->name, name) == 0) + return entry; + return NULL; +} + + +/* Get the next non-comment entry. */ +nve_t +nve_next (nve_t entry) +{ + for (entry = entry->next; entry; entry = entry->next) + if (entry->name) + return entry; + return NULL; +} + + +/* Get the next entry with the given name. */ +nve_t +nve_next_value (nve_t entry, const char *name) +{ + for (entry = entry->next; entry; entry = entry->next) + if (entry->name && ascii_strcasecmp (entry->name, name) == 0) + return entry; + return NULL; +} + + + +/* Private key handling. */ + +/* Get the private key. */ +gpg_error_t +nvc_get_private_key (nvc_t pk, gcry_sexp_t *retsexp) +{ + gpg_error_t err; + nve_t e; + + e = pk->private_key_mode? nvc_lookup (pk, "Key:") : NULL; + if (e == NULL) + return my_error (GPG_ERR_MISSING_KEY); + + err = assert_value (e); + if (err) + return err; + + return gcry_sexp_sscan (retsexp, NULL, e->value, strlen (e->value)); +} + + +/* Set the private key. */ +gpg_error_t +nvc_set_private_key (nvc_t pk, gcry_sexp_t sexp) +{ + gpg_error_t err; + char *raw, *clean, *p; + size_t len, i; + + if (!pk->private_key_mode) + return my_error (GPG_ERR_MISSING_KEY); + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + + raw = xtrymalloc (len); + if (raw == NULL) + return my_error_from_syserror (); + + clean = xtrymalloc (len); + if (clean == NULL) + { + xfree (raw); + return my_error_from_syserror (); + } + + gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, raw, len); + + /* Strip any whitespace at the end. */ + i = strlen (raw) - 1; + while (i && ascii_isspace (raw[i])) + { + raw[i] = 0; + i--; + } + + /* Replace any newlines with spaces, remove superfluous whitespace. */ + len = strlen (raw); + for (p = clean, i = 0; i < len; i++) + { + char c = raw[i]; + + /* Collapse contiguous and superfluous spaces. */ + if (ascii_isspace (c) && i > 0 + && (ascii_isspace (raw[i-1]) || raw[i-1] == '(' || raw[i-1] == ')')) + continue; + + if (c == '\n') + c = ' '; + + *p++ = c; + } + *p = 0; + + err = nvc_set (pk, "Key:", clean); + xfree (raw); + xfree (clean); + return err; +} + + + +/* Parsing and serialization. */ + +static gpg_error_t +do_nvc_parse (nvc_t *result, int *errlinep, estream_t stream, + int for_private_key) +{ + gpg_error_t err = 0; + gpgrt_ssize_t len; + char *buf = NULL; + size_t buf_len = 0; + char *name = NULL; + strlist_t raw_value = NULL; + + *result = for_private_key? nvc_new_private_key () : nvc_new (); + if (*result == NULL) + return my_error_from_syserror (); + + if (errlinep) + *errlinep = 0; + while ((len = es_read_line (stream, &buf, &buf_len, NULL)) > 0) + { + char *p; + if (errlinep) + *errlinep += 1; + + /* Skip any whitespace. */ + for (p = buf; *p && ascii_isspace (*p); p++) + /* Do nothing. */; + + if (name && (spacep (buf) || *p == 0)) + { + /* A continuation. */ + if (append_to_strlist_try (&raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + continue; + } + + /* No continuation. Add the current entry if any. */ + if (raw_value) + { + err = _nvc_add (*result, name, NULL, raw_value, 1); + if (err) + goto leave; + } + + /* And prepare for the next one. */ + name = NULL; + raw_value = NULL; + + if (*p != 0 && *p != '#') + { + char *colon, *value, tmp; + + colon = strchr (buf, ':'); + if (colon == NULL) + { + err = my_error (GPG_ERR_INV_VALUE); + goto leave; + } + + value = colon + 1; + tmp = *value; + *value = 0; + name = xtrystrdup (p); + *value = tmp; + + if (name == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + if (append_to_strlist_try (&raw_value, value) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + continue; + } + + if (append_to_strlist_try (&raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + } + if (len < 0) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Add the final entry. */ + if (raw_value) + err = _nvc_add (*result, name, NULL, raw_value, 1); + + leave: + gpgrt_free (buf); + if (err) + { + nvc_release (*result); + *result = NULL; + } + + return err; +} + + +/* Parse STREAM and return a newly allocated name value container + structure in RESULT. If ERRLINEP is given, the line number the + parser was last considering is stored there. */ +gpg_error_t +nvc_parse (nvc_t *result, int *errlinep, estream_t stream) +{ + return do_nvc_parse (result, errlinep, stream, 0); +} + + +/* Parse STREAM and return a newly allocated name value container + structure in RESULT - assuming the extended private key format. If + ERRLINEP is given, the line number the parser was last considering + is stored there. */ +gpg_error_t +nvc_parse_private_key (nvc_t *result, int *errlinep, estream_t stream) +{ + return do_nvc_parse (result, errlinep, stream, 1); +} + + +/* Write a representation of PK to STREAM. */ +gpg_error_t +nvc_write (nvc_t pk, estream_t stream) +{ + gpg_error_t err; + nve_t entry; + strlist_t s; + + for (entry = pk->first; entry; entry = entry->next) + { + if (entry->name) + es_fputs (entry->name, stream); + + err = assert_raw_value (entry); + if (err) + return err; + + for (s = entry->raw_value; s; s = s->next) + es_fputs (s->d, stream); + + if (es_ferror (stream)) + return my_error_from_syserror (); + } + + return 0; +} diff --git a/common/name-value.h b/common/name-value.h new file mode 100644 index 0000000..db9270a --- /dev/null +++ b/common/name-value.h @@ -0,0 +1,120 @@ +/* name-value.h - Parser and writer for a name-value format. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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, see . + */ + +#ifndef GNUPG_COMMON_NAME_VALUE_H +#define GNUPG_COMMON_NAME_VALUE_H + +struct name_value_container; +typedef struct name_value_container *nvc_t; + +struct name_value_entry; +typedef struct name_value_entry *nve_t; + + + +/* Memory management, and dealing with entries. */ + +/* Allocate a name value container structure. */ +nvc_t nvc_new (void); + +/* Allocate a name value container structure for use with the extended + * private key format. */ +nvc_t nvc_new_private_key (void); + +/* Release a name value container structure. */ +void nvc_release (nvc_t pk); + +/* Get the name. */ +char *nve_name (nve_t pke); + +/* Get the value. */ +char *nve_value (nve_t pke); + + + +/* Lookup and iteration. */ + +/* Get the first non-comment entry. */ +nve_t nvc_first (nvc_t pk); + +/* Get the first entry with the given name. */ +nve_t nvc_lookup (nvc_t pk, const char *name); + +/* Get the next non-comment entry. */ +nve_t nve_next (nve_t entry); + +/* Get the next entry with the given name. */ +nve_t nve_next_value (nve_t entry, const char *name); + + + +/* Adding and modifying values. */ + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is not updated but the new entry is appended. */ +gpg_error_t nvc_add (nvc_t pk, const char *name, const char *value); + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is updated with VALUE. If multiple entries with NAME exist, the + first entry is updated. */ +gpg_error_t nvc_set (nvc_t pk, const char *name, const char *value); + +/* Delete the given entry from PK. */ +void nvc_delete (nvc_t pk, nve_t pke); + + + +/* Private key handling. */ + +/* Get the private key. */ +gpg_error_t nvc_get_private_key (nvc_t pk, gcry_sexp_t *retsexp); + +/* Set the private key. */ +gpg_error_t nvc_set_private_key (nvc_t pk, gcry_sexp_t sexp); + + + +/* Parsing and serialization. */ + +/* Parse STREAM and return a newly allocated private key container + structure in RESULT. If ERRLINEP is given, the line number the + parser was last considering is stored there. */ +gpg_error_t nvc_parse (nvc_t *result, int *errlinep, estream_t stream); + +/* Parse STREAM and return a newly allocated name value container + structure in RESULT - assuming the extended private key format. If + ERRLINEP is given, the line number the parser was last considering + is stored there. */ +gpg_error_t nvc_parse_private_key (nvc_t *result, int *errlinep, + estream_t stream); + +/* Write a representation of PK to STREAM. */ +gpg_error_t nvc_write (nvc_t pk, estream_t stream); + +#endif /* GNUPG_COMMON_NAME_VALUE_H */ diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c new file mode 100644 index 0000000..e7c68f2 --- /dev/null +++ b/common/openpgp-oid.c @@ -0,0 +1,443 @@ +/* openpgp-oids.c - OID helper for OpenPGP + * Copyright (C) 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "openpgpdefs.h" + +/* A table with all our supported OpenPGP curves. */ +static struct { + const char *name; /* Standard name. */ + const char *oidstr; /* IETF formatted OID. */ + unsigned int nbits; /* Nominal bit length of the curve. */ + const char *alias; /* NULL or alternative name of the curve. */ + int pubkey_algo; /* Required OpenPGP algo or 0 for ECDSA/ECDH. */ +} oidtable[] = { + + { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", PUBKEY_ALGO_ECDH }, + { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", PUBKEY_ALGO_EDDSA }, + + { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, + { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, + { "NIST P-521", "1.3.132.0.35", 521, "nistp521" }, + + { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256 }, + { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384 }, + { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512 }, + + { "secp256k1", "1.3.132.0.10", 256 }, + + { NULL, NULL, 0} +}; + + +/* The OID for Curve Ed25519 in OpenPGP format. */ +static const char oid_ed25519[] = + { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; + +/* The OID for Curve25519 in OpenPGP format. */ +static const char oid_cv25519[] = + { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; + + +/* Helper for openpgp_oid_from_str. */ +static size_t +make_flagged_int (unsigned long value, char *buf, size_t buflen) +{ + int more = 0; + int shift; + + /* fixme: figure out the number of bits in an ulong and start with + that value as shift (after making it a multiple of 7) a more + straigtforward implementation is to do it in reverse order using + a temporary buffer - saves a lot of compares */ + for (more=0, shift=28; shift > 0; shift -= 7) + { + if (more || value >= (1<> shift); + value -= (value >> shift) << shift; + more = 1; + } + } + buf[buflen++] = value; + return buflen; +} + + +/* Convert the OID given in dotted decimal form in STRING to an DER + * encoding and store it as an opaque value at R_MPI. The format of + * the DER encoded is not a regular ASN.1 object but the modified + * format as used by OpenPGP for the ECC curve description. On error + * the function returns and error code an NULL is stored at R_BUG. + * Note that scanning STRING stops at the first white space + * character. */ +gpg_error_t +openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) +{ + unsigned char *buf; + size_t buflen; + unsigned long val1, val; + const char *endp; + int arcno; + + *r_mpi = NULL; + + if (!string || !*string) + return gpg_error (GPG_ERR_INV_VALUE); + + /* We can safely assume that the encoded OID is shorter than the string. */ + buf = xtrymalloc (1 + strlen (string) + 2); + if (!buf) + return gpg_error_from_syserror (); + /* Save the first byte for the length. */ + buflen = 1; + + val1 = 0; /* Avoid compiler warning. */ + arcno = 0; + do { + arcno++; + val = strtoul (string, (char**)&endp, 10); + if (!digitp (string) || !(*endp == '.' || !*endp)) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + if (*endp == '.') + string = endp+1; + + if (arcno == 1) + { + if (val > 2) + break; /* Not allowed, error catched below. */ + val1 = val; + } + else if (arcno == 2) + { /* Need to combine the first two arcs in one octet. */ + if (val1 < 2) + { + if (val > 39) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + buf[buflen++] = val1*40 + val; + } + else + { + val += 80; + buflen = make_flagged_int (val, buf, buflen); + } + } + else + { + buflen = make_flagged_int (val, buf, buflen); + } + } while (*endp == '.'); + + if (arcno == 1 || buflen < 2 || buflen > 254 ) + { /* It is not possible to encode only the first arc. */ + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + + *buf = buflen - 1; + *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); + if (!*r_mpi) + { + xfree (buf); + return gpg_error_from_syserror (); + } + return 0; +} + + +/* Return a malloced string represenation of the OID in the opaque MPI + A. In case of an error NULL is returned and ERRNO is set. */ +char * +openpgp_oid_to_str (gcry_mpi_t a) +{ + const unsigned char *buf; + size_t length; + unsigned int lengthi; + char *string, *p; + int n = 0; + unsigned long val, valmask; + + valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); + + if (!a + || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE) + || !(buf = gcry_mpi_get_opaque (a, &lengthi))) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + buf = gcry_mpi_get_opaque (a, &lengthi); + length = (lengthi+7)/8; + + /* The first bytes gives the length; check consistency. */ + if (!length || buf[0] != length -1) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + /* Skip length byte. */ + length--; + buf++; + + /* To calculate the length of the string we can safely assume an + upper limit of 3 decimal characters per byte. Two extra bytes + account for the special first octect */ + string = p = xtrymalloc (length*(1+3)+2+1); + if (!string) + return NULL; + if (!length) + { + *p = 0; + return string; + } + + if (buf[0] < 40) + p += sprintf (p, "0.%d", buf[n]); + else if (buf[0] < 80) + p += sprintf (p, "1.%d", buf[n]-40); + else { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < length ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + if (val < 80) + goto badoid; + val -= 80; + sprintf (p, "2.%lu", val); + p += strlen (p); + } + for (n++; n < length; n++) + { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < length ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + sprintf (p, ".%lu", val); + p += strlen (p); + } + + *p = 0; + return string; + + badoid: + /* Return a special OID (gnu.gnupg.badoid) to indicate the error + case. The OID is broken and thus we return one which can't do + any harm. Formally this does not need to be a bad OID but an OID + with an arc that can't be represented in a 32 bit word is more + than likely corrupt. */ + xfree (string); + return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); +} + + + +/* Return true if A represents the OID for Ed25519. */ +int +openpgp_oid_is_ed25519 (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int nbits; + size_t n; + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; + + buf = gcry_mpi_get_opaque (a, &nbits); + n = (nbits+7)/8; + return (n == DIM (oid_ed25519) + && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); +} + + +int +openpgp_oid_is_cv25519 (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int nbits; + size_t n; + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; + + buf = gcry_mpi_get_opaque (a, &nbits); + n = (nbits+7)/8; + return (n == DIM (oid_cv25519) + && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))); +} + + +/* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL + store the bit size of the curve there. Returns NULL for unknown + curve names. */ +const char * +openpgp_curve_to_oid (const char *name, unsigned int *r_nbits) +{ + int i; + unsigned int nbits = 0; + const char *oidstr = NULL; + + if (name) + { + for (i=0; oidtable[i].name; i++) + if (!strcmp (oidtable[i].name, name) + || (oidtable[i].alias && !strcmp (oidtable[i].alias, name))) + { + oidstr = oidtable[i].oidstr; + nbits = oidtable[i].nbits; + break; + } + if (!oidtable[i].name) + { + /* If not found assume the input is already an OID and check + whether we support it. */ + for (i=0; oidtable[i].name; i++) + if (!strcmp (name, oidtable[i].oidstr)) + { + oidstr = oidtable[i].oidstr; + nbits = oidtable[i].nbits; + break; + } + } + } + + if (r_nbits) + *r_nbits = nbits; + return oidstr; +} + + +/* Map an OpenPGP OID to the Libgcrypt curve NAME. Returns NULL for + unknown curve names. Unless CANON is set we prefer an alias name + here which is more suitable for printing. */ +const char * +openpgp_oid_to_curve (const char *oidstr, int canon) +{ + int i; + + if (!oidstr) + return NULL; + + for (i=0; oidtable[i].name; i++) + if (!strcmp (oidtable[i].oidstr, oidstr)) + return !canon && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; + + return NULL; +} + + +/* Return true if the curve with NAME is supported. */ +static int +curve_supported_p (const char *name) +{ + int result = 0; + gcry_sexp_t keyparms; + + if (!gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", name)) + { + result = !!gcry_pk_get_curve (keyparms, 0, NULL); + gcry_sexp_release (keyparms); + } + return result; +} + + +/* Enumerate available and supported OpenPGP curves. The caller needs + to set the integer variable at ITERP to zero and keep on calling + this function until NULL is returned. */ +const char * +openpgp_enum_curves (int *iterp) +{ + int idx = *iterp; + + while (idx >= 0 && idx < DIM (oidtable) && oidtable[idx].name) + { + if (curve_supported_p (oidtable[idx].name)) + { + *iterp = idx + 1; + return oidtable[idx].alias? oidtable[idx].alias : oidtable[idx].name; + } + idx++; + } + *iterp = idx; + return NULL; +} + + +/* Return the Libgcrypt name for the gpg curve NAME if supported. If + * R_ALGO is not NULL the required OpenPGP public key algo or 0 is + * stored at that address. If R_NBITS is not NULL the nominal bitsize + * of the curves is stored there. NULL is returned if the curve is + * not supported. */ +const char * +openpgp_is_curve_supported (const char *name, int *r_algo, + unsigned int *r_nbits) +{ + int idx; + + if (r_algo) + *r_algo = 0; + if (r_nbits) + *r_nbits = 0; + for (idx = 0; idx < DIM (oidtable) && oidtable[idx].name; idx++) + { + if ((!strcmp (name, oidtable[idx].name) + || (oidtable[idx].alias && !strcmp (name, (oidtable[idx].alias)))) + && curve_supported_p (oidtable[idx].name)) + { + if (r_algo) + *r_algo = oidtable[idx].pubkey_algo; + if (r_nbits) + *r_nbits = oidtable[idx].nbits; + return oidtable[idx].name; + } + } + return NULL; +} diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h new file mode 100644 index 0000000..3d5d306 --- /dev/null +++ b/common/openpgpdefs.h @@ -0,0 +1,184 @@ +/* openpgpdefs.h - Constants from the OpenPGP standard (rfc2440) + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_OPENPGPDEFS_H +#define GNUPG_COMMON_OPENPGPDEFS_H + +typedef enum + { + PKT_NONE = 0, + PKT_PUBKEY_ENC = 1, /* Public key encrypted packet. */ + PKT_SIGNATURE = 2, /* Secret key encrypted packet. */ + PKT_SYMKEY_ENC = 3, /* Session key packet. */ + PKT_ONEPASS_SIG = 4, /* One pass sig packet. */ + PKT_SECRET_KEY = 5, /* Secret key. */ + PKT_PUBLIC_KEY = 6, /* Public key. */ + PKT_SECRET_SUBKEY = 7, /* Secret subkey. */ + PKT_COMPRESSED = 8, /* Compressed data packet. */ + PKT_ENCRYPTED = 9, /* Conventional encrypted data. */ + PKT_MARKER = 10, /* Marker packet. */ + PKT_PLAINTEXT = 11, /* Literal data packet. */ + PKT_RING_TRUST = 12, /* Keyring trust packet. */ + PKT_USER_ID = 13, /* User id packet. */ + PKT_PUBLIC_SUBKEY = 14, /* Public subkey. */ + PKT_OLD_COMMENT = 16, /* Comment packet from an OpenPGP draft. */ + PKT_ATTRIBUTE = 17, /* PGP's attribute packet. */ + PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */ + PKT_MDC = 19, /* Manipulation detection code packet. */ + PKT_COMMENT = 61, /* new comment packet (GnuPG specific). */ + PKT_GPG_CONTROL = 63 /* internal control packet (GnuPG specific). */ + } +pkttype_t; + +static inline const char * +pkttype_str (pkttype_t type) +{ + switch (type) + { + case PKT_PUBKEY_ENC: return "PUBKEY_ENC"; + case PKT_SIGNATURE: return "SIGNATURE"; + case PKT_SYMKEY_ENC: return "SYMKEY_ENC"; + case PKT_ONEPASS_SIG: return "ONEPASS_SIG"; + case PKT_SECRET_KEY: return "SECRET_KEY"; + case PKT_PUBLIC_KEY: return "PUBLIC_KEY"; + case PKT_SECRET_SUBKEY: return "SECRET_SUBKEY"; + case PKT_COMPRESSED: return "COMPRESSED"; + case PKT_ENCRYPTED: return "ENCRYPTED"; + case PKT_MARKER: return "MARKER"; + case PKT_PLAINTEXT: return "PLAINTEXT"; + case PKT_RING_TRUST: return "RING_TRUST"; + case PKT_USER_ID: return "USER_ID"; + case PKT_PUBLIC_SUBKEY: return "PUBLIC_SUBKEY"; + case PKT_OLD_COMMENT: return "OLD_COMMENT"; + case PKT_ATTRIBUTE: return "ATTRIBUTE"; + case PKT_ENCRYPTED_MDC: return "ENCRYPTED_MDC"; + case PKT_MDC: return "MDC"; + case PKT_COMMENT: return "COMMENT"; + case PKT_GPG_CONTROL: return "GPG_CONTROL"; + default: return "unknown packet type"; + } +} + +typedef enum + { + SIGSUBPKT_TEST_CRITICAL = -3, + SIGSUBPKT_LIST_UNHASHED = -2, + SIGSUBPKT_LIST_HASHED = -1, + SIGSUBPKT_NONE = 0, + SIGSUBPKT_SIG_CREATED = 2, /* Signature creation time. */ + SIGSUBPKT_SIG_EXPIRE = 3, /* Signature expiration time. */ + SIGSUBPKT_EXPORTABLE = 4, /* Exportable. */ + SIGSUBPKT_TRUST = 5, /* Trust signature. */ + SIGSUBPKT_REGEXP = 6, /* Regular expression. */ + SIGSUBPKT_REVOCABLE = 7, /* Revocable. */ + SIGSUBPKT_KEY_EXPIRE = 9, /* Key expiration time. */ + SIGSUBPKT_ARR = 10, /* Additional recipient request. */ + SIGSUBPKT_PREF_SYM = 11, /* Preferred symmetric algorithms. */ + SIGSUBPKT_REV_KEY = 12, /* Revocation key. */ + SIGSUBPKT_ISSUER = 16, /* Issuer key ID. */ + SIGSUBPKT_NOTATION = 20, /* Notation data. */ + SIGSUBPKT_PREF_HASH = 21, /* Preferred hash algorithms. */ + SIGSUBPKT_PREF_COMPR = 22, /* Preferred compression algorithms. */ + SIGSUBPKT_KS_FLAGS = 23, /* Key server preferences. */ + SIGSUBPKT_PREF_KS = 24, /* Preferred keyserver. */ + SIGSUBPKT_PRIMARY_UID = 25, /* Primary user id. */ + SIGSUBPKT_POLICY = 26, /* Policy URL. */ + SIGSUBPKT_KEY_FLAGS = 27, /* Key flags. */ + SIGSUBPKT_SIGNERS_UID = 28, /* Signer's user id. */ + SIGSUBPKT_REVOC_REASON = 29, /* Reason for revocation. */ + SIGSUBPKT_FEATURES = 30, /* Feature flags. */ + + SIGSUBPKT_SIGNATURE = 32, /* Embedded signature. */ + SIGSUBPKT_ISSUER_FPR = 33, /* EXPERIMENTAL: Issuer fingerprint. */ + + SIGSUBPKT_FLAG_CRITICAL = 128 + } +sigsubpkttype_t; + + +typedef enum + { + CIPHER_ALGO_NONE = 0, + CIPHER_ALGO_IDEA = 1, + CIPHER_ALGO_3DES = 2, + CIPHER_ALGO_CAST5 = 3, + CIPHER_ALGO_BLOWFISH = 4, /* 128 bit */ + /* 5 & 6 are reserved */ + CIPHER_ALGO_AES = 7, + CIPHER_ALGO_AES192 = 8, + CIPHER_ALGO_AES256 = 9, + CIPHER_ALGO_TWOFISH = 10, /* 256 bit */ + CIPHER_ALGO_CAMELLIA128 = 11, + CIPHER_ALGO_CAMELLIA192 = 12, + CIPHER_ALGO_CAMELLIA256 = 13 + } +cipher_algo_t; + + +typedef enum + { + PUBKEY_ALGO_RSA = 1, + PUBKEY_ALGO_RSA_E = 2, /* RSA encrypt only (legacy). */ + PUBKEY_ALGO_RSA_S = 3, /* RSA sign only (legacy). */ + PUBKEY_ALGO_ELGAMAL_E = 16, /* Elgamal encrypt only. */ + PUBKEY_ALGO_DSA = 17, + PUBKEY_ALGO_ECDH = 18, /* RFC-6637 */ + PUBKEY_ALGO_ECDSA = 19, /* RFC-6637 */ + PUBKEY_ALGO_ELGAMAL = 20, /* Elgamal encrypt+sign (legacy). */ + /* 21 reserved by OpenPGP. */ + PUBKEY_ALGO_EDDSA = 22 /* EdDSA (not yet assigned). */ + } +pubkey_algo_t; + + +typedef enum + { + DIGEST_ALGO_MD5 = 1, + DIGEST_ALGO_SHA1 = 2, + DIGEST_ALGO_RMD160 = 3, + /* 4, 5, 6, and 7 are reserved. */ + DIGEST_ALGO_SHA256 = 8, + DIGEST_ALGO_SHA384 = 9, + DIGEST_ALGO_SHA512 = 10, + DIGEST_ALGO_SHA224 = 11 + } +digest_algo_t; + + +typedef enum + { + COMPRESS_ALGO_NONE = 0, + COMPRESS_ALGO_ZIP = 1, + COMPRESS_ALGO_ZLIB = 2, + COMPRESS_ALGO_BZIP2 = 3 + } +compress_algo_t; + + +#endif /*GNUPG_COMMON_OPENPGPDEFS_H*/ diff --git a/common/percent.c b/common/percent.c new file mode 100644 index 0000000..569c5fd --- /dev/null +++ b/common/percent.c @@ -0,0 +1,238 @@ +/* percent.c - Percent escaping + * Copyright (C) 2008, 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" + + +/* Create a newly alloced string from STRING with all spaces and + control characters converted to plus signs or %xx sequences. The + function returns the new string or NULL in case of a malloc + failure. + + Note that we also escape the quote character to work around a bug + in the mingw32 runtime which does not correcty handle command line + quoting. We correctly double the quote mark when calling a program + (i.e. gpg-protect-tool), but the pre-main code does not notice the + double quote as an escaped quote. We do this also on POSIX systems + for consistency. */ +char * +percent_plus_escape (const char *string) +{ + char *buffer, *p; + const char *s; + size_t length; + + for (length=1, s=string; *s; s++) + { + if (*s == '+' || *s == '\"' || *s == '%' + || *(const unsigned char *)s < 0x20) + length += 3; + else + length++; + } + + buffer = p = xtrymalloc (length); + if (!buffer) + return NULL; + + for (s=string; *s; s++) + { + if (*s == '+' || *s == '\"' || *s == '%' + || *(const unsigned char *)s < 0x20) + { + snprintf (p, 4, "%%%02X", *(unsigned char *)s); + p += 3; + } + else if (*s == ' ') + *p++ = '+'; + else + *p++ = *s; + } + *p = 0; + + return buffer; + +} + + +/* Do the percent and plus/space unescaping from STRING to BUFFER and + return the length of the valid buffer. Plus unescaping is only + done if WITHPLUS is true. An escaped Nul character will be + replaced by NULREPL. */ +static size_t +do_unescape (unsigned char *buffer, const unsigned char *string, + int withplus, int nulrepl) +{ + unsigned char *p = buffer; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p = xtoi_2 (string); + if (!*p) + *p = nulrepl; + string++; + } + else if (*string == '+' && withplus) + *p = ' '; + else + *p = *string; + p++; + string++; + } + + return (p - buffer); +} + + +/* Count space required after unescaping STRING. Note that this will + never be larger than strlen (STRING). */ +static size_t +count_unescape (const unsigned char *string) +{ + size_t n = 0; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + string++; + } + string++; + n++; + } + + return n; +} + + +/* Helper. */ +static char * +do_plus_or_plain_unescape (const char *string, int withplus, int nulrepl) +{ + size_t nbytes, n; + char *newstring; + + nbytes = count_unescape (string); + newstring = xtrymalloc (nbytes+1); + if (newstring) + { + n = do_unescape (newstring, string, withplus, nulrepl); + assert (n == nbytes); + newstring[n] = 0; + } + return newstring; +} + + +/* Create a new allocated string from STRING with all "%xx" sequences + decoded and all plus signs replaced by a space. Embedded Nul + characters are replaced by the value of NULREPL. The function + returns the new string or NULL in case of a malloc failure. */ +char * +percent_plus_unescape (const char *string, int nulrepl) +{ + return do_plus_or_plain_unescape (string, 1, nulrepl); +} + + +/* Create a new allocated string from STRING with all "%xx" sequences + decoded. Embedded Nul characters are replaced by the value of + NULREPL. The function returns the new string or NULL in case of a + malloc failure. */ +char * +percent_unescape (const char *string, int nulrepl) +{ + return do_plus_or_plain_unescape (string, 0, nulrepl); +} + + +static size_t +do_unescape_inplace (char *string, int withplus, int nulrepl) +{ + unsigned char *p, *p0; + + p = p0 = string; + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p = xtoi_2 (string); + if (!*p) + *p = nulrepl; + string++; + } + else if (*string == '+' && withplus) + *p = ' '; + else + *p = *string; + p++; + string++; + } + + return (p - p0); +} + + +/* Perform percent and plus unescaping in STRING and return the new + valid length of the string. Embedded Nul characters are replaced + by the value of NULREPL. A terminating Nul character is not + inserted; the caller might want to call this function this way: + + foo[percent_plus_unescape_inplace (foo, 0)] = 0; + */ +size_t +percent_plus_unescape_inplace (char *string, int nulrepl) +{ + return do_unescape_inplace (string, 1, nulrepl); +} + + +/* Perform percent unescaping in STRING and return the new valid + length of the string. Embedded Nul characters are replaced by the + value of NULREPL. A terminating Nul character is not inserted; the + caller might want to call this function this way: + + foo[percent_unescape_inplace (foo, 0)] = 0; + */ +size_t +percent_unescape_inplace (char *string, int nulrepl) +{ + return do_unescape_inplace (string, 0, nulrepl); +} diff --git a/common/recsel.c b/common/recsel.c new file mode 100644 index 0000000..66c1c5e --- /dev/null +++ b/common/recsel.c @@ -0,0 +1,624 @@ +/* recsel.c - Record selection + * Copyright (C) 2014, 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "recsel.h" + +/* Select operators. */ +typedef enum + { + SELECT_SAME, + SELECT_SUB, + SELECT_NONEMPTY, + SELECT_ISTRUE, + SELECT_EQ, /* Numerically equal. */ + SELECT_LE, + SELECT_GE, + SELECT_LT, + SELECT_GT, + SELECT_STRLE, /* String is less or equal. */ + SELECT_STRGE, + SELECT_STRLT, + SELECT_STRGT + } select_op_t; + + +/* Definition for a select expression. */ +struct recsel_expr_s +{ + recsel_expr_t next; + select_op_t op; /* Operation code. */ + unsigned int not:1; /* Negate operators. */ + unsigned int disjun:1;/* Start of a disjunction. */ + unsigned int xcase:1; /* String match is case sensitive. */ + const char *value; /* (Points into NAME.) */ + long numvalue; /* strtol of VALUE. */ + char name[1]; /* Name of the property. */ +}; + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +/* Helper */ +static inline gpg_error_t +my_error (gpg_err_code_t ec) +{ + return gpg_err_make (default_errsource, ec); +} + + +/* This is a case-sensitive version of our memistr. I wonder why no + * standard function memstr exists but I better do not use the name + * memstr to avoid future conflicts. + * + * FIXME: Move this to a stringhelp.c + */ +static const char * +my_memstr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if (*t == *s) + { + for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--) + ; + if (!*s) + return (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + + +/* Return a pointer to the next logical connection operator or NULL if + * none. */ +static char * +find_next_lc (char *string) +{ + char *p1, *p2; + + p1 = strchr (string, '&'); + if (p1 && p1[1] != '&') + p1 = NULL; + p2 = strchr (string, '|'); + if (p2 && p2[1] != '|') + p2 = NULL; + if (p1 && !p2) + return p1; + if (!p1) + return p2; + return p1 < p2 ? p1 : p2; +} + + +/* Parse an expression. The expression syntax is: + * + * [] {{} PROPNAME VALUE []} + * + * A [] indicates an optional part, a {} a repetition. PROPNAME and + * VALUE may not be the empty string. White space between the + * elements is ignored. Numerical values are computed as long int; + * standard C notation applies. is the logical connection + * operator; either "&&" for a conjunction or "||" for a disjunction. + * A conjunction is assumed at the begin of an expression and + * conjunctions have higher precedence than disjunctions. If VALUE + * starts with one of the characters used in any a space after + * the is required. A VALUE is terminated by an unless the + * "--" is used in which case the VALUE spans to the end of the + * expression. may be any of + * + * =~ Substring must match + * !~ Substring must not match + * = The full string must match + * <> The full string must not match + * == The numerical value must match + * != The numerical value must not match + * <= The numerical value of the field must be LE than the value. + * < The numerical value of the field must be LT than the value. + * >= The numerical value of the field must be GT than the value. + * >= The numerical value of the field must be GE than the value. + * -n True if value is not empty (no VALUE parameter allowed). + * -z True if value is empty (no VALUE parameter allowed). + * -t Alias for "PROPNAME != 0" (no VALUE parameter allowed). + * -f Alias for "PROPNAME == 0" (no VALUE parameter allowed). + * + * Values for must be space separated and any of: + * + * -- VALUE spans to the end of the expression. + * -c The string match in this part is done case-sensitive. + * + * For example four calls to recsel_parse_expr() with these values for + * EXPR + * + * "uid =~ Alfa" + * "&& uid !~ Test" + * "|| uid =~ Alpha" + * "uid !~ Test" + * + * or the equivalent expression + * + * "uid =~ Alfa" && uid !~ Test" || uid =~ Alpha" && "uid !~ Test" + * + * are making a selector for records where the "uid" property contains + * the strings "Alfa" or "Alpha" but not the String "test". + * + * The caller must pass the address of a selector variable to this + * function and initialize the value of the function to NULL before + * the first call. recset_release needs to be called to free the + * selector. + */ +gpg_error_t +recsel_parse_expr (recsel_expr_t *selector, const char *expression) +{ + recsel_expr_t se_head = NULL; + recsel_expr_t se, se2; + char *expr_buffer; + char *expr; + char *s0, *s; + int toend = 0; + int xcase = 0; + int disjun = 0; + char *next_lc = NULL; + + while (*expression == ' ' || *expression == '\t') + expression++; + + expr_buffer = xtrystrdup (expression); + if (!expr_buffer) + return my_error_from_syserror (); + expr = expr_buffer; + + if (*expr == '|' && expr[1] == '|') + { + disjun = 1; + expr += 2; + } + else if (*expr == '&' && expr[1] == '&') + expr += 2; + + next_term: + while (*expr == ' ' || *expr == '\t') + expr++; + + while (*expr == '-') + { + switch (*++expr) + { + case '-': toend = 1; break; + case 'c': xcase = 1; break; + default: + log_error ("invalid flag '-%c' in expression\n", *expr); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_INV_FLAG); + } + expr++; + while (*expr == ' ' || *expr == '\t') + expr++; + } + + next_lc = toend? NULL : find_next_lc (expr); + if (next_lc) + *next_lc = 0; /* Terminate this term. */ + + se = xtrymalloc (sizeof *se + strlen (expr)); + if (!se) + return my_error_from_syserror (); + strcpy (se->name, expr); + se->next = NULL; + se->not = 0; + se->disjun = disjun; + se->xcase = xcase; + + if (!se_head) + se_head = se; + else + { + for (se2 = se_head; se2->next; se2 = se2->next) + ; + se2->next = se; + } + + + s = strpbrk (expr, "=<>!~-"); + if (!s || s == expr ) + { + log_error ("no field name given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_NO_NAME); + } + s0 = s; + + if (!strncmp (s, "=~", 2)) + { + se->op = SELECT_SUB; + s += 2; + } + else if (!strncmp (s, "!~", 2)) + { + se->op = SELECT_SUB; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "<>", 2)) + { + se->op = SELECT_SAME; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "==", 2)) + { + se->op = SELECT_EQ; + s += 2; + } + else if (!strncmp (s, "!=", 2)) + { + se->op = SELECT_EQ; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "<=", 2)) + { + se->op = SELECT_LE; + s += 2; + } + else if (!strncmp (s, ">=", 2)) + { + se->op = SELECT_GE; + s += 2; + } + else if (!strncmp (s, "<", 1)) + { + se->op = SELECT_LT; + s += 1; + } + else if (!strncmp (s, ">", 1)) + { + se->op = SELECT_GT; + s += 1; + } + else if (!strncmp (s, "=", 1)) + { + se->op = SELECT_SAME; + s += 1; + } + else if (!strncmp (s, "-z", 2)) + { + se->op = SELECT_NONEMPTY; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "-n", 2)) + { + se->op = SELECT_NONEMPTY; + s += 2; + } + else if (!strncmp (s, "-f", 2)) + { + se->op = SELECT_ISTRUE; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "-t", 2)) + { + se->op = SELECT_ISTRUE; + s += 2; + } + else if (!strncmp (s, "-le", 3)) + { + se->op = SELECT_STRLE; + s += 3; + } + else if (!strncmp (s, "-ge", 3)) + { + se->op = SELECT_STRGE; + s += 3; + } + else if (!strncmp (s, "-lt", 3)) + { + se->op = SELECT_STRLT; + s += 3; + } + else if (!strncmp (s, "-gt", 3)) + { + se->op = SELECT_STRGT; + s += 3; + } + else + { + log_error ("invalid operator in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_INV_OP); + } + + /* We require that a space is used if the value starts with any of + the operator characters. */ + if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE) + ; + else if (strchr ("=<>!~", *s)) + { + log_error ("invalid operator in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_INV_OP); + } + + while (*s == ' ' || *s == '\t') + s++; + + if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE) + { + if (*s) + { + log_error ("value given for -n or -z\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_SYNTAX); + } + } + else + { + if (!*s) + { + log_error ("no value given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_MISSING_VALUE); + } + } + + se->name[s0 - expr] = 0; + trim_spaces (se->name); + if (!se->name[0]) + { + log_error ("no field name given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_NO_NAME); + } + + trim_spaces (se->name + (s - expr)); + se->value = se->name + (s - expr); + if (!se->value[0] && !(se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)) + { + log_error ("no value given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_MISSING_VALUE); + } + + se->numvalue = strtol (se->value, NULL, 0); + + if (next_lc) + { + disjun = next_lc[1] == '|'; + expr = next_lc + 2; + goto next_term; + } + + /* Read:y Append to passes last selector. */ + if (!*selector) + *selector = se_head; + else + { + for (se2 = *selector; se2->next; se2 = se2->next) + ; + se2->next = se_head; + } + + xfree (expr_buffer); + return 0; +} + + +void +recsel_release (recsel_expr_t a) +{ + while (a) + { + recsel_expr_t tmp = a->next; + xfree (a); + a = tmp; + } +} + + +void +recsel_dump (recsel_expr_t selector) +{ + recsel_expr_t se; + + log_debug ("--- Begin selectors ---\n"); + for (se = selector; se; se = se->next) + { + log_debug ("%s %s %s %s '%s'\n", + se==selector? " ": (se->disjun? "||":"&&"), + se->xcase? "-c":" ", + se->name, + se->op == SELECT_SAME? (se->not? "<>":"= "): + se->op == SELECT_SUB? (se->not? "!~":"=~"): + se->op == SELECT_NONEMPTY?(se->not? "-z":"-n"): + se->op == SELECT_ISTRUE? (se->not? "-f":"-t"): + se->op == SELECT_EQ? (se->not? "!=":"=="): + se->op == SELECT_LT? "< ": + se->op == SELECT_LE? "<=": + se->op == SELECT_GT? "> ": + se->op == SELECT_GE? ">=": + se->op == SELECT_STRLT? "-lt": + se->op == SELECT_STRLE? "-le": + se->op == SELECT_STRGT? "-gt": + se->op == SELECT_STRGE? "-ge": + /**/ "[oops]", + se->value); + } + log_debug ("--- End selectors ---\n"); +} + + +/* Return true if the record RECORD has been selected. The GETVAL + * function is called with COOKIE and the NAME of a property used in + * the expression. */ +int +recsel_select (recsel_expr_t selector, + const char *(*getval)(void *cookie, const char *propname), + void *cookie) +{ + recsel_expr_t se; + const char *value; + size_t selen, valuelen; + long numvalue; + int result = 1; + + se = selector; + while (se) + { + value = getval? getval (cookie, se->name) : NULL; + if (!value) + value = ""; + + if (!*value) + { + /* Field is empty. */ + result = 0; + } + else /* Field has a value. */ + { + valuelen = strlen (value); + numvalue = strtol (value, NULL, 0); + selen = strlen (se->value); + + switch (se->op) + { + case SELECT_SAME: + if (se->xcase) + result = (valuelen==selen && !memcmp (value,se->value,selen)); + else + result = (valuelen==selen && !memicmp (value,se->value,selen)); + break; + case SELECT_SUB: + if (se->xcase) + result = !!my_memstr (value, valuelen, se->value); + else + result = !!memistr (value, valuelen, se->value); + break; + case SELECT_NONEMPTY: + result = !!valuelen; + break; + case SELECT_ISTRUE: + result = !!numvalue; + break; + case SELECT_EQ: + result = (numvalue == se->numvalue); + break; + case SELECT_GT: + result = (numvalue > se->numvalue); + break; + case SELECT_GE: + result = (numvalue >= se->numvalue); + break; + case SELECT_LT: + result = (numvalue < se->numvalue); + break; + case SELECT_LE: + result = (numvalue <= se->numvalue); + break; + case SELECT_STRGT: + if (se->xcase) + result = strcmp (value, se->value) > 0; + else + result = strcasecmp (value, se->value) > 0; + break; + case SELECT_STRGE: + if (se->xcase) + result = strcmp (value, se->value) >= 0; + else + result = strcasecmp (value, se->value) >= 0; + break; + case SELECT_STRLT: + if (se->xcase) + result = strcmp (value, se->value) < 0; + else + result = strcasecmp (value, se->value) < 0; + break; + case SELECT_STRLE: + if (se->xcase) + result = strcmp (value, se->value) <= 0; + else + result = strcasecmp (value, se->value) <= 0; + break; + } + } + + if (se->not) + result = !result; + + if (result) + { + /* This expression evaluated to true. See wether there are + remaining expressions in this conjunction. */ + if (!se->next || se->next->disjun) + break; /* All expressions are true. Return True. */ + se = se->next; /* Test the next. */ + } + else + { + /* This expression evaluated to false and thus the + * conjunction evaluates to false. We skip over the + * remaining expressions of this conjunction and continue + * with the next disjunction if any. */ + do + se = se->next; + while (se && !se->disjun); + } + } + + return result; +} diff --git a/common/recsel.h b/common/recsel.h new file mode 100644 index 0000000..0e0a792 --- /dev/null +++ b/common/recsel.h @@ -0,0 +1,43 @@ +/* recsel.c - Record selection + * Copyright (C) 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ +#ifndef GNUPG_COMMON_RECSEL_H +#define GNUPG_COMMON_RECSEL_H + +struct recsel_expr_s; +typedef struct recsel_expr_s *recsel_expr_t; + +gpg_error_t recsel_parse_expr (recsel_expr_t *selector, const char *expr); +void recsel_release (recsel_expr_t a); +void recsel_dump (recsel_expr_t selector); +int recsel_select (recsel_expr_t selector, + const char *(*getval)(void *cookie, const char *propname), + void *cookie); + + +#endif /*GNUPG_COMMON_RECSEL_H*/ diff --git a/common/server-help.c b/common/server-help.c new file mode 100644 index 0000000..53a888a --- /dev/null +++ b/common/server-help.c @@ -0,0 +1,137 @@ +/* server-help.h - Helper functions for writing Assuan servers. + * Copyright (C) 2003, 2009, 2010 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include + +#include "server-help.h" +#include "util.h" + +/* Skip over options in LINE. + + Blanks after the options are also removed. Options are indicated + by two leading dashes followed by a string consisting of non-space + characters. The special option "--" indicates an explicit end of + options; all what follows will not be considered an option. The + first no-option string also indicates the end of option parsing. */ +char * +skip_options (const char *line) +{ + while (spacep (line)) + line++; + while (*line == '-' && line[1] == '-') + { + while (*line && !spacep (line)) + line++; + while (spacep (line)) + line++; + } + return (char*) line; +} + + +/* Check whether the option NAME appears in LINE. */ +int +has_option (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + if (s && s >= skip_options (line)) + return 0; + return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); +} + + +/* Same as has_option but only considers options at the begin of the + line. This is useful for commands which allow arbitrary strings on + the line. */ +int +has_leading_option (const char *line, const char *name) +{ + const char *s; + int n; + + if (name[0] != '-' || name[1] != '-' || !name[2] || spacep (name+2)) + return 0; + n = strlen (name); + while ( *line == '-' && line[1] == '-' ) + { + s = line; + while (*line && !spacep (line)) + line++; + if (n == (line - s) && !strncmp (s, name, n)) + return 1; + while (spacep (line)) + line++; + } + return 0; +} + + +/* Same as has_option but does only test for the name of the option + and ignores an argument, i.e. with NAME being "--hash" it would + return a pointer for "--hash" as well as for "--hash=foo". If + there is no such option NULL is returned. The pointer returned + points right behind the option name, this may be an equal sign, Nul + or a space. */ +const char * +has_option_name (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + return (s && (s == line || spacep (s-1)) + && (!s[n] || spacep (s+n) || s[n] == '=')) ? (s+n) : NULL; +} + + +/* Return a pointer to the argument of the option with NAME. If such + an option is not given, NULL is returned. */ +char * +option_value (const char *line, const char *name) +{ + char *s; + int n = strlen (name); + + s = strstr (line, name); + if (s && s >= skip_options (line)) + return NULL; + if (s && (s == line || spacep (s-1)) + && s[n] && (spacep (s+n) || s[n] == '=')) + { + s += n + 1; + s += strspn (s, " "); + if (*s && !spacep(s)) + return s; + } + return NULL; +} diff --git a/common/server-help.h b/common/server-help.h new file mode 100644 index 0000000..9e3d7ad --- /dev/null +++ b/common/server-help.h @@ -0,0 +1,62 @@ +/* server-help.h - Helper functions for writing Assuan servers. + * Copyright (C) 2003, 2009, 2010 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_SERVER_HELP_H +#define GNUPG_COMMON_SERVER_HELP_H + +/* Skip over options in LINE. + + Blanks after the options are also removed. Options are indicated + by two leading dashes followed by a string consisting of non-space + characters. The special option "--" indicates an explicit end of + options; all what follows will not be considered an option. The + first no-option string also indicates the end of option parsing. */ +char *skip_options (const char *line); + +/* Check whether the option NAME appears in LINE. */ +int has_option (const char *line, const char *name); + +/* Same as has_option but only considers options at the begin of the + line. This is useful for commands which allow arbitrary strings on + the line. */ +int has_leading_option (const char *line, const char *name); + +/* Same as has_option but does only test for the name of the option + and ignores an argument, i.e. with NAME being "--hash" it would + return a pointer for "--hash" as well as for "--hash=foo". If + there is no such option NULL is returned. The pointer returned + points right behind the option name, this may be an equal sign, Nul + or a space. */ +const char *has_option_name (const char *line, const char *name); + +/* Return a pointer to the argument of the option with NAME. If such + an option is not given, NULL is returned. */ +char *option_value (const char *line, const char *name); + +#endif /* GNUPG_COMMON_SERVER_HELP_H */ diff --git a/common/session-env.c b/common/session-env.c new file mode 100644 index 0000000..1bc3a2b --- /dev/null +++ b/common/session-env.c @@ -0,0 +1,400 @@ +/* session-env.c - Session environment helper functions. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "session-env.h" + + +struct variable_s +{ + char *value; /* Pointer into NAME to the Nul terminated value. */ + int is_default; /* The value is a default one. */ + char name[1]; /* Nul terminated Name and space for the value. */ +}; + + + +/* The session environment object. */ +struct session_environment_s +{ + size_t arraysize; /* Allocated size or ARRAY. */ + size_t arrayused; /* Used size of ARRAY. */ + struct variable_s **array; /* Array of variables. NULL slots are unused. */ +}; + + +/* A list of environment variables we pass from the actual user + (e.g. gpgme) down to the pinentry. We do not handle the locale + settings because they do not only depend on envvars. */ +static struct +{ + const char *name; + const char *assname; /* Name used by Assuan or NULL. */ +} stdenvnames[] = { + { "GPG_TTY", "ttyname" }, /* GnuPG specific envvar. */ + { "TERM", "ttytype" }, /* Used to set ttytype. */ + { "DISPLAY", "display" }, /* The X-Display. */ + { "XAUTHORITY","xauthority"}, /* Xlib Authentication. */ + { "XMODIFIERS" }, /* Used by Xlib to select X input + modules (eg "@im=SCIM"). */ + { "GTK_IM_MODULE" }, /* Used by gtk to select gtk input + modules (eg "scim-bridge"). */ + { "DBUS_SESSION_BUS_ADDRESS" },/* Used by GNOME3 to talk to gcr over + dbus */ + { "QT_IM_MODULE" }, /* Used by Qt to select qt input + modules (eg "xim"). */ + { "INSIDE_EMACS" }, /* Set by Emacs before running a + process. */ + { "PINENTRY_USER_DATA", "pinentry-user-data"} + /* Used for communication with + non-standard Pinentries. */ +}; + + +/* Track last allocated arraysize of all objects ever created. If + nothing has ever been allocated we use INITIAL_ARRAYSIZE and we + will never use more than MAXDEFAULT_ARRAYSIZE for initial + allocation. Note that this is not reentrant if used with a + preemptive thread model. */ +static size_t lastallocatedarraysize; +#define INITIAL_ARRAYSIZE 8 /* Let's use the number of stdenvnames. */ +#define CHUNK_ARRAYSIZE 10 +#define MAXDEFAULT_ARRAYSIZE (INITIAL_ARRAYSIZE + CHUNK_ARRAYSIZE * 5) + + +/* Return the names of standard environment variables one after the + other. The caller needs to set the value at the address of + ITERATOR initially to 0 and then call this function until it returns + NULL. */ +const char * +session_env_list_stdenvnames (int *iterator, const char **r_assname) +{ + int idx = *iterator; + + if (idx < 0 || idx >= DIM (stdenvnames)) + return NULL; + *iterator = idx + 1; + if (r_assname) + *r_assname = stdenvnames[idx].assname; + return stdenvnames[idx].name; +} + + +/* Create a new session environment object. Return NULL and sets + ERRNO on failure. */ +session_env_t +session_env_new (void) +{ + session_env_t se; + + se = xtrycalloc (1, sizeof *se); + if (se) + { + se->arraysize = (lastallocatedarraysize? + lastallocatedarraysize : INITIAL_ARRAYSIZE); + se->array = xtrycalloc (se->arraysize, sizeof *se->array); + if (!se->array) + { + xfree (se); + se = NULL; + } + } + + return se; +} + + +/* Release a session environment object. */ +void +session_env_release (session_env_t se) +{ + int idx; + + if (!se) + return; + + if (se->arraysize > INITIAL_ARRAYSIZE + && se->arraysize <= MAXDEFAULT_ARRAYSIZE + && se->arraysize > lastallocatedarraysize) + lastallocatedarraysize = se->arraysize; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx]) + xfree (se->array[idx]); + xfree (se->array); + xfree (se); +} + + +static gpg_error_t +delete_var (session_env_t se, const char *name) +{ + int idx; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + { + xfree (se->array[idx]); + se->array[idx] = NULL; + } + return 0; +} + + +static gpg_error_t +update_var (session_env_t se, const char *string, size_t namelen, + const char *explicit_value, int set_default) +{ + int idx; + int freeidx = -1; + const char *value; + size_t valuelen; + struct variable_s *var; + + if (explicit_value) + value = explicit_value; + else + value = string + namelen + 1; + valuelen = strlen (value); + + for (idx=0; idx < se->arrayused; idx++) + { + if (!se->array[idx]) + freeidx = idx; + else if (!strncmp (se->array[idx]->name, string, namelen) + && strlen (se->array[idx]->name) == namelen) + { + if (strlen (se->array[idx]->value) == valuelen) + { + /* The new value has the same length. We can update it + in-place. */ + memcpy (se->array[idx]->value, value, valuelen); + se->array[idx]->is_default = !!set_default; + return 0; + } + /* Prepare for update. */ + freeidx = idx; + } + } + + if (freeidx == -1) + { + if (se->arrayused == se->arraysize) + { + /* Reallocate the array. */ + size_t newsize; + struct variable_s **newarray; + + newsize = se->arraysize + CHUNK_ARRAYSIZE; + newarray = xtrycalloc (newsize, sizeof *newarray); + if (!newarray) + return gpg_error_from_syserror (); + for (idx=0; idx < se->arrayused; idx++) + newarray[idx] = se->array[idx]; + se->arraysize = newsize; + xfree (se->array); + se->array = newarray; + } + freeidx = se->arrayused++; + } + + /* Allocate new memory and return an error if that didn't worked. + Allocating it first allows us to keep the old value; it doesn't + matter that arrayused has already been incremented in case of a + new entry - it will then pint to a NULL slot. */ + var = xtrymalloc (sizeof *var + namelen + 1 + valuelen); + if (!var) + return gpg_error_from_syserror (); + var->is_default = !!set_default; + memcpy (var->name, string, namelen); + var->name[namelen] = '\0'; + var->value = var->name + namelen + 1; + strcpy (var->value, value); + + xfree (se->array[freeidx]); + se->array[freeidx] = var; + return 0; +} + + +/* Set or update an environment variable of the session environment. + String is similar to the putval(3) function but it is reentrant and + takes a copy. In particular it exhibits this behaviour: + + Delete envvar NAME + = Set envvar NAME to the empty string + = Set envvar NAME to VALUE + + On success 0 is returned; on error an gpg-error code. */ +gpg_error_t +session_env_putenv (session_env_t se, const char *string) +{ + const char *s; + + if (!string || !*string) + return gpg_error (GPG_ERR_INV_VALUE); + s = strchr (string, '='); + if (s == string) + return gpg_error (GPG_ERR_INV_VALUE); + if (!s) + return delete_var (se, string); + else + return update_var (se, string, s - string, NULL, 0); +} + + +/* Same as session_env_putenv but with name and value given as distict + values. */ +gpg_error_t +session_env_setenv (session_env_t se, const char *name, const char *value) +{ + if (!name || !*name) + return gpg_error (GPG_ERR_INV_VALUE); + if (!value) + return delete_var (se, name); + else + return update_var (se, name, strlen (name), value, 0); +} + + + + +/* Return the value of the environment variable NAME from the SE + object. If the variable does not exist, NULL is returned. The + returned value is valid as long as SE is valid and as long it has + not been removed or updated by a call to session_env_putenv. The + caller MUST not change the returned value. */ +char * +session_env_getenv (session_env_t se, const char *name) +{ + int idx; + + if (!se || !name || !*name) + return NULL; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + return se->array[idx]->is_default? NULL : se->array[idx]->value; + return NULL; +} + + +/* Return the value of the environment variable NAME from the SE + object. The returned value is valid as long as SE is valid and as + long it has not been removed or updated by a call to + session_env_putenv. If the variable does not exist, the function + tries to return the value trough a call to getenv; if that returns + a value, this value is recorded and and used. If no value could be + found, returns NULL. The caller must not change the returned + value. */ +char * +session_env_getenv_or_default (session_env_t se, const char *name, + int *r_default) +{ + int idx; + char *defvalue; + + if (r_default) + *r_default = 0; + if (!se || !name || !*name) + return NULL; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + { + if (r_default && se->array[idx]->is_default) + *r_default = 1; + return se->array[idx]->value; + } + + /* Get the default value with an additional fallback for GPG_TTY. */ + defvalue = getenv (name); + if ((!defvalue || !*defvalue) && !strcmp (name, "GPG_TTY") + && gnupg_ttyname (0)) + { + defvalue = gnupg_ttyname (0); + } + if (defvalue) + { + /* Record the default value for later use so that we are safe + from later modifications of the environment. We need to take + a copy to better cope with the rules of putenv(3). We ignore + the error of the update function because we can't return an + explicit error anyway and the following scan would then fail + anyway. */ + update_var (se, name, strlen (name), defvalue, 1); + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + { + if (r_default && se->array[idx]->is_default) + *r_default = 1; + return se->array[idx]->value; + } + } + + return NULL; +} + + +/* List the entire environment stored in SE. The caller initially + needs to set the value of ITERATOR to 0 and then call this function + until it returns NULL. The value is returned at R_VALUE. If + R_DEFAULT is not NULL, the default flag is stored on return. The + default flag indicates that the value has been taken from the + process' environment. The caller must not change the returned + name or value. */ +char * +session_env_listenv (session_env_t se, int *iterator, + const char **r_value, int *r_default) +{ + int idx = *iterator; + + if (!se || idx < 0) + return NULL; + + for (; idx < se->arrayused; idx++) + if (se->array[idx]) + { + *iterator = idx+1; + if (r_default) + *r_default = se->array[idx]->is_default; + if (r_value) + *r_value = se->array[idx]->value; + return se->array[idx]->name; + } + return NULL; +} diff --git a/common/session-env.h b/common/session-env.h new file mode 100644 index 0000000..8709e22 --- /dev/null +++ b/common/session-env.h @@ -0,0 +1,53 @@ +/* session-env.h - Definitions for session environment functions + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_SESSION_ENV_H +#define GNUPG_COMMON_SESSION_ENV_H + +struct session_environment_s; +typedef struct session_environment_s *session_env_t; + +const char *session_env_list_stdenvnames (int *iterator, + const char **r_assname); + +session_env_t session_env_new (void); +void session_env_release (session_env_t se); + +gpg_error_t session_env_putenv (session_env_t se, const char *string); +gpg_error_t session_env_setenv (session_env_t se, + const char *name, const char *value); + +char *session_env_getenv (session_env_t se, const char *name); +char *session_env_getenv_or_default (session_env_t se, const char *name, + int *r_default); +char *session_env_listenv (session_env_t se, int *iterator, + const char **r_value, int *r_default); + + +#endif /*GNUPG_COMMON_SESSION_ENV_H*/ diff --git a/common/sexp-parse.h b/common/sexp-parse.h new file mode 100644 index 0000000..9b14f77 --- /dev/null +++ b/common/sexp-parse.h @@ -0,0 +1,137 @@ +/* sexp-parse.h - S-expression helper functions + * Copyright (C) 2002, 2003, 2007 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef SEXP_PARSE_H +#define SEXP_PARSE_H + +#include + + +/* Return the length of the next S-Exp part and update the pointer to + the first data byte. 0 is returned on error */ +static inline size_t +snext (unsigned char const **buf) +{ + const unsigned char *s; + int n; + + s = *buf; + for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++) + n = n*10 + (*s - '0'); + if (!n || *s != ':') + return 0; /* we don't allow empty lengths */ + *buf = s+1; + return n; +} + +/* Skip over the S-Expression BUF points to and update BUF to point to + the character right behind. DEPTH gives the initial number of open + lists and may be passed as a positive number to skip over the + remainder of an S-Expression if the current position is somewhere + in an S-Expression. The function may return an error code if it + encounters an impossible condition. */ +static inline gpg_error_t +sskip (unsigned char const **buf, int *depth) +{ + const unsigned char *s = *buf; + size_t n; + int d = *depth; + + while (d > 0) + { + if (*s == '(') + { + d++; + s++; + } + else if (*s == ')') + { + d--; + s++; + } + else + { + if (!d) + return gpg_error (GPG_ERR_INV_SEXP); + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + } + } + *buf = s; + *depth = d; + return 0; +} + + +/* Check whether the the string at the address BUF points to matches + the token. Return true on match and update BUF to point behind the + token. Return false and do not update the buffer if it does not + match. */ +static inline int +smatch (unsigned char const **buf, size_t buflen, const char *token) +{ + size_t toklen = strlen (token); + + if (buflen != toklen || memcmp (*buf, token, toklen)) + return 0; + *buf += toklen; + return 1; +} + +/* Format VALUE for use as the length indicatior of an S-expression. + The caller needs to provide a buffer HELP_BUFFER wth a length of + HELP_BUFLEN. The return value is a pointer into HELP_BUFFER with + the formatted length string. The colon and a trailing nul are + appended. HELP_BUFLEN must be at least 3 - a more useful value is + 15. If LENGTH is not NULL, the LENGTH of the resulting string + (excluding the terminating nul) is stored at that address. */ +static inline char * +smklen (char *help_buffer, size_t help_buflen, size_t value, size_t *length) +{ + char *p = help_buffer + help_buflen; + + if (help_buflen >= 3) + { + *--p = 0; + *--p = ':'; + do + { + *--p = '0' + (value % 10); + value /= 10; + } + while (value && p > help_buffer); + } + + if (length) + *length = (help_buffer + help_buflen) - p; + return p; +} + + +#endif /*SEXP_PARSE_H*/ diff --git a/common/sexputil.c b/common/sexputil.c new file mode 100644 index 0000000..0c5c730 --- /dev/null +++ b/common/sexputil.c @@ -0,0 +1,608 @@ +/* sexputil.c - Utility functions for S-expressions. + * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +/* This file implements a few utility functions useful when working + with canonical encrypted S-expresions (i.e. not the S-exprssion + objects from libgcrypt). */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "util.h" +#include "tlv.h" +#include "sexp-parse.h" +#include "openpgpdefs.h" /* for pubkey_algo_t */ + + +/* Return a malloced string with the S-expression CANON in advanced + format. Returns NULL on error. */ +static char * +sexp_to_string (gcry_sexp_t sexp) +{ + size_t n; + char *result; + + if (!sexp) + return NULL; + n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + if (!n) + return NULL; + result = xtrymalloc (n); + if (!result) + return NULL; + n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, n); + if (!n) + BUG (); + + return result; +} + + +/* Return a malloced string with the S-expression CANON in advanced + format. Returns NULL on error. */ +char * +canon_sexp_to_string (const unsigned char *canon, size_t canonlen) +{ + size_t n; + gcry_sexp_t sexp; + char *result; + + n = gcry_sexp_canon_len (canon, canonlen, NULL, NULL); + if (!n) + return NULL; + if (gcry_sexp_sscan (&sexp, NULL, canon, n)) + return NULL; + result = sexp_to_string (sexp); + gcry_sexp_release (sexp); + return result; +} + + +/* Print the canonical encoded S-expression in SEXP in advanced + format. SEXPLEN may be passed as 0 is SEXP is known to be valid. + With TEXT of NULL print just the raw S-expression, with TEXT just + an empty string, print a trailing linefeed, otherwise print an + entire debug line. */ +void +log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen) +{ + if (text && *text) + log_debug ("%s ", text); + if (sexp) + { + char *buf = canon_sexp_to_string (sexp, sexplen); + log_printf ("%s", buf? buf : "[invalid S-expression]"); + xfree (buf); + } + if (text) + log_printf ("\n"); +} + + +/* Print the gcryp S-expression in SEXP in advanced format. With TEXT + of NULL print just the raw S-expression, with TEXT just an empty + string, print a trailing linefeed, otherwise print an entire debug + line. */ +void +log_printsexp (const char *text, gcry_sexp_t sexp) +{ + if (text && *text) + log_debug ("%s ", text); + if (sexp) + { + char *buf = sexp_to_string (sexp); + log_printf ("%s", buf? buf : "[invalid S-expression]"); + xfree (buf); + } + if (text) + log_printf ("\n"); +} + + +/* Helper function to create a canonical encoded S-expression from a + Libgcrypt S-expression object. The function returns 0 on success + and the malloced canonical S-expression is stored at R_BUFFER and + the allocated length at R_BUFLEN. On error an error code is + returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN. If the + allocated buffer length is not required, NULL by be used for + R_BUFLEN. */ +gpg_error_t +make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen) +{ + size_t len; + unsigned char *buf; + + *r_buffer = NULL; + if (r_buflen) + *r_buflen = 0;; + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!len) + return gpg_error (GPG_ERR_BUG); + buf = xtrymalloc (len); + if (!buf) + return gpg_error_from_syserror (); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len); + if (!len) + return gpg_error (GPG_ERR_BUG); + + *r_buffer = buf; + if (r_buflen) + *r_buflen = len; + + return 0; +} + + +/* Same as make_canon_sexp but pad the buffer to multiple of 64 + bits. If SECURE is set, secure memory will be allocated. */ +gpg_error_t +make_canon_sexp_pad (gcry_sexp_t sexp, int secure, + unsigned char **r_buffer, size_t *r_buflen) +{ + size_t len; + unsigned char *buf; + + *r_buffer = NULL; + if (r_buflen) + *r_buflen = 0;; + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!len) + return gpg_error (GPG_ERR_BUG); + len += (8 - len % 8) % 8; + buf = secure? xtrycalloc_secure (1, len) : xtrycalloc (1, len); + if (!buf) + return gpg_error_from_syserror (); + if (!gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len)) + return gpg_error (GPG_ERR_BUG); + + *r_buffer = buf; + if (r_buflen) + *r_buflen = len; + + return 0; +} + +/* Return the so called "keygrip" which is the SHA-1 hash of the + public key parameters expressed in a way depended on the algorithm. + + KEY is expected to be an canonical encoded S-expression with a + public or private key. KEYLEN is the length of that buffer. + + GRIP must be at least 20 bytes long. On success 0 is returned, on + error an error code. */ +gpg_error_t +keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, + unsigned char *grip) +{ + gpg_error_t err; + gcry_sexp_t sexp; + + if (!grip) + return gpg_error (GPG_ERR_INV_VALUE); + err = gcry_sexp_sscan (&sexp, NULL, (const char *)key, keylen); + if (err) + return err; + if (!gcry_pk_get_keygrip (sexp, grip)) + err = gpg_error (GPG_ERR_INTERNAL); + gcry_sexp_release (sexp); + return err; +} + + +/* Compare two simple S-expressions like "(3:foo)". Returns 0 if they + are identical or !0 if they are not. Note that this function can't + be used for sorting. */ +int +cmp_simple_canon_sexp (const unsigned char *a_orig, + const unsigned char *b_orig) +{ + const char *a = (const char *)a_orig; + const char *b = (const char *)b_orig; + unsigned long n1, n2; + char *endp; + + if (!a && !b) + return 0; /* Both are NULL, they are identical. */ + if (!a || !b) + return 1; /* One is NULL, they are not identical. */ + if (*a != '(' || *b != '(') + log_bug ("invalid S-exp in cmp_simple_canon_sexp\n"); + + a++; + n1 = strtoul (a, &endp, 10); + a = endp; + b++; + n2 = strtoul (b, &endp, 10); + b = endp; + + if (*a != ':' || *b != ':' ) + log_bug ("invalid S-exp in cmp_simple_canon_sexp\n"); + if (n1 != n2) + return 1; /* Not the same. */ + + for (a++, b++; n1; n1--, a++, b++) + if (*a != *b) + return 1; /* Not the same. */ + return 0; +} + + +/* Create a simple S-expression from the hex string at LINE. Returns + a newly allocated buffer with that canonical encoded S-expression + or NULL in case of an error. On return the number of characters + scanned in LINE will be stored at NSCANNED. This fucntions stops + converting at the first character not representing a hexdigit. Odd + numbers of hex digits are allowed; a leading zero is then + assumed. If no characters have been found, NULL is returned.*/ +unsigned char * +make_simple_sexp_from_hexstr (const char *line, size_t *nscanned) +{ + size_t n, len; + const char *s; + unsigned char *buf; + unsigned char *p; + char numbuf[50], *numbufp; + size_t numbuflen; + + for (n=0, s=line; hexdigitp (s); s++, n++) + ; + if (nscanned) + *nscanned = n; + if (!n) + return NULL; + len = ((n+1) & ~0x01)/2; + numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen); + buf = xtrymalloc (1 + numbuflen + len + 1 + 1); + if (!buf) + return NULL; + buf[0] = '('; + p = (unsigned char *)stpcpy ((char *)buf+1, numbufp); + s = line; + if ((n&1)) + { + *p++ = xtoi_1 (s); + s++; + n--; + } + for (; n > 1; n -=2, s += 2) + *p++ = xtoi_2 (s); + *p++ = ')'; + *p = 0; /* (Not really neaded.) */ + + return buf; +} + + +/* Return the hash algorithm from a KSBA sig-val. SIGVAL is a + canonical encoded S-expression. Return 0 if the hash algorithm is + not encoded in SIG-VAL or it is not supported by libgcrypt. */ +int +hash_algo_from_sigval (const unsigned char *sigval) +{ + const unsigned char *s = sigval; + size_t n; + int depth; + char buffer[50]; + + if (!s || *s != '(') + return 0; /* Invalid S-expression. */ + s++; + n = snext (&s); + if (!n) + return 0; /* Invalid S-expression. */ + if (!smatch (&s, n, "sig-val")) + return 0; /* Not a sig-val. */ + if (*s != '(') + return 0; /* Invalid S-expression. */ + s++; + /* Skip over the algo+parameter list. */ + depth = 1; + if (sskip (&s, &depth) || depth) + return 0; /* Invalid S-expression. */ + if (*s != '(') + return 0; /* No further list. */ + /* Check whether this is (hash ALGO). */ + s++; + n = snext (&s); + if (!n) + return 0; /* Invalid S-expression. */ + if (!smatch (&s, n, "hash")) + return 0; /* Not a "hash" keyword. */ + n = snext (&s); + if (!n || n+1 >= sizeof (buffer)) + return 0; /* Algorithm string is missing or too long. */ + memcpy (buffer, s, n); + buffer[n] = 0; + + return gcry_md_map_name (buffer); +} + + +/* Create a public key S-expression for an RSA public key from the + modulus M with length MLEN and the public exponent E with length + ELEN. Returns a newly allocated buffer of NULL in case of a memory + allocation problem. If R_LEN is not NULL, the length of the + canonical S-expression is stored there. */ +unsigned char * +make_canon_sexp_from_rsa_pk (const void *m_arg, size_t mlen, + const void *e_arg, size_t elen, + size_t *r_len) +{ + const unsigned char *m = m_arg; + const unsigned char *e = e_arg; + int m_extra = 0; + int e_extra = 0; + char mlen_str[35]; + char elen_str[35]; + unsigned char *keybuf, *p; + const char part1[] = "(10:public-key(3:rsa(1:n"; + const char part2[] = ")(1:e"; + const char part3[] = ")))"; + + /* Remove leading zeroes. */ + for (; mlen && !*m; mlen--, m++) + ; + for (; elen && !*e; elen--, e++) + ; + + /* Insert a leading zero if the number would be zero or interpreted + as negative. */ + if (!mlen || (m[0] & 0x80)) + m_extra = 1; + if (!elen || (e[0] & 0x80)) + e_extra = 1; + + /* Build the S-expression. */ + snprintf (mlen_str, sizeof mlen_str, "%u:", (unsigned int)mlen+m_extra); + snprintf (elen_str, sizeof elen_str, "%u:", (unsigned int)elen+e_extra); + + keybuf = xtrymalloc (strlen (part1) + strlen (mlen_str) + mlen + m_extra + + strlen (part2) + strlen (elen_str) + elen + e_extra + + strlen (part3) + 1); + if (!keybuf) + return NULL; + + p = stpcpy (keybuf, part1); + p = stpcpy (p, mlen_str); + if (m_extra) + *p++ = 0; + memcpy (p, m, mlen); + p += mlen; + p = stpcpy (p, part2); + p = stpcpy (p, elen_str); + if (e_extra) + *p++ = 0; + memcpy (p, e, elen); + p += elen; + p = stpcpy (p, part3); + + if (r_len) + *r_len = p - keybuf; + + return keybuf; +} + + +/* Return the parameters of a public RSA key expressed as an + canonical encoded S-expression. */ +gpg_error_t +get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, + unsigned char const **r_n, size_t *r_nlen, + unsigned char const **r_e, size_t *r_elen) +{ + gpg_error_t err; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth, last_depth1, last_depth2; + const unsigned char *rsa_n = NULL; + const unsigned char *rsa_e = NULL; + size_t rsa_n_len, rsa_e_len; + + *r_n = NULL; + *r_nlen = 0; + *r_e = NULL; + *r_elen = 0; + + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && toklen == 1) + { + const unsigned char **mpi; + size_t *mpi_len; + + switch (*tok) + { + case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; + case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; + default: mpi = NULL; mpi_len = NULL; break; + } + if (mpi && *mpi) + return gpg_error (GPG_ERR_DUP_VALUE); + + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && mpi) + { + /* Strip off leading zero bytes and save. */ + for (;toklen && !*tok; toklen--, tok++) + ; + *mpi = tok; + *mpi_len = toklen; + } + } + + /* Skip to the end of the list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + return err; + } + + if (err) + return err; + + if (!rsa_n || !rsa_n_len || !rsa_e || !rsa_e_len) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + *r_n = rsa_n; + *r_nlen = rsa_n_len; + *r_e = rsa_e; + *r_elen = rsa_e_len; + return 0; +} + + +/* Return the algo of a public RSA expressed as an canonical encoded + S-expression. The return value is a statically allocated + string. On error that string is set to NULL. */ +gpg_error_t +get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, + const char **r_algo) +{ + gpg_error_t err; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth; + + *r_algo = NULL; + + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + if (toklen == 3 && !memcmp ("rsa", tok, toklen)) + *r_algo = "rsa"; + else if (toklen == 3 && !memcmp ("dsa", tok, toklen)) + *r_algo = "dsa"; + else if (toklen == 3 && !memcmp ("elg", tok, toklen)) + *r_algo = "elg"; + else if (toklen == 5 && !memcmp ("ecdsa", tok, toklen)) + *r_algo = "ecdsa"; + else if (toklen == 5 && !memcmp ("eddsa", tok, toklen)) + *r_algo = "eddsa"; + else + return gpg_error (GPG_ERR_PUBKEY_ALGO); + + return 0; +} + + +/* Return the algo of a public KEY of SEXP. */ +int +get_pk_algo_from_key (gcry_sexp_t key) +{ + gcry_sexp_t list; + const char *s; + size_t n; + char algoname[6]; + int algo = 0; + + list = gcry_sexp_nth (key, 1); + if (!list) + goto out; + s = gcry_sexp_nth_data (list, 0, &n); + if (!s) + goto out; + if (n >= sizeof (algoname)) + goto out; + memcpy (algoname, s, n); + algoname[n] = 0; + + algo = gcry_pk_map_name (algoname); + if (algo == GCRY_PK_ECC) + { + gcry_sexp_t l1 = gcry_sexp_find_token (list, "flags", 0); + int i; + + for (i = l1 ? gcry_sexp_length (l1)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (l1, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + algo = GCRY_PK_EDDSA; + break; + } + } + gcry_sexp_release (l1); + } + + out: + gcry_sexp_release (list); + + return algo; +} diff --git a/common/shareddefs.h b/common/shareddefs.h new file mode 100644 index 0000000..1594f66 --- /dev/null +++ b/common/shareddefs.h @@ -0,0 +1,48 @@ +/* shareddefs.h - Constants and helpers useful for all modules + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_SHAREDDEFS_H +#define GNUPG_COMMON_SHAREDDEFS_H + +/* Values for the pinentry mode. */ +typedef enum + { + PINENTRY_MODE_ASK = 0, /* Ask via pinentry (default). */ + PINENTRY_MODE_CANCEL, /* Always return a cancel error. */ + PINENTRY_MODE_ERROR, /* Return error code for no pinentry. */ + PINENTRY_MODE_LOOPBACK /* Use an inquiry to get the value. */ + } +pinentry_mode_t; + + +/*-- agent-opt.c --*/ +int parse_pinentry_mode (const char *value); +const char *str_pinentry_mode (pinentry_mode_t mode); + + + +#endif /*GNUPG_COMMON_SHAREDDEFS_H*/ diff --git a/common/signal.c b/common/signal.c new file mode 100644 index 0000000..ccfa8e6 --- /dev/null +++ b/common/signal.c @@ -0,0 +1,249 @@ +/* signal.c - signal handling + * Copyright (C) 1998, 1999, 2000, 2001, 2002, + * 2005 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include +#include +#include +#include + +#include "util.h" + + +#ifndef HAVE_DOSISH_SYSTEM +static volatile int caught_fatal_sig; +static volatile int caught_sigusr1; +#endif +static void (*cleanup_fnc)(void); + + +#ifndef HAVE_DOSISH_SYSTEM +static void +init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) +{ +# ifdef HAVE_SIGACTION + struct sigaction oact, nact; + + if (check_ign) + { + /* we don't want to change an IGN handler */ + sigaction (sig, NULL, &oact ); + if (oact.sa_handler == SIG_IGN ) + return; + } + + nact.sa_handler = handler; + sigemptyset (&nact.sa_mask); + nact.sa_flags = 0; + sigaction ( sig, &nact, NULL); +# else + RETSIGTYPE (*ohandler)(int); + + ohandler = signal (sig, handler); + if (check_ign && ohandler == SIG_IGN) + { + /* Change it back if it was already set to IGN */ + signal (sig, SIG_IGN); + } +# endif +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +#ifndef HAVE_DOSISH_SYSTEM +static const char * +get_signal_name( int signum ) +{ + /* Note that we can't use strsignal(), because it is not + reentrant. */ +#if HAVE_DECL_SYS_SIGLIST && defined(NSIG) + return (signum >= 0 && signum < NSIG) ? sys_siglist[signum] : "?"; +#else + return NULL; +#endif +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +#ifndef HAVE_DOSISH_SYSTEM +static RETSIGTYPE +got_fatal_signal (int sig) +{ + const char *s; + + if (caught_fatal_sig) + raise (sig); + caught_fatal_sig = 1; + + if (cleanup_fnc) + cleanup_fnc (); + /* Better don't translate these messages. */ + (void)write (2, "\n", 1 ); + s = log_get_prefix (NULL); + if (s) + (void)write(2, s, strlen (s)); + (void)write (2, ": signal ", 9 ); + s = get_signal_name(sig); + if (s) + (void) write (2, s, strlen(s) ); + else + { + /* We are in a signal handler so we can't use any kind of printf + even not sprintf. So we use a straightforward algorithm. We + got a report that on one particular system, raising a signal + while in this handler, the parameter SIG get sclobbered and + things are messed up because we modify its value. Although + this is a bug in that system, we will protect against it. */ + if (sig < 0 || sig >= 100000) + (void)write (2, "?", 1); + else + { + int i, value, any=0; + + for (value=sig,i=10000; i; i /= 10) + { + if (value >= i || ((any || i==1) && !(value/i))) + { + (void)write (2, &"0123456789"[value/i], 1); + if ((value/i)) + any = 1; + value %= i; + } + } + } + } + (void)write (2, " caught ... exiting\n", 20); + + /* Reset action to default action and raise signal again */ + init_one_signal (sig, SIG_DFL, 0); + /* Fixme: remove_lockfiles ();*/ +#ifdef __riscos__ + close_fds (); +#endif /* __riscos__ */ + raise( sig ); +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +#ifndef HAVE_DOSISH_SYSTEM +static RETSIGTYPE +got_usr_signal (int sig) +{ + (void)sig; + caught_sigusr1 = 1; +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +void +gnupg_init_signals (int mode, void (*fast_cleanup)(void)) +{ + assert (!mode); + + cleanup_fnc = fast_cleanup; +#ifndef HAVE_DOSISH_SYSTEM + init_one_signal (SIGINT, got_fatal_signal, 1 ); + init_one_signal (SIGHUP, got_fatal_signal, 1 ); + init_one_signal (SIGTERM, got_fatal_signal, 1 ); + init_one_signal (SIGQUIT, got_fatal_signal, 1 ); + init_one_signal (SIGSEGV, got_fatal_signal, 1 ); + init_one_signal (SIGUSR1, got_usr_signal, 0 ); + init_one_signal (SIGPIPE, SIG_IGN, 0 ); +#endif +} + + +static void +do_block (int block) +{ +#ifdef HAVE_DOSISH_SYSTEM + (void)block; +#else /*!HAVE_DOSISH_SYSTEM*/ + static int is_blocked; +#ifdef HAVE_SIGPROCMASK + static sigset_t oldmask; + + if (block) + { + sigset_t newmask; + + if (is_blocked) + log_bug ("signals are already blocked\n"); + sigfillset( &newmask ); + sigprocmask( SIG_BLOCK, &newmask, &oldmask ); + is_blocked = 1; + } + else + { + if (!is_blocked) + log_bug("signals are not blocked\n"); + sigprocmask (SIG_SETMASK, &oldmask, NULL); + is_blocked = 0; + } +#else /*!HAVE_SIGPROCMASK*/ + static void (*disposition[MAXSIG])(); + int sig; + + if (block) + { + if (is_blocked) + log_bug("signals are already blocked\n"); + for (sig=1; sig < MAXSIG; sig++) + { + disposition[sig] = sigset (sig, SIG_HOLD); + } + is_blocked = 1; + } + else + { + if (!is_blocked) + log_bug ("signals are not blocked\n"); + for (sig=1; sig < MAXSIG; sig++) { + sigset (sig, disposition[sig]); + } + is_blocked = 0; + } +#endif /*!HAVE_SIGPROCMASK*/ +#endif /*!HAVE_DOSISH_SYSTEM*/ +} + + +void +gnupg_block_all_signals () +{ + do_block(1); +} + +void +gnupg_unblock_all_signals () +{ + do_block(0); +} diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c new file mode 100644 index 0000000..c74317f --- /dev/null +++ b/common/simple-pwquery.c @@ -0,0 +1,494 @@ +/* simple-pwquery.c - A simple password query client for gpg-agent + * Copyright (C) 2002, 2004, 2007 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 3 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, see . + */ + +/* This module is intended as a simple client implementation to + gpg-agent's GET_PASSPHRASE command. It can only cope with an + already running gpg-agent. Some stuff is configurable in the + header file. */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +#include +#else +#include +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#endif + +#define GNUPG_COMMON_NEED_AFLOCAL +#include "../common/mischelp.h" +#include "sysutils.h" +#include "membuf.h" + + +#define SIMPLE_PWQUERY_IMPLEMENTATION 1 +#include "simple-pwquery.h" + +#define SPWQ_OUT_OF_CORE gpg_error_from_errno (ENOMEM) +#define SPWQ_IO_ERROR gpg_error_from_errno (EIO) +#define SPWQ_PROTOCOL_ERROR gpg_error (GPG_ERR_PROTOCOL_VIOLATION) +#define SPWQ_ERR_RESPONSE gpg_error (GPG_ERR_INV_RESPONSE) +#define SPWQ_NO_AGENT gpg_error (GPG_ERR_NO_AGENT) +#define SPWQ_SYS_ERROR gpg_error_from_syserror () +#define SPWQ_GENERAL_ERROR gpg_error (GPG_ERR_GENERAL) +#define SPWQ_NO_PIN_ENTRY gpg_error (GPG_ERR_NO_PIN_ENTRY) + +#ifndef _ +#define _(a) (a) +#endif + +#if !defined (hexdigitp) && !defined (xtoi_2) +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#endif + + +/* Name of the socket to be used. This is a kludge to keep on using + the existsing code despite that we only support a standard socket. */ +static char *default_gpg_agent_info; + + + + + +#ifndef HAVE_STPCPY +static char * +my_stpcpy(char *a,const char *b) +{ + while( *b ) + *a++ = *b++; + *a = 0; + + return (char*)a; +} +#define stpcpy(a,b) my_stpcpy((a), (b)) +#endif + + +/* Send an option to the agent */ +static int +agent_send_option (assuan_context_t ctx, const char *name, const char *value) +{ + int err; + char *line; + + line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2); + if (!line) + return SPWQ_OUT_OF_CORE; + strcpy (stpcpy (stpcpy (stpcpy ( + stpcpy (line, "OPTION "), name), "="), value), "\n"); + + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + + spwq_free (line); + return err; +} + + +/* Send all available options to the agent. */ +static int +agent_send_all_options (assuan_context_t ctx) +{ + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + char *dft_xauthority = NULL; + char *dft_pinentry_user_data = NULL; + int rc = 0; + + dft_display = getenv ("DISPLAY"); + if (dft_display) + { + if ((rc = agent_send_option (ctx, "display", dft_display))) + return rc; + } + + dft_ttyname = getenv ("GPG_TTY"); +#if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME) + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); +#endif + if (dft_ttyname && *dft_ttyname) + { + if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname))) + return rc; + } + + dft_ttytype = getenv ("TERM"); + if (dft_ttyname && dft_ttytype) + { + if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype))) + return rc; + } + +#if defined(HAVE_SETLOCALE) + { + char *old_lc = NULL; + char *dft_lc = NULL; + +#if defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_CTYPE, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (ctx, "lc-ctype", dft_lc); + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + +#if defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_MESSAGES, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (ctx, "lc-messages", dft_lc); + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + } +#endif /*HAVE_SETLOCALE*/ + + /* Send the XAUTHORITY variable. */ + dft_xauthority = getenv ("XAUTHORITY"); + if (dft_xauthority) + { + /* We ignore errors here because older gpg-agents don't support + this option. */ + agent_send_option (ctx, "xauthority", dft_xauthority); + } + + /* Send the PINENTRY_USER_DATA variable. */ + dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA"); + if (dft_pinentry_user_data) + { + /* We ignore errors here because older gpg-agents don't support + this option. */ + agent_send_option (ctx, "pinentry-user-data", dft_pinentry_user_data); + } + + /* Tell the agent that we support Pinentry notifications. No + error checking so that it will work with older agents. */ + assuan_transact (ctx, "OPTION allow-pinentry-notify", + NULL, NULL, NULL, NULL, NULL, NULL); + + return 0; +} + + + +/* Try to open a connection to the agent, send all options and return + the file descriptor for the connection. Return -1 in case of + error. */ +static int +agent_open (assuan_context_t *ctx) +{ + int rc; + char *infostr; + + infostr = default_gpg_agent_info; + if ( !infostr || !*infostr ) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("no gpg-agent running in this session\n")); +#endif + return SPWQ_NO_AGENT; + } + + rc = assuan_new (ctx); + if (rc) + return rc; + + rc = assuan_socket_connect (*ctx, infostr, 0, 0); + if (rc) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("can't connect to '%s': %s\n"), + infostr, gpg_strerror (rc)); +#endif + goto errout; + } + + rc = agent_send_all_options (*ctx); + if (rc) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("problem setting the gpg-agent options\n")); +#endif + goto errout; + } + + return 0; + + errout: + assuan_release (*ctx); + *ctx = NULL; + return rc; +} + + +/* Copy text to BUFFER and escape as required. Return a pointer to + the end of the new buffer. Note that BUFFER must be large enough + to keep the entire text; allocataing it 3 times the size of TEXT + is sufficient. */ +static char * +copy_and_escape (char *buffer, const char *text) +{ + int i; + const unsigned char *s = (unsigned char *)text; + char *p = buffer; + + + for (i=0; s[i]; i++) + { + if (s[i] < ' ' || s[i] == '+') + { + sprintf (p, "%%%02X", s[i]); + p += 3; + } + else if (s[i] == ' ') + *p++ = '+'; + else + *p++ = s[i]; + } + return p; +} + + +/* Set the name of the default socket to NAME. */ +int +simple_pw_set_socket (const char *name) +{ + spwq_free (default_gpg_agent_info); + default_gpg_agent_info = NULL; + if (name) + { + default_gpg_agent_info = spwq_malloc (strlen (name) + 1); + if (!default_gpg_agent_info) + return SPWQ_OUT_OF_CORE; + strcpy (default_gpg_agent_info, name); + } + + return 0; +} + + +/* This is the default inquiry callback. It merely handles the + Pinentry notification. */ +static gpg_error_t +default_inq_cb (void *opaque, const char *line) +{ + (void)opaque; + + if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) + { + gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); + /* We do not return errors to avoid breaking other code. */ + } + else + { +#ifdef SPWQ_USE_LOGGING + log_debug ("ignoring gpg-agent inquiry '%s'\n", line); +#endif + } + + return 0; +} + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in in + the cache and store it under this ID. If OPT_CHECK is true + gpg-agent is asked to apply some checks on the passphrase security. + If ERRORCODE is not NULL it should point a variable receiving an + errorcode; this error code might be 0 if the user canceled the + operation. The function returns NULL to indicate an error. */ +char * +simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int opt_check, + int *errorcode) +{ + int rc; + assuan_context_t ctx; + membuf_t data; + char *result = NULL; + char *pw = NULL; + char *p; + size_t n; + + + rc = agent_open (&ctx); + if (rc) + goto leave; + + if (!cacheid) + cacheid = "X"; + if (!tryagain) + tryagain = "X"; + if (!prompt) + prompt = "X"; + if (!description) + description = "X"; + + { + char *line; + /* We allocate 3 times the needed space so that there is enough + space for escaping. */ + line = spwq_malloc (15 + 10 + + 3*strlen (cacheid) + 1 + + 3*strlen (tryagain) + 1 + + 3*strlen (prompt) + 1 + + 3*strlen (description) + 1 + + 2); + if (!line) + { + rc = SPWQ_OUT_OF_CORE; + goto leave; + } + strcpy (line, "GET_PASSPHRASE "); + p = line+15; + if (opt_check) + p = stpcpy (p, "--check "); + p = copy_and_escape (p, cacheid); + *p++ = ' '; + p = copy_and_escape (p, tryagain); + *p++ = ' '; + p = copy_and_escape (p, prompt); + *p++ = ' '; + p = copy_and_escape (p, description); + *p++ = '\n'; + + init_membuf_secure (&data, 64); + + rc = assuan_transact (ctx, line, put_membuf_cb, &data, + default_inq_cb, NULL, NULL, NULL); + spwq_free (line); + + /* Older Pinentries return the old assuan error code for canceled + which gets translated by libassuan to GPG_ERR_ASS_CANCELED and + not to the code for a user cancel. Fix this here. */ + if (rc && gpg_err_source (rc) + && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + if (rc) + { + p = get_membuf (&data, &n); + if (p) + wipememory (p, n); + spwq_free (p); + } + else + { + put_membuf (&data, "", 1); + result = get_membuf (&data, NULL); + if (pw == NULL) + rc = gpg_error_from_syserror (); + } + } + + leave: + if (errorcode) + *errorcode = rc; + assuan_release (ctx); + return result; +} + + +/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */ +int +simple_pwclear (const char *cacheid) +{ + char line[500]; + char *p; + + /* We need not more than 50 characters for the command and the + terminating nul. */ + if (strlen (cacheid) * 3 > sizeof (line) - 50) + return SPWQ_PROTOCOL_ERROR; + + strcpy (line, "CLEAR_PASSPHRASE "); + p = line + 17; + p = copy_and_escape (p, cacheid); + *p++ = '\n'; + *p++ = '\0'; + + return simple_query (line); +} + + +/* Perform the simple query QUERY (which must be new-line and 0 + terminated) and return the error code. */ +int +simple_query (const char *query) +{ + assuan_context_t ctx; + int rc; + + rc = agent_open (&ctx); + if (rc) + return rc; + + rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL); + + assuan_release (ctx); + return rc; +} diff --git a/common/simple-pwquery.h b/common/simple-pwquery.h new file mode 100644 index 0000000..f98a396 --- /dev/null +++ b/common/simple-pwquery.h @@ -0,0 +1,70 @@ +/* simple-pwquery.c - A simple password query cleint for gpg-agent + * Copyright (C) 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 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, see . + */ + +#ifndef SIMPLE_PWQUERY_H +#define SIMPLE_PWQUERY_H + +#ifdef SIMPLE_PWQUERY_IMPLEMENTATION /* Begin configuration stuff. */ + +/* Include whatever files you need. */ +#include +#include "../common/logging.h" + +/* Try to write error message using the standard gnupg log mechanism. */ +#define SPWQ_USE_LOGGING 1 + +/* Memory allocation functions used by the implementation. Note, that + the returned value is expected to be freed with + spwq_secure_free. */ +#define spwq_malloc(a) gcry_malloc (a) +#define spwq_free(a) gcry_free (a) +#define spwq_secure_malloc(a) gcry_malloc_secure (a) +#define spwq_secure_free(a) gcry_free (a) + +#endif /*SIMPLE_PWQUERY_IMPLEMENTATION*/ /* End configuration stuff. */ + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in in + the cache and store it under this ID. If OPT_CHECK is true + gpg-agent is asked to apply some checks on the passphrase security. + If ERRORCODE is not NULL it should point a variable receiving an + errorcode; this errocode might be 0 if the user canceled the + operation. The function returns NULL to indicate an error. */ +char *simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int opt_check, + int *errorcode); + +/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */ +int simple_pwclear (const char *cacheid); + +/* Perform the simple query QUERY (which must be new-line and 0 + terminated) and return the error code. */ +int simple_query (const char *query); + +/* Set the name of the standard socket to be used if GPG_AGENT_INFO is + not defined. The use of this function is optional but if it needs + to be called before any other function. Returns 0 on success. */ +int simple_pw_set_socket (const char *name); + +#endif /*SIMPLE_PWQUERY_H*/ diff --git a/common/ssh-utils.c b/common/ssh-utils.c new file mode 100644 index 0000000..60aa07b --- /dev/null +++ b/common/ssh-utils.c @@ -0,0 +1,284 @@ +/* ssh-utils.c - Secure Shell helper functions + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "ssh-utils.h" + + +/* Return true if KEYPARMS holds an EdDSA key. */ +static int +is_eddsa (gcry_sexp_t keyparms) +{ + int result = 0; + gcry_sexp_t list; + const char *s; + size_t n; + int i; + + list = gcry_sexp_find_token (keyparms, "flags", 0); + for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (list, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + result = 1; + break; + } + } + gcry_sexp_release (list); + return result; +} + + +/* Return the Secure Shell type fingerprint for KEY. The length of + the fingerprint is returned at R_LEN and the fingerprint itself at + R_FPR. In case of a error code is returned and NULL stored at + R_FPR. */ +static gpg_error_t +get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string) +{ + gpg_error_t err; + gcry_sexp_t list = NULL; + gcry_sexp_t l2 = NULL; + const char *s; + char *name = NULL; + int idx; + const char *elems; + gcry_md_hd_t md = NULL; + int blobmode = 0; + + *r_fpr = NULL; + *r_len = 0; + + /* Check that the first element is valid. */ + list = gcry_sexp_find_token (key, "public-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "private-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "protected-private-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "shadowed-private-key", 0); + if (!list) + { + err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP); + goto leave; + } + + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + l2 = NULL; + + name = gcry_sexp_nth_string (list, 0); + if (!name) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + + err = gcry_md_open (&md, GCRY_MD_MD5, 0); + if (err) + goto leave; + + switch (gcry_pk_map_name (name)) + { + case GCRY_PK_RSA: + elems = "en"; + gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11); + break; + + case GCRY_PK_DSA: + elems = "pqgy"; + gcry_md_write (md, "\0\0\0\x07ssh-dss", 11); + break; + + case GCRY_PK_ECC: + if (is_eddsa (list)) + { + elems = "q"; + blobmode = 1; + /* For now there is just one curve, thus no need to switch + on it. */ + gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15); + } + else + { + /* We only support the 3 standard curves for now. It is + just a quick hack. */ + elems = "q"; + gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20); + l2 = gcry_sexp_find_token (list, "curve", 0); + if (!l2) + elems = ""; + else + { + gcry_free (name); + name = gcry_sexp_nth_string (l2, 1); + gcry_sexp_release (l2); + l2 = NULL; + if (!name) + elems = ""; + else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256")) + gcry_md_write (md, "256\0\0\0\x08nistp256", 15); + else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384")) + gcry_md_write (md, "384\0\0\0\x08nistp384", 15); + else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521")) + gcry_md_write (md, "521\0\0\0\x08nistp521", 15); + else + elems = ""; + } + if (!*elems) + err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE); + } + break; + + default: + elems = ""; + err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO); + break; + } + if (err) + goto leave; + + + for (idx = 0, s = elems; *s; s++, idx++) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + if (blobmode) + { + const char *blob; + size_t bloblen; + unsigned char lenbuf[4]; + + blob = gcry_sexp_nth_data (l2, 1, &bloblen); + if (!blob) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + blob++; + bloblen--; + lenbuf[0] = bloblen >> 24; + lenbuf[1] = bloblen >> 16; + lenbuf[2] = bloblen >> 8; + lenbuf[3] = bloblen; + gcry_md_write (md, lenbuf, 4); + gcry_md_write (md, blob, bloblen); + } + else + { + gcry_mpi_t a; + unsigned char *buf; + size_t buflen; + + a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + l2 = NULL; + if (!a) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + + err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a); + gcry_mpi_release (a); + if (err) + goto leave; + gcry_md_write (md, buf, buflen); + gcry_free (buf); + } + } + + *r_fpr = gcry_malloc (as_string? 61:20); + if (!*r_fpr) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + + if (as_string) + { + bin2hexcolon (gcry_md_read (md, GCRY_MD_MD5), 16, *r_fpr); + *r_len = 3*16+1; + strlwr (*r_fpr); + } + else + { + memcpy (*r_fpr, gcry_md_read (md, GCRY_MD_MD5), 16); + *r_len = 16; + } + err = 0; + + leave: + gcry_free (name); + gcry_sexp_release (l2); + gcry_md_close (md); + gcry_sexp_release (list); + return err; +} + +/* Return the Secure Shell type fingerprint for KEY. The length of + the fingerprint is returned at R_LEN and the fingerprint itself at + R_FPR. In case of an error an error code is returned and NULL + stored at R_FPR. */ +gpg_error_t +ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len) +{ + return get_fingerprint (key, r_fpr, r_len, 0); +} + + +/* Return the Secure Shell type fingerprint for KEY as a string. The + fingerprint is mallcoed and stored at R_FPRSTR. In case of an + error an error code is returned and NULL stored at R_FPRSTR. */ +gpg_error_t +ssh_get_fingerprint_string (gcry_sexp_t key, char **r_fprstr) +{ + gpg_error_t err; + size_t dummy; + void *string; + + err = get_fingerprint (key, &string, &dummy, 1); + *r_fprstr = string; + return err; +} diff --git a/common/ssh-utils.h b/common/ssh-utils.h new file mode 100644 index 0000000..36d38a3 --- /dev/null +++ b/common/ssh-utils.h @@ -0,0 +1,39 @@ +/* ssh-utils.c - Secure Shell helper function definitions + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_SSH_UTILS_H +#define GNUPG_COMMON_SSH_UTILS_H + + +gpg_error_t ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len); + +gpg_error_t ssh_get_fingerprint_string (gcry_sexp_t key, char **r_fprstr); + + +#endif /*GNUPG_COMMON_SSH_UTILS_H*/ diff --git a/common/status-codes.h b/common/status-codes.h new file mode 100644 index 0000000..6be226d --- /dev/null +++ b/common/status-codes.h @@ -0,0 +1,238 @@ +/* Output of mkstrtable.awk. DO NOT EDIT. */ + +/* status.h - Status codes + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +/* The purpose of this complex string table is to produce + optimal code with a minimum of relocations. */ + +static const char statusstr_msgstr[] = + "ENTER" "\0" + "LEAVE" "\0" + "ABORT" "\0" + "GOODSIG" "\0" + "BADSIG" "\0" + "ERRSIG" "\0" + "BADARMOR" "\0" + "TRUST_UNDEFINED" "\0" + "TRUST_NEVER" "\0" + "TRUST_MARGINAL" "\0" + "TRUST_FULLY" "\0" + "TRUST_ULTIMATE" "\0" + "NEED_PASSPHRASE" "\0" + "VALIDSIG" "\0" + "SIG_ID" "\0" + "ENC_TO" "\0" + "NODATA" "\0" + "BAD_PASSPHRASE" "\0" + "NO_PUBKEY" "\0" + "NO_SECKEY" "\0" + "NEED_PASSPHRASE_SYM" "\0" + "DECRYPTION_INFO" "\0" + "DECRYPTION_FAILED" "\0" + "DECRYPTION_OKAY" "\0" + "MISSING_PASSPHRASE" "\0" + "GOOD_PASSPHRASE" "\0" + "GOODMDC" "\0" + "BADMDC" "\0" + "ERRMDC" "\0" + "IMPORTED" "\0" + "IMPORT_OK" "\0" + "IMPORT_PROBLEM" "\0" + "IMPORT_RES" "\0" + "IMPORT_CHECK" "\0" + "EXPORTED" "\0" + "EXPORT_RES" "\0" + "FILE_START" "\0" + "FILE_DONE" "\0" + "FILE_ERROR" "\0" + "BEGIN_DECRYPTION" "\0" + "END_DECRYPTION" "\0" + "BEGIN_ENCRYPTION" "\0" + "END_ENCRYPTION" "\0" + "BEGIN_SIGNING" "\0" + "DELETE_PROBLEM" "\0" + "GET_BOOL" "\0" + "GET_LINE" "\0" + "GET_HIDDEN" "\0" + "GOT_IT" "\0" + "PROGRESS" "\0" + "SIG_CREATED" "\0" + "SESSION_KEY" "\0" + "NOTATION_NAME" "\0" + "NOTATION_FLAGS" "\0" + "NOTATION_DATA" "\0" + "POLICY_URL" "\0" + "KEY_CREATED" "\0" + "USERID_HINT" "\0" + "UNEXPECTED" "\0" + "INV_RECP" "\0" + "INV_SGNR" "\0" + "NO_RECP" "\0" + "NO_SGNR" "\0" + "KEY_CONSIDERED" "\0" + "ALREADY_SIGNED" "\0" + "KEYEXPIRED" "\0" + "KEYREVOKED" "\0" + "EXPSIG" "\0" + "EXPKEYSIG" "\0" + "ATTRIBUTE" "\0" + "REVKEYSIG" "\0" + "NEWSIG" "\0" + "SIG_SUBPACKET" "\0" + "PLAINTEXT" "\0" + "PLAINTEXT_LENGTH" "\0" + "KEY_NOT_CREATED" "\0" + "NEED_PASSPHRASE_PIN" "\0" + "CARDCTRL" "\0" + "SC_OP_FAILURE" "\0" + "SC_OP_SUCCESS" "\0" + "BACKUP_KEY_CREATED" "\0" + "PKA_TRUST_BAD" "\0" + "PKA_TRUST_GOOD" "\0" + "TOFU_USER" "\0" + "TOFU_STATS" "\0" + "TOFU_STATS_SHORT" "\0" + "TOFU_STATS_LONG" "\0" + "TRUNCATED" "\0" + "MOUNTPOINT" "\0" + "BLOCKDEV" "\0" + "PINENTRY_LAUNCHED" "\0" + "PLAINTEXT_FOLLOWS" "\0" + "ERROR" "\0" + "WARNING" "\0" + "SUCCESS" "\0" + "FAILURE" "\0" + "INQUIRE_MAXLEN"; + +static const int statusstr_msgidx[] = + { + 0, + 6, + 12, + 18, + 26, + 33, + 40, + 49, + 65, + 77, + 92, + 104, + 119, + 135, + 144, + 151, + 158, + 165, + 180, + 190, + 200, + 220, + 236, + 254, + 270, + 289, + 305, + 313, + 320, + 327, + 336, + 346, + 361, + 372, + 385, + 394, + 405, + 416, + 426, + 437, + 454, + 469, + 486, + 501, + 515, + 530, + 539, + 548, + 559, + 566, + 575, + 587, + 599, + 613, + 628, + 642, + 653, + 665, + 677, + 688, + 697, + 706, + 714, + 722, + 737, + 752, + 763, + 774, + 781, + 791, + 801, + 811, + 818, + 832, + 842, + 859, + 875, + 895, + 904, + 918, + 932, + 951, + 965, + 980, + 990, + 1001, + 1018, + 1034, + 1044, + 1055, + 1064, + 1082, + 1100, + 1106, + 1114, + 1122, + 1130, + + }; + +#define statusstr_msgidxof(code) (0 ? -1 \ + : ((code >= 0) && (code <= 96)) ? (code - 0) \ + : -1) diff --git a/common/status.c b/common/status.c new file mode 100644 index 0000000..50afce4 --- /dev/null +++ b/common/status.c @@ -0,0 +1,76 @@ +/* status.c - status code helper functions + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include + +#include "util.h" +#include "status.h" +#include "status-codes.h" + + +/* Return the status string for code NO. */ +const char * +get_status_string ( int no ) +{ + int idx = statusstr_msgidxof (no); + if (idx == -1) + return "?"; + else + return statusstr_msgstr + statusstr_msgidx[idx]; +} + + +const char * +get_inv_recpsgnr_code (gpg_error_t err) +{ + const char *errstr; + + switch (gpg_err_code (err)) + { + case GPG_ERR_NO_PUBKEY: errstr = "1"; break; + case GPG_ERR_AMBIGUOUS_NAME: errstr = "2"; break; + case GPG_ERR_WRONG_KEY_USAGE: errstr = "3"; break; + case GPG_ERR_CERT_REVOKED: errstr = "4"; break; + case GPG_ERR_CERT_EXPIRED: errstr = "5"; break; + case GPG_ERR_NO_CRL_KNOWN: errstr = "6"; break; + case GPG_ERR_CRL_TOO_OLD: errstr = "7"; break; + case GPG_ERR_NO_POLICY_MATCH: errstr = "8"; break; + + case GPG_ERR_UNUSABLE_SECKEY: + case GPG_ERR_NO_SECKEY: errstr = "9"; break; + + case GPG_ERR_NOT_TRUSTED: errstr = "10"; break; + case GPG_ERR_MISSING_CERT: errstr = "11"; break; + case GPG_ERR_MISSING_ISSUER_CERT: errstr = "12"; break; + default: errstr = "0"; break; + } + + return errstr; +} diff --git a/common/status.h b/common/status.h new file mode 100644 index 0000000..3de4aa5 --- /dev/null +++ b/common/status.h @@ -0,0 +1,164 @@ +/* status.h - Status codes + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_STATUS_H +#define GNUPG_COMMON_STATUS_H + +enum + { + STATUS_ENTER, + STATUS_LEAVE, + STATUS_ABORT, + + STATUS_GOODSIG, + STATUS_BADSIG, + STATUS_ERRSIG, + + STATUS_BADARMOR, + + STATUS_TRUST_UNDEFINED, + STATUS_TRUST_NEVER, + STATUS_TRUST_MARGINAL, + STATUS_TRUST_FULLY, + STATUS_TRUST_ULTIMATE, + + STATUS_NEED_PASSPHRASE, + STATUS_VALIDSIG, + STATUS_SIG_ID, + STATUS_ENC_TO, + STATUS_NODATA, + STATUS_BAD_PASSPHRASE, + STATUS_NO_PUBKEY, + STATUS_NO_SECKEY, + STATUS_NEED_PASSPHRASE_SYM, + STATUS_DECRYPTION_INFO, + STATUS_DECRYPTION_FAILED, + STATUS_DECRYPTION_OKAY, + STATUS_MISSING_PASSPHRASE, + STATUS_GOOD_PASSPHRASE, + STATUS_GOODMDC, + STATUS_BADMDC, + STATUS_ERRMDC, + + STATUS_IMPORTED, + STATUS_IMPORT_OK, + STATUS_IMPORT_PROBLEM, + STATUS_IMPORT_RES, + STATUS_IMPORT_CHECK, + + STATUS_EXPORTED, + STATUS_EXPORT_RES, + + STATUS_FILE_START, + STATUS_FILE_DONE, + STATUS_FILE_ERROR, + + STATUS_BEGIN_DECRYPTION, + STATUS_END_DECRYPTION, + STATUS_BEGIN_ENCRYPTION, + STATUS_END_ENCRYPTION, + STATUS_BEGIN_SIGNING, + + STATUS_DELETE_PROBLEM, + + STATUS_GET_BOOL, + STATUS_GET_LINE, + STATUS_GET_HIDDEN, + STATUS_GOT_IT, + + STATUS_PROGRESS, + STATUS_SIG_CREATED, + STATUS_SESSION_KEY, + STATUS_NOTATION_NAME, + STATUS_NOTATION_FLAGS, + STATUS_NOTATION_DATA, + STATUS_POLICY_URL, + STATUS_KEY_CREATED, + STATUS_USERID_HINT, + STATUS_UNEXPECTED, + STATUS_INV_RECP, + STATUS_INV_SGNR, + STATUS_NO_RECP, + STATUS_NO_SGNR, + STATUS_KEY_CONSIDERED, + + STATUS_ALREADY_SIGNED, + STATUS_KEYEXPIRED, + STATUS_KEYREVOKED, + STATUS_EXPSIG, + STATUS_EXPKEYSIG, + + STATUS_ATTRIBUTE, + + STATUS_REVKEYSIG, + + STATUS_NEWSIG, + STATUS_SIG_SUBPACKET, + + STATUS_PLAINTEXT, + STATUS_PLAINTEXT_LENGTH, + STATUS_KEY_NOT_CREATED, + STATUS_NEED_PASSPHRASE_PIN, + + STATUS_CARDCTRL, + STATUS_SC_OP_FAILURE, + STATUS_SC_OP_SUCCESS, + + STATUS_BACKUP_KEY_CREATED, + + STATUS_PKA_TRUST_BAD, + STATUS_PKA_TRUST_GOOD, + + STATUS_TOFU_USER, + STATUS_TOFU_STATS, + STATUS_TOFU_STATS_SHORT, + STATUS_TOFU_STATS_LONG, + + STATUS_TRUNCATED, + STATUS_MOUNTPOINT, + STATUS_BLOCKDEV, + + STATUS_PINENTRY_LAUNCHED, + + STATUS_PLAINTEXT_FOLLOWS, /* Used by g13-syshelp */ + + STATUS_ERROR, + STATUS_WARNING, + STATUS_SUCCESS, + STATUS_FAILURE, + + STATUS_INQUIRE_MAXLEN + }; + + +const char *get_status_string (int code); +const char *get_inv_recpsgnr_code (gpg_error_t err); + + +#endif /*GNUPG_COMMON_STATUS_H*/ diff --git a/common/stringhelp.c b/common/stringhelp.c new file mode 100644 index 0000000..dea2212 --- /dev/null +++ b/common/stringhelp.c @@ -0,0 +1,1567 @@ +/* stringhelp.c - standard string helper functions + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, + * 2008, 2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PWD_H +# include +#endif +#include +#include +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#endif +#include +#include + +#include "util.h" +#include "common-defs.h" +#include "utf8conv.h" +#include "sysutils.h" +#include "stringhelp.h" + +#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a')) + + +/* Sometimes we want to avoid mixing slashes and backslashes on W32 + and prefer backslashes. There is usual no problem with mixing + them, however a very few W32 API calls can't grok plain slashes. + Printing filenames with mixed slashes also looks a bit strange. + This function has no effext on POSIX. */ +static inline char * +change_slashes (char *name) +{ +#ifdef HAVE_DOSISH_SYSTEM + char *p; + + if (strchr (name, '\\')) + { + for (p=name; *p; p++) + if (*p == '/') + *p = '\\'; + } +#endif /*HAVE_DOSISH_SYSTEM*/ + return name; +} + + +/* + * Check whether STRING starts with KEYWORD. The keyword is + * delimited by end of string, a space or a tab. Returns NULL if not + * found or a pointer into STRING to the next non-space character + * after the KEYWORD (which may be end of string). + */ +char * +has_leading_keyword (const char *string, const char *keyword) +{ + size_t n = strlen (keyword); + + if (!strncmp (string, keyword, n) + && (!string[n] || string[n] == ' ' || string[n] == '\t')) + { + string += n; + while (*string == ' ' || *string == '\t') + string++; + return (char*)string; + } + return NULL; +} + + +/* + * Look for the substring SUB in buffer and return a pointer to that + * substring in BUFFER or NULL if not found. + * Comparison is case-insensitive. + */ +const char * +memistr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buffer; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if ( toupper (*t) == toupper (*s) ) + { + for ( buf=t++, buflen = n--, s++; + n && toupper (*t) == toupper (*s); t++, s++, n-- ) + ; + if (!*s) + return (const char*)buf; + t = buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + +const char * +ascii_memistr ( const void *buffer, size_t buflen, const char *sub ) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if (ascii_toupper (*t) == ascii_toupper (*s) ) + { + for ( buf=t++, buflen = n--, s++; + n && ascii_toupper (*t) == ascii_toupper (*s); t++, s++, n-- ) + ; + if (!*s) + return (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + +/* This function is similar to strncpy(). However it won't copy more + than N - 1 characters and makes sure that a '\0' is appended. With + N given as 0, nothing will happen. With DEST given as NULL, memory + will be allocated using xmalloc (i.e. if it runs out of core + the function terminates). Returns DES or a pointer to the + allocated memory. + */ +char * +mem2str( char *dest , const void *src , size_t n ) +{ + char *d; + const char *s; + + if( n ) { + if( !dest ) + dest = xmalloc( n ) ; + d = dest; + s = src ; + for(n--; n && *s; n-- ) + *d++ = *s++; + *d = '\0' ; + } + + return dest ; +} + + +/**************** + * remove leading and trailing white spaces + */ +char * +trim_spaces( char *str ) +{ + char *string, *p, *mark; + + string = str; + /* find first non space character */ + for( p=string; *p && isspace( *(byte*)p ) ; p++ ) + ; + /* move characters */ + for( (mark = NULL); (*string = *p); string++, p++ ) + if( isspace( *(byte*)p ) ) { + if( !mark ) + mark = string ; + } + else + mark = NULL ; + if( mark ) + *mark = '\0' ; /* remove trailing spaces */ + + return str ; +} + +/**************** + * remove trailing white spaces + */ +char * +trim_trailing_spaces( char *string ) +{ + char *p, *mark; + + for( mark = NULL, p = string; *p; p++ ) { + if( isspace( *(byte*)p ) ) { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + if( mark ) + *mark = '\0' ; + + return string ; +} + + +unsigned +trim_trailing_chars( byte *line, unsigned len, const char *trimchars ) +{ + byte *p, *mark; + unsigned n; + + for(mark=NULL, p=line, n=0; n < len; n++, p++ ) { + if( strchr(trimchars, *p ) ) { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + + if( mark ) { + *mark = 0; + return mark - line; + } + return len; +} + +/**************** + * remove trailing white spaces and return the length of the buffer + */ +unsigned +trim_trailing_ws( byte *line, unsigned len ) +{ + return trim_trailing_chars( line, len, " \t\r\n" ); +} + +size_t +length_sans_trailing_chars (const unsigned char *line, size_t len, + const char *trimchars ) +{ + const unsigned char *p, *mark; + size_t n; + + for( mark=NULL, p=line, n=0; n < len; n++, p++ ) + { + if (strchr (trimchars, *p )) + { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + + if (mark) + return mark - line; + return len; +} + +/* + * Return the length of line ignoring trailing white-space. + */ +size_t +length_sans_trailing_ws (const unsigned char *line, size_t len) +{ + return length_sans_trailing_chars (line, len, " \t\r\n"); +} + + + +/* + * Extract from a given path the filename component. This function + * terminates the process on memory shortage. + */ +char * +make_basename(const char *filepath, const char *inputpath) +{ +#ifdef __riscos__ + return riscos_make_basename(filepath, inputpath); +#else + char *p; + + (void)inputpath; /* Only required for riscos. */ + + if ( !(p=strrchr(filepath, '/')) ) +#ifdef HAVE_DOSISH_SYSTEM + if ( !(p=strrchr(filepath, '\\')) ) +#endif +#ifdef HAVE_DRIVE_LETTERS + if ( !(p=strrchr(filepath, ':')) ) +#endif + { + return xstrdup(filepath); + } + + return xstrdup(p+1); +#endif +} + + + +/* + * Extract from a given filename the path prepended to it. If there + * isn't a path prepended to the filename, a dot is returned ('.'). + * This function terminates the process on memory shortage. + */ +char * +make_dirname(const char *filepath) +{ + char *dirname; + int dirname_length; + char *p; + + if ( !(p=strrchr(filepath, '/')) ) +#ifdef HAVE_DOSISH_SYSTEM + if ( !(p=strrchr(filepath, '\\')) ) +#endif +#ifdef HAVE_DRIVE_LETTERS + if ( !(p=strrchr(filepath, ':')) ) +#endif + { + return xstrdup("."); + } + + dirname_length = p-filepath; + dirname = xmalloc(dirname_length+1); + strncpy(dirname, filepath, dirname_length); + dirname[dirname_length] = 0; + + return dirname; +} + + + +static char * +get_pwdir (int xmode, const char *name) +{ + char *result = NULL; +#ifdef HAVE_PWD_H + struct passwd *pwd = NULL; + + if (name) + { +#ifdef HAVE_GETPWNAM + /* Fixme: We should use getpwnam_r if available. */ + pwd = getpwnam (name); +#endif + } + else + { +#ifdef HAVE_GETPWUID + /* Fixme: We should use getpwuid_r if available. */ + pwd = getpwuid (getuid()); +#endif + } + if (pwd) + { + if (xmode) + result = xstrdup (pwd->pw_dir); + else + result = xtrystrdup (pwd->pw_dir); + } +#else /*!HAVE_PWD_H*/ + /* No support at all. */ + (void)xmode; + (void)name; +#endif /*HAVE_PWD_H*/ + return result; +} + + +/* xmode 0 := Return NULL on error + 1 := Terminate on error + 2 := Make sure that name is absolute; return NULL on error + 3 := Make sure that name is absolute; terminate on error + */ +static char * +do_make_filename (int xmode, const char *first_part, va_list arg_ptr) +{ + const char *argv[32]; + int argc; + size_t n; + int skip = 1; + char *home_buffer = NULL; + char *name, *home, *p; + int want_abs; + + want_abs = !!(xmode & 2); + xmode &= 1; + + n = strlen (first_part) + 1; + argc = 0; + while ( (argv[argc] = va_arg (arg_ptr, const char *)) ) + { + n += strlen (argv[argc]) + 1; + if (argc >= DIM (argv)-1) + { + if (xmode) + BUG (); + gpg_err_set_errno (EINVAL); + return NULL; + } + argc++; + } + n++; + + home = NULL; + if (*first_part == '~') + { + if (first_part[1] == '/' || !first_part[1]) + { + /* This is the "~/" or "~" case. */ + home = getenv("HOME"); + if (!home) + home = home_buffer = get_pwdir (xmode, NULL); + if (home && *home) + n += strlen (home); + } + else + { + /* This is the "~username/" or "~username" case. */ + char *user; + + if (xmode) + user = xstrdup (first_part+1); + else + { + user = xtrystrdup (first_part+1); + if (!user) + return NULL; + } + p = strchr (user, '/'); + if (p) + *p = 0; + skip = 1 + strlen (user); + + home = home_buffer = get_pwdir (xmode, user); + xfree (user); + if (home) + n += strlen (home); + else + skip = 1; + } + } + + if (xmode) + name = xmalloc (n); + else + { + name = xtrymalloc (n); + if (!name) + { + xfree (home_buffer); + return NULL; + } + } + + if (home) + p = stpcpy (stpcpy (name, home), first_part + skip); + else + p = stpcpy (name, first_part); + + xfree (home_buffer); + for (argc=0; argv[argc]; argc++) + { + /* Avoid a leading double slash if the first part was "/". */ + if (!argc && name[0] == '/' && !name[1]) + p = stpcpy (p, argv[argc]); + else + p = stpcpy (stpcpy (p, "/"), argv[argc]); + } + + if (want_abs) + { +#ifdef HAVE_DRIVE_LETTERS + p = strchr (name, ':'); + if (p) + p++; + else + p = name; +#else + p = name; +#endif + if (*p != '/' +#ifdef HAVE_DRIVE_LETTERS + && *p != '\\' +#endif + ) + { + home = gnupg_getcwd (); + if (!home) + { + if (xmode) + { + fprintf (stderr, "\nfatal: getcwd failed: %s\n", + strerror (errno)); + exit(2); + } + xfree (name); + return NULL; + } + n = strlen (home) + 1 + strlen (name) + 1; + if (xmode) + home_buffer = xmalloc (n); + else + { + home_buffer = xtrymalloc (n); + if (!home_buffer) + { + xfree (home); + xfree (name); + return NULL; + } + } + if (p == name) + p = home_buffer; + else /* Windows case. */ + { + memcpy (home_buffer, p, p - name + 1); + p = home_buffer + (p - name + 1); + } + + /* Avoid a leading double slash if the cwd is "/". */ + if (home[0] == '/' && !home[1]) + strcpy (stpcpy (p, "/"), name); + else + strcpy (stpcpy (stpcpy (p, home), "/"), name); + + xfree (home); + xfree (name); + name = home_buffer; + /* Let's do a simple compression to catch the most common + case of using "." for gpg's --homedir option. */ + n = strlen (name); + if (n > 2 && name[n-2] == '/' && name[n-1] == '.') + name[n-2] = 0; + } + } + return change_slashes (name); +} + +/* Construct a filename from the NULL terminated list of parts. Tilde + expansion is done for the first argument. This function terminates + the process on memory shortage. */ +char * +make_filename (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (1, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct a filename from the NULL terminated list of parts. Tilde + expansion is done for the first argument. This function may return + NULL on error. */ +char * +make_filename_try (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (0, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct an absolute filename from the NULL terminated list of + parts. Tilde expansion is done for the first argument. This + function terminates the process on memory shortage. */ +char * +make_absfilename (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (3, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct an absolute filename from the NULL terminated list of + parts. Tilde expansion is done for the first argument. This + function may return NULL on error. */ +char * +make_absfilename_try (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (2, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + + + +/* Compare whether the filenames are identical. This is a + special version of strcmp() taking the semantics of filenames in + account. Note that this function works only on the supplied names + without considering any context like the current directory. See + also same_file_p(). */ +int +compare_filenames (const char *a, const char *b) +{ +#ifdef HAVE_DOSISH_SYSTEM + for ( ; *a && *b; a++, b++ ) + { + if (*a != *b + && (toupper (*(const unsigned char*)a) + != toupper (*(const unsigned char*)b) ) + && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/'))) + break; + } + if ((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')) + return 0; + else + return (toupper (*(const unsigned char*)a) + - toupper (*(const unsigned char*)b)); +#else + return strcmp(a,b); +#endif +} + + +/* Convert a base-10 number in STRING into a 64 bit unsigned int + * value. Leading white spaces are skipped but no error checking is + * done. Thus it is similar to atoi(). */ +uint64_t +string_to_u64 (const char *string) +{ + uint64_t val = 0; + + while (spacep (string)) + string++; + for (; digitp (string); string++) + { + val *= 10; + val += *string - '0'; + } + return val; +} + + +/* Convert 2 hex characters at S to a byte value. Return this value + or -1 if there is an error. */ +int +hextobyte (const char *s) +{ + int c; + + if ( *s >= '0' && *s <= '9' ) + c = 16 * (*s - '0'); + else if ( *s >= 'A' && *s <= 'F' ) + c = 16 * (10 + *s - 'A'); + else if ( *s >= 'a' && *s <= 'f' ) + c = 16 * (10 + *s - 'a'); + else + return -1; + s++; + if ( *s >= '0' && *s <= '9' ) + c += *s - '0'; + else if ( *s >= 'A' && *s <= 'F' ) + c += 10 + *s - 'A'; + else if ( *s >= 'a' && *s <= 'f' ) + c += 10 + *s - 'a'; + else + return -1; + return c; +} + +/* Given a string containing an UTF-8 encoded text, return the number + of characters in this string. It differs from strlen in that it + only counts complete UTF-8 characters. SIZE is the maximum length + of the string in bytes. If SIZE is -1, then a NUL character is + taken to be the end of the string. Note, that this function does + not take combined characters into account. */ +size_t +utf8_charcount (const char *s, int len) +{ + size_t n; + + if (len == 0) + return 0; + + for (n=0; *s; s++) + { + if ( (*s&0xc0) != 0x80 ) /* Exclude continuation bytes: 10xxxxxx */ + n++; + + if (len != -1) + { + len --; + if (len == 0) + break; + } + } + + return n; +} + + +/**************************************************** + ********** W32 specific functions **************** + ****************************************************/ + +#ifdef HAVE_W32_SYSTEM +const char * +w32_strerror (int ec) +{ + static char strerr[256]; + + if (ec == -1) + ec = (int)GetLastError (); +#ifdef HAVE_W32CE_SYSTEM + /* There is only a wchar_t FormatMessage. It does not make much + sense to play the conversion game; we print only the code. */ + snprintf (strerr, sizeof strerr, "ec=%d", (int)GetLastError ()); +#else + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + strerr, DIM (strerr)-1, NULL); +#endif + return strerr; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/**************************************************** + ******** Locale insensitive ctype functions ******** + ****************************************************/ +/* FIXME: replace them by a table lookup and macros */ +int +ascii_isupper (int c) +{ + return c >= 'A' && c <= 'Z'; +} + +int +ascii_islower (int c) +{ + return c >= 'a' && c <= 'z'; +} + +int +ascii_toupper (int c) +{ + if (c >= 'a' && c <= 'z') + c &= ~0x20; + return c; +} + +int +ascii_tolower (int c) +{ + if (c >= 'A' && c <= 'Z') + c |= 0x20; + return c; +} + +/* Lowercase all ASCII characters in S. */ +char * +ascii_strlwr (char *s) +{ + char *p = s; + + for (p=s; *p; p++ ) + if (isascii (*p) && *p >= 'A' && *p <= 'Z') + *p |= 0x20; + + return s; +} + +int +ascii_strcasecmp( const char *a, const char *b ) +{ + if (a == b) + return 0; + + for (; *a && *b; a++, b++) { + if (*a != *b && ascii_toupper(*a) != ascii_toupper(*b)) + break; + } + return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b)); +} + +int +ascii_strncasecmp (const char *a, const char *b, size_t n) +{ + const unsigned char *p1 = (const unsigned char *)a; + const unsigned char *p2 = (const unsigned char *)b; + unsigned char c1, c2; + + if (p1 == p2 || !n ) + return 0; + + do + { + c1 = ascii_tolower (*p1); + c2 = ascii_tolower (*p2); + + if ( !--n || c1 == '\0') + break; + + ++p1; + ++p2; + } + while (c1 == c2); + + return c1 - c2; +} + + +int +ascii_memcasecmp (const void *a_arg, const void *b_arg, size_t n ) +{ + const char *a = a_arg; + const char *b = b_arg; + + if (a == b) + return 0; + for ( ; n; n--, a++, b++ ) + { + if( *a != *b && ascii_toupper (*a) != ascii_toupper (*b) ) + return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b)); + } + return 0; +} + +int +ascii_strcmp( const char *a, const char *b ) +{ + if (a == b) + return 0; + + for (; *a && *b; a++, b++) { + if (*a != *b ) + break; + } + return *a == *b? 0 : (*(signed char *)a - *(signed char *)b); +} + + +void * +ascii_memcasemem (const void *haystack, size_t nhaystack, + const void *needle, size_t nneedle) +{ + + if (!nneedle) + return (void*)haystack; /* finding an empty needle is really easy */ + if (nneedle <= nhaystack) + { + const char *a = haystack; + const char *b = a + nhaystack - nneedle; + + for (; a <= b; a++) + { + if ( !ascii_memcasecmp (a, needle, nneedle) ) + return (void *)a; + } + } + return NULL; +} + +/********************************************* + ********** missing string functions ********* + *********************************************/ + +#ifndef HAVE_STPCPY +char * +stpcpy(char *a,const char *b) +{ + while( *b ) + *a++ = *b++; + *a = 0; + + return (char*)a; +} +#endif + +#ifndef HAVE_STRPBRK +/* Find the first occurrence in S of any character in ACCEPT. + Code taken from glibc-2.6/string/strpbrk.c (LGPLv2.1+) and modified. */ +char * +strpbrk (const char *s, const char *accept) +{ + while (*s != '\0') + { + const char *a = accept; + while (*a != '\0') + if (*a++ == *s) + return (char *) s; + ++s; + } + + return NULL; +} +#endif /*!HAVE_STRPBRK*/ + + +#ifndef HAVE_STRSEP +/* Code taken from glibc-2.2.1/sysdeps/generic/strsep.c. */ +char * +strsep (char **stringp, const char *delim) +{ + char *begin, *end; + + begin = *stringp; + if (begin == NULL) + return NULL; + + /* A frequent case is when the delimiter string contains only one + character. Here we don't need to call the expensive 'strpbrk' + function and instead work using 'strchr'. */ + if (delim[0] == '\0' || delim[1] == '\0') + { + char ch = delim[0]; + + if (ch == '\0') + end = NULL; + else + { + if (*begin == ch) + end = begin; + else if (*begin == '\0') + end = NULL; + else + end = strchr (begin + 1, ch); + } + } + else + /* Find the end of the token. */ + end = strpbrk (begin, delim); + + if (end) + { + /* Terminate the token and set *STRINGP past NUL character. */ + *end++ = '\0'; + *stringp = end; + } + else + /* No more delimiters; this is the last token. */ + *stringp = NULL; + + return begin; +} +#endif /*HAVE_STRSEP*/ + + +#ifndef HAVE_STRLWR +char * +strlwr(char *s) +{ + char *p; + for(p=s; *p; p++ ) + *p = tolower(*p); + return s; +} +#endif + + +#ifndef HAVE_STRCASECMP +int +strcasecmp( const char *a, const char *b ) +{ + for( ; *a && *b; a++, b++ ) { + if( *a != *b && toupper(*a) != toupper(*b) ) + break; + } + return *(const byte*)a - *(const byte*)b; +} +#endif + + +/**************** + * mingw32/cpd has a memicmp() + */ +#ifndef HAVE_MEMICMP +int +memicmp( const char *a, const char *b, size_t n ) +{ + for( ; n; n--, a++, b++ ) + if( *a != *b && toupper(*(const byte*)a) != toupper(*(const byte*)b) ) + return *(const byte *)a - *(const byte*)b; + return 0; +} +#endif + + +#ifndef HAVE_MEMRCHR +void * +memrchr (const void *buffer, int c, size_t n) +{ + const unsigned char *p = buffer; + + for (p += n; n ; n--) + if (*--p == c) + return (void *)p; + return NULL; +} +#endif /*HAVE_MEMRCHR*/ + + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL all characters in EXTRA are also escaped. */ +static char * +do_percent_escape (const char *str, const char *extra, int die) +{ + int i, j; + char *ptr; + + if (!str) + return NULL; + + for (i=j=0; str[i]; i++) + if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i]))) + j++; + if (die) + ptr = xmalloc (i + 2 * j + 1); + else + { + ptr = xtrymalloc (i + 2 * j + 1); + if (!ptr) + return NULL; + } + i = 0; + while (*str) + { + if (*str == ':') + { + ptr[i++] = '%'; + ptr[i++] = '3'; + ptr[i++] = 'a'; + } + else if (*str == '%') + { + ptr[i++] = '%'; + ptr[i++] = '2'; + ptr[i++] = '5'; + } + else if (extra && strchr (extra, *str)) + { + ptr[i++] = '%'; + ptr[i++] = tohex_lower ((*str>>4)&15); + ptr[i++] = tohex_lower (*str&15); + } + else + ptr[i++] = *str; + str++; + } + ptr[i] = '\0'; + + return ptr; +} + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL all characters in EXTRA are also escaped. This + function terminates the process on memory shortage. */ +char * +percent_escape (const char *str, const char *extra) +{ + return do_percent_escape (str, extra, 1); +} + +/* Same as percent_escape but return NULL instead of exiting on memory + error. */ +char * +try_percent_escape (const char *str, const char *extra) +{ + return do_percent_escape (str, extra, 0); +} + + + +static char * +do_strconcat (const char *s1, va_list arg_ptr) +{ + const char *argv[48]; + size_t argc; + size_t needed; + char *buffer, *p; + + argc = 0; + argv[argc++] = s1; + needed = strlen (s1); + while (((argv[argc] = va_arg (arg_ptr, const char *)))) + { + needed += strlen (argv[argc]); + if (argc >= DIM (argv)-1) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + argc++; + } + needed++; + buffer = xtrymalloc (needed); + if (buffer) + { + for (p = buffer, argc=0; argv[argc]; argc++) + p = stpcpy (p, argv[argc]); + } + return buffer; +} + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char * +strconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = xtrystrdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (s1, arg_ptr); + va_end (arg_ptr); + } + return result; +} + +/* Same as strconcat but terminate the process with an error message + if something goes wrong. */ +char * +xstrconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = xstrdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (s1, arg_ptr); + va_end (arg_ptr); + } + if (!result) + { + if (errno == EINVAL) + fputs ("\nfatal: too many args for xstrconcat\n", stderr); + else + fputs ("\nfatal: out of memory\n", stderr); + exit (2); + } + return result; +} + +/* Split a string into fields at DELIM. REPLACEMENT is the character + to replace the delimiter with (normally: '\0' so that each field is + NUL terminated). The caller is responsible for freeing the result. + Note: this function modifies STRING! If you need the original + value, then you should pass a copy to this function. + + If malloc fails, this function returns NULL. */ +char ** +strsplit (char *string, char delim, char replacement, int *count) +{ + int fields = 1; + char *t; + char **result; + + /* First, count the number of fields. */ + for (t = strchr (string, delim); t; t = strchr (t + 1, delim)) + fields ++; + + result = xtrycalloc ((fields + 1), sizeof (*result)); + if (! result) + return NULL; + + result[0] = string; + fields = 1; + for (t = strchr (string, delim); t; t = strchr (t + 1, delim)) + { + result[fields ++] = t + 1; + *t = replacement; + } + + if (count) + *count = fields; + + return result; +} + + +/* Tokenize STRING using the set of delimiters in DELIM. Leading + * spaces and tabs are removed from all tokens. The caller must xfree + * the result. + * + * Returns: A malloced and NULL delimited array with the tokens. On + * memory error NULL is returned and ERRNO is set. + */ +char ** +strtokenize (const char *string, const char *delim) +{ + const char *s; + size_t fields; + size_t bytes, n; + char *buffer; + char *p, *px, *pend; + char **result; + + /* Count the number of fields. */ + for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim)) + fields++; + fields++; /* Add one for the terminating NULL. */ + + /* Allocate an array for all fields, a terminating NULL, and space + for a copy of the string. */ + bytes = fields * sizeof *result; + if (bytes / sizeof *result != fields) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + n = strlen (string) + 1; + bytes += n; + if (bytes < n) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + result = xtrymalloc (bytes); + if (!result) + return NULL; + buffer = (char*)(result + fields); + + /* Copy and parse the string. */ + strcpy (buffer, string); + for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1) + { + *pend = 0; + while (spacep (p)) + p++; + for (px = pend - 1; px >= p && spacep (px); px--) + *px = 0; + result[n++] = p; + } + while (spacep (p)) + p++; + for (px = p + strlen (p) - 1; px >= p && spacep (px); px--) + *px = 0; + result[n++] = p; + result[n] = NULL; + + assert ((char*)(result + n + 1) == buffer); + + return result; +} + + +/* Split a string into space delimited fields and remove leading and + * trailing spaces from each field. A pointer to each field is stored + * in ARRAY. Stop splitting at ARRAYSIZE fields. The function + * modifies STRING. The number of parsed fields is returned. + * Example: + * + * char *fields[2]; + * if (split_fields (string, fields, DIM (fields)) < 2) + * return // Not enough args. + * foo (fields[0]); + * foo (fields[1]); + */ +int +split_fields (char *string, char **array, int arraysize) +{ + int n = 0; + char *p, *pend; + + for (p = string; *p == ' '; p++) + ; + do + { + if (n == arraysize) + break; + array[n++] = p; + pend = strchr (p, ' '); + if (!pend) + break; + *pend++ = 0; + for (p = pend; *p == ' '; p++) + ; + } + while (*p); + + return n; +} + + + +/* Version number parsing. */ + +/* This function parses the first portion of the version number S and + stores it in *NUMBER. On success, this function returns a pointer + into S starting with the first character, which is not part of the + initial number portion; on failure, NULL is returned. */ +static const char* +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && digitp (s+1)) + return NULL; /* Leading zeros are not allowed. */ + for (; digitp (s); s++) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0 ? NULL : s; +} + + +/* This function breaks up the complete string-representation of the + version number S, which is of the following struture: .[.]. The major, + minor, and micro number components will be stored in *MAJOR, *MINOR + and *MICRO. If MICRO is not given 0 is used instead. + + On success, the last component, the patch level, will be returned; + in failure, NULL will be returned. */ +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s) + return NULL; + if (*s == '.') + { + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + } + else + *micro = 0; + return s; /* Patchlevel. */ +} + + +/* Compare the version string MY_VERSION to the version string + * REQ_VERSION. Returns -1, 0, or 1 if MY_VERSION is found, + * respectively, to be less than, to match, or be greater than + * REQ_VERSION. This function works for three and two part version + * strings; for a two part version string the micro part is assumed to + * be 0. Patch levels are compared as strings. If a version number + * is invalid INT_MIN is returned. If REQ_VERSION is given as NULL + * the function returns 0 if MY_VERSION is parsable version string. */ +int +compare_version_strings (const char *my_version, const char *req_version) +{ + int my_major, my_minor, my_micro; + int rq_major, rq_minor, rq_micro; + const char *my_patch, *rq_patch; + int result; + + if (!my_version) + return INT_MIN; + + my_patch = parse_version_string (my_version, &my_major, &my_minor, &my_micro); + if (!my_patch) + return INT_MIN; + if (!req_version) + return 0; /* MY_VERSION can be parsed. */ + rq_patch = parse_version_string (req_version, &rq_major, &rq_minor,&rq_micro); + if (!rq_patch) + return INT_MIN; + + if (my_major == rq_major) + { + if (my_minor == rq_minor) + { + if (my_micro == rq_micro) + result = strcmp (my_patch, rq_patch); + else + result = my_micro - rq_micro; + } + else + result = my_minor - rq_minor; + } + else + result = my_major - rq_major; + + return !result? 0 : result < 0 ? -1 : 1; +} + + + +/* Format a string so that it fits within about TARGET_COLS columns. + If IN_PLACE is 0, then TEXT is copied to a new buffer, which is + returned. Otherwise, TEXT is modified in place and returned. + Normally, target_cols will be 72 and max_cols is 80. */ +char * +format_text (char *text, int in_place, int target_cols, int max_cols) +{ + const int do_debug = 0; + + /* The character under consideration. */ + char *p; + /* The start of the current line. */ + char *line; + /* The last space that we saw. */ + char *last_space = NULL; + int last_space_cols = 0; + int copied_last_space = 0; + + if (! in_place) + text = xstrdup (text); + + p = line = text; + while (1) + { + /* The number of columns including any trailing space. */ + int cols; + + p = p + strcspn (p, "\n "); + if (! p) + /* P now points to the NUL character. */ + p = &text[strlen (text)]; + + if (*p == '\n') + /* Pass through any newlines. */ + { + p ++; + line = p; + last_space = NULL; + last_space_cols = 0; + copied_last_space = 1; + continue; + } + + /* Have a space or a NUL. Note: we don't count the trailing + space. */ + cols = utf8_charcount (line, (uintptr_t) p - (uintptr_t) line); + if (cols < target_cols) + { + if (! *p) + /* Nothing left to break. */ + break; + + last_space = p; + last_space_cols = cols; + p ++; + /* Skip any immediately following spaces. If we break: + "... foo bar ..." between "foo" and "bar" then we want: + "... foo\nbar ...", which means that the left space has + to be the first space after foo, not the last space + before bar. */ + while (*p == ' ') + p ++; + } + else + { + int cols_with_left_space; + int cols_with_right_space; + int left_penalty; + int right_penalty; + + cols_with_left_space = last_space_cols; + cols_with_right_space = cols; + + if (do_debug) + log_debug ("Breaking: '%.*s'\n", + (int) ((uintptr_t) p - (uintptr_t) line), line); + + /* The number of columns away from TARGET_COLS. We prefer + to underflow than to overflow. */ + left_penalty = target_cols - cols_with_left_space; + right_penalty = 2 * (cols_with_right_space - target_cols); + + if (cols_with_right_space > max_cols) + /* Add a large penalty for each column that exceeds + max_cols. */ + right_penalty += 4 * (cols_with_right_space - max_cols); + + if (do_debug) + log_debug ("Left space => %d cols (penalty: %d); right space => %d cols (penalty: %d)\n", + cols_with_left_space, left_penalty, + cols_with_right_space, right_penalty); + if (last_space_cols && left_penalty <= right_penalty) + /* Prefer the left space. */ + { + if (do_debug) + log_debug ("Breaking at left space.\n"); + p = last_space; + } + else + { + if (do_debug) + log_debug ("Breaking at right space.\n"); + } + + if (! *p) + break; + + *p = '\n'; + p ++; + if (*p == ' ') + { + int spaces; + for (spaces = 1; p[spaces] == ' '; spaces ++) + ; + memmove (p, &p[spaces], strlen (&p[spaces]) + 1); + } + line = p; + last_space = NULL; + last_space_cols = 0; + copied_last_space = 0; + } + } + + /* Chop off any trailing space. */ + trim_trailing_chars (text, strlen (text), " "); + /* If we inserted the trailing newline, then remove it. */ + if (! copied_last_space && *text && text[strlen (text) - 1] == '\n') + text[strlen (text) - 1] = '\0'; + + return text; +} diff --git a/common/stringhelp.h b/common/stringhelp.h new file mode 100644 index 0000000..d0156d5 --- /dev/null +++ b/common/stringhelp.h @@ -0,0 +1,164 @@ +/* stringhelp.h + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2006, 2007, 2009 Free Software Foundation, Inc. + * 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_STRINGHELP_H +#define GNUPG_COMMON_STRINGHELP_H + +#include +#include "types.h" + +/*-- stringhelp.c --*/ +char *has_leading_keyword (const char *string, const char *keyword); + +const char *memistr (const void *buf, size_t buflen, const char *sub); +char *mem2str( char *, const void *, size_t); +char *trim_spaces( char *string ); +char *trim_trailing_spaces( char *string ); +unsigned int trim_trailing_chars( unsigned char *line, unsigned len, + const char *trimchars); +unsigned int trim_trailing_ws( unsigned char *line, unsigned len ); +size_t length_sans_trailing_chars (const unsigned char *line, size_t len, + const char *trimchars ); +size_t length_sans_trailing_ws (const unsigned char *line, size_t len); + + +char *make_basename(const char *filepath, const char *inputpath); +char *make_dirname(const char *filepath); +char *make_filename( const char *first_part, ... ) GPGRT_ATTR_SENTINEL(0); +char *make_filename_try (const char *first_part, ... ) GPGRT_ATTR_SENTINEL(0); +char *make_absfilename (const char *first_part, ...) GPGRT_ATTR_SENTINEL(0); +char *make_absfilename_try (const char *first_part, + ...) GPGRT_ATTR_SENTINEL(0); +int compare_filenames( const char *a, const char *b ); + +uint64_t string_to_u64 (const char *string); +int hextobyte (const char *s); + +size_t utf8_charcount (const char *s, int len); + + +#ifdef HAVE_W32_SYSTEM +const char *w32_strerror (int ec); +#endif + + +int ascii_isupper (int c); +int ascii_islower (int c); +int ascii_toupper (int c); +int ascii_tolower (int c); +char *ascii_strlwr (char *s); +int ascii_strcasecmp( const char *a, const char *b ); +int ascii_strncasecmp (const char *a, const char *b, size_t n); +int ascii_memcasecmp( const void *a, const void *b, size_t n ); +const char *ascii_memistr ( const void *buf, size_t buflen, const char *sub); +void *ascii_memcasemem (const void *haystack, size_t nhaystack, + const void *needle, size_t nneedle); + + +#ifndef HAVE_MEMICMP +int memicmp( const char *a, const char *b, size_t n ); +#endif +#ifndef HAVE_STPCPY +char *stpcpy(char *a,const char *b); +#endif +#ifndef HAVE_STRPBRK +char *strpbrk (const char *s, const char *accept); +#endif +#ifndef HAVE_STRSEP +char *strsep (char **stringp, const char *delim); +#endif +#ifndef HAVE_STRLWR +char *strlwr(char *a); +#endif +#ifndef HAVE_STRTOUL +# define strtoul(a,b,c) ((unsigned long)strtol((a),(b),(c))) +#endif +#ifndef HAVE_MEMMOVE +# define memmove(d, s, n) bcopy((s), (d), (n)) +#endif +#ifndef HAVE_STRICMP +# define stricmp(a,b) strcasecmp( (a), (b) ) +#endif +#ifndef HAVE_MEMRCHR +void *memrchr (const void *buffer, int c, size_t n); +#endif + + +#ifndef HAVE_ISASCII +static inline int +isascii (int c) +{ + return (((c) & ~0x7f) == 0); +} +#endif /* !HAVE_ISASCII */ + + +#ifndef STR +# define STR(v) #v +#endif +#define STR2(v) STR(v) + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL, also replace all characters given in EXTRA. The + "try_" variant fails with NULL if not enough memory can be + allocated. */ +char *percent_escape (const char *str, const char *extra); +char *try_percent_escape (const char *str, const char *extra); + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char *strconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0); +/* Ditto, but die on error. */ +char *xstrconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0); + +char **strsplit (char *string, char delim, char replacement, int *count); + +/* Tokenize STRING using the set of delimiters in DELIM. */ +char **strtokenize (const char *string, const char *delim); + +/* Split STRING into space delimited fields and store them in the + * provided ARRAY. */ +int split_fields (char *string, char **array, int arraysize); + +/* Return True if MYVERSION is greater or equal than REQ_VERSION. */ +int compare_version_strings (const char *my_version, const char *req_version); + +/* Format a string so that it fits within about TARGET_COLS columns. */ +char *format_text (char *text, int in_place, int target_cols, int max_cols); + + +/*-- mapstrings.c --*/ +const char *map_static_macro_string (const char *string); + +#endif /*GNUPG_COMMON_STRINGHELP_H*/ diff --git a/common/strlist.c b/common/strlist.c new file mode 100644 index 0000000..02881cd --- /dev/null +++ b/common/strlist.c @@ -0,0 +1,281 @@ +/* strlist.c - string helpers + * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "common-defs.h" +#include "strlist.h" +#include "utf8conv.h" +#include "mischelp.h" + +void +free_strlist( strlist_t sl ) +{ + strlist_t sl2; + + for(; sl; sl = sl2 ) { + sl2 = sl->next; + xfree(sl); + } +} + + +void +free_strlist_wipe (strlist_t sl) +{ + strlist_t sl2; + + for(; sl; sl = sl2 ) { + sl2 = sl->next; + wipememory (sl, sizeof *sl + strlen (sl->d)); + xfree(sl); + } +} + + +/* Add STRING to the LIST at the front. This function terminates the + process on memory shortage. */ +strlist_t +add_to_strlist( strlist_t *list, const char *string ) +{ + strlist_t sl; + + sl = xmalloc( sizeof *sl + strlen(string)); + sl->flags = 0; + strcpy(sl->d, string); + sl->next = *list; + *list = sl; + return sl; +} + + +/* Add STRING to the LIST at the front. This function returns NULL + and sets ERRNO on memory shortage. */ +strlist_t +add_to_strlist_try (strlist_t *list, const char *string) +{ + strlist_t sl; + + sl = xtrymalloc (sizeof *sl + strlen (string)); + if (sl) + { + sl->flags = 0; + strcpy (sl->d, string); + sl->next = *list; + *list = sl; + } + return sl; +} + + +/* Same as add_to_strlist() but if IS_UTF8 is *not* set, a conversion + to UTF-8 is done. This function terminates the process on memory + shortage. */ +strlist_t +add_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) +{ + strlist_t sl; + + if (is_utf8) + sl = add_to_strlist( list, string ); + else + { + char *p = native_to_utf8( string ); + sl = add_to_strlist( list, p ); + xfree ( p ); + } + return sl; +} + + +/* Add STRING to the LIST at the end. This function terminates the + process on memory shortage. */ +strlist_t +append_to_strlist( strlist_t *list, const char *string ) +{ + strlist_t sl; + sl = append_to_strlist_try (list, string); + if (!sl) + xoutofcore (); + return sl; +} + + +/* Add STRING to the LIST at the end. */ +strlist_t +append_to_strlist_try (strlist_t *list, const char *string) +{ + strlist_t r, sl; + + sl = xtrymalloc( sizeof *sl + strlen(string)); + if (sl == NULL) + return NULL; + + sl->flags = 0; + strcpy(sl->d, string); + sl->next = NULL; + if( !*list ) + *list = sl; + else { + for( r = *list; r->next; r = r->next ) + ; + r->next = sl; + } + return sl; +} + + +strlist_t +append_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) +{ + strlist_t sl; + + if( is_utf8 ) + sl = append_to_strlist( list, string ); + else + { + char *p = native_to_utf8 (string); + sl = append_to_strlist( list, p ); + xfree( p ); + } + return sl; +} + + +/* Return a copy of LIST. This function terminates the process on + memory shortage.*/ +strlist_t +strlist_copy (strlist_t list) +{ + strlist_t newlist = NULL, sl, *last; + + last = &newlist; + for (; list; list = list->next) + { + sl = xmalloc (sizeof *sl + strlen (list->d)); + sl->flags = list->flags; + strcpy(sl->d, list->d); + sl->next = NULL; + *last = sl; + last = &sl; + } + return newlist; +} + + + +strlist_t +strlist_prev( strlist_t head, strlist_t node ) +{ + strlist_t n; + + for(n=NULL; head && head != node; head = head->next ) + n = head; + return n; +} + +strlist_t +strlist_last( strlist_t node ) +{ + if( node ) + for( ; node->next ; node = node->next ) + ; + return node; +} + + +/* Remove the first item from LIST and return its content in an + allocated buffer. This function terminates the process on memory + shortage. */ +char * +strlist_pop (strlist_t *list) +{ + char *str=NULL; + strlist_t sl=*list; + + if(sl) + { + str = xmalloc(strlen(sl->d)+1); + strcpy(str,sl->d); + + *list=sl->next; + xfree(sl); + } + + return str; +} + +/* Return the first element of the string list HAYSTACK whose string + matches NEEDLE. If no elements match, return NULL. */ +strlist_t +strlist_find (strlist_t haystack, const char *needle) +{ + for (; + haystack; + haystack = haystack->next) + if (strcmp (haystack->d, needle) == 0) + return haystack; + return NULL; +} + +int +strlist_length (strlist_t list) +{ + int i; + for (i = 0; list; list = list->next) + i ++; + + return i; +} + +/* Reverse the list *LIST in place. */ +strlist_t +strlist_rev (strlist_t *list) +{ + strlist_t l = *list; + strlist_t lrev = NULL; + + while (l) + { + strlist_t tail = l->next; + l->next = lrev; + lrev = l; + l = tail; + } + + *list = lrev; + return lrev; +} diff --git a/common/strlist.h b/common/strlist.h new file mode 100644 index 0000000..d74bc4d --- /dev/null +++ b/common/strlist.h @@ -0,0 +1,69 @@ +/* strlist.h + * Copyright (C) 1998, 2000, 2001, 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_STRLIST_H +#define GNUPG_COMMON_STRLIST_H + +struct string_list +{ + struct string_list *next; + unsigned int flags; + char d[1]; +}; +typedef struct string_list *strlist_t; + +void free_strlist (strlist_t sl); +void free_strlist_wipe (strlist_t sl); + +strlist_t add_to_strlist (strlist_t *list, const char *string); +strlist_t add_to_strlist_try (strlist_t *list, const char *string); + +strlist_t add_to_strlist2( strlist_t *list, const char *string, int is_utf8); + +strlist_t append_to_strlist (strlist_t *list, const char *string); +strlist_t append_to_strlist_try (strlist_t *list, const char *string); +strlist_t append_to_strlist2 (strlist_t *list, const char *string, + int is_utf8); + +strlist_t strlist_copy (strlist_t list); + +strlist_t strlist_prev (strlist_t head, strlist_t node); +strlist_t strlist_last (strlist_t node); +char * strlist_pop (strlist_t *list); + +strlist_t strlist_find (strlist_t haystack, const char *needle); +int strlist_length (strlist_t list); + +strlist_t strlist_rev (strlist_t *haystack); + +#define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0) + + +#endif /*GNUPG_COMMON_STRLIST_H*/ diff --git a/common/sysutils.c b/common/sysutils.c new file mode 100644 index 0000000..e67420f --- /dev/null +++ b/common/sysutils.c @@ -0,0 +1,1283 @@ +/* sysutils.c - system helpers + * Copyright (C) 1991-2001, 2003-2004, + * 2006-2008 Free Software Foundation, Inc. + * Copyright (C) 2013-2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +# undef HAVE_NPTH +# undef USE_NPTH +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STAT +# include +#endif +#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 +# include +# include +#endif +#include +#ifdef HAVE_SETRLIMIT +# include +# include +#endif +#ifdef HAVE_W32_SYSTEM +# if WINVER < 0x0500 +# define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */ +# endif +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else /*!HAVE_W32_SYSTEM*/ +# include +# include +#endif +#ifdef HAVE_INOTIFY_INIT +# include +#endif /*HAVE_INOTIFY_INIT*/ +#ifdef HAVE_NPTH +# include +#endif +#include + +#include + +#include "util.h" +#include "i18n.h" + +#include "sysutils.h" + +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + +/* Flag to tell whether special file names are enabled. See gpg.c for + * an explanation of these file names. */ +static int allow_special_filenames; + + +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static GPGRT_INLINE gpg_error_t +my_error (int e) +{ + return gpg_err_make (default_errsource, (e)); +} + + + +#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 +#warning using trap_unaligned +static int +setsysinfo(unsigned long op, void *buffer, unsigned long size, + int *start, void *arg, unsigned long flag) +{ + return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); +} + +void +trap_unaligned(void) +{ + unsigned int buf[2]; + + buf[0] = SSIN_UACPROC; + buf[1] = UAC_SIGBUS | UAC_NOPRINT; + setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); +} +#else +void +trap_unaligned(void) +{ /* dummy */ +} +#endif + + +int +disable_core_dumps (void) +{ +#ifdef HAVE_DOSISH_SYSTEM + return 0; +#else +# ifdef HAVE_SETRLIMIT + struct rlimit limit; + + /* We only set the current limit unless we were not able to + retrieve the old value. */ + if (getrlimit (RLIMIT_CORE, &limit)) + limit.rlim_max = 0; + limit.rlim_cur = 0; + if( !setrlimit (RLIMIT_CORE, &limit) ) + return 0; + if( errno != EINVAL && errno != ENOSYS ) + log_fatal (_("can't disable core dumps: %s\n"), strerror(errno) ); +#endif + return 1; +#endif +} + +int +enable_core_dumps (void) +{ +#ifdef HAVE_DOSISH_SYSTEM + return 0; +#else +# ifdef HAVE_SETRLIMIT + struct rlimit limit; + + if (getrlimit (RLIMIT_CORE, &limit)) + return 1; + limit.rlim_cur = limit.rlim_max; + setrlimit (RLIMIT_CORE, &limit); + return 1; /* We always return true because this function is + merely a debugging aid. */ +# endif + return 1; +#endif +} + + +/* Allow the use of special "-&nnn" style file names. */ +void +enable_special_filenames (void) +{ + allow_special_filenames = 1; +} + + +/* Return a string which is used as a kind of process ID. */ +const byte * +get_session_marker (size_t *rlen) +{ + static byte marker[SIZEOF_UNSIGNED_LONG*2]; + static int initialized; + + if (!initialized) + { + gcry_create_nonce (marker, sizeof marker); + initialized = 1; + } + *rlen = sizeof (marker); + return marker; +} + +/* Return a random number in an unsigned int. */ +unsigned int +get_uint_nonce (void) +{ + unsigned int value; + + gcry_create_nonce (&value, sizeof value); + return value; +} + + + +#if 0 /* not yet needed - Note that this will require inclusion of + cmacros.am in Makefile.am */ +int +check_permissions(const char *path,int extension,int checkonly) +{ +#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) + char *tmppath; + struct stat statbuf; + int ret=1; + int isdir=0; + + if(opt.no_perm_warn) + return 0; + + if(extension && path[0]!=DIRSEP_C) + { + if(strchr(path,DIRSEP_C)) + tmppath=make_filename(path,NULL); + else + tmppath=make_filename(GNUPG_LIBDIR,path,NULL); + } + else + tmppath=m_strdup(path); + + /* It's okay if the file doesn't exist */ + if(stat(tmppath,&statbuf)!=0) + { + ret=0; + goto end; + } + + isdir=S_ISDIR(statbuf.st_mode); + + /* Per-user files must be owned by the user. Extensions must be + owned by the user or root. */ + if((!extension && statbuf.st_uid != getuid()) || + (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid())) + { + if(!checkonly) + log_info(_("Warning: unsafe ownership on %s \"%s\"\n"), + isdir?"directory":extension?"extension":"file",path); + goto end; + } + + /* This works for both directories and files - basically, we don't + care what the owner permissions are, so long as the group and + other permissions are 0 for per-user files, and non-writable for + extensions. */ + if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) || + (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0)) + { + char *dir; + + /* However, if the directory the directory/file is in is owned + by the user and is 700, then this is not a problem. + Theoretically, we could walk this test up to the root + directory /, but for the sake of sanity, I'm stopping at one + level down. */ + + dir= make_dirname (tmppath); + if(stat(dir,&statbuf)==0 && statbuf.st_uid==getuid() && + S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) + { + xfree (dir); + ret=0; + goto end; + } + + m_free(dir); + + if(!checkonly) + log_info(_("Warning: unsafe permissions on %s \"%s\"\n"), + isdir?"directory":extension?"extension":"file",path); + goto end; + } + + ret=0; + + end: + m_free(tmppath); + + return ret; + +#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ + + return 0; +} +#endif + + +/* Wrapper around the usual sleep function. This one won't wake up + before the sleep time has really elapsed. When build with Pth it + merely calls pth_sleep and thus suspends only the current + thread. */ +void +gnupg_sleep (unsigned int seconds) +{ +#ifdef USE_NPTH + npth_sleep (seconds); +#else + /* Fixme: make sure that a sleep won't wake up to early. */ +# ifdef HAVE_W32_SYSTEM + Sleep (seconds*1000); +# else + sleep (seconds); +# endif +#endif +} + + +/* Wrapper around the platforms usleep function. This one won't wake + * up before the sleep time has really elapsed. When build with nPth + * it merely calls npth_usleep and thus suspends only the current + * thread. */ +void +gnupg_usleep (unsigned int usecs) +{ +#if defined(USE_NPTH) + + npth_usleep (usecs); + +#elif defined(HAVE_W32_SYSTEM) + + Sleep ((usecs + 999) / 1000); + +#elif defined(HAVE_NANOSLEEP) + + if (usecs) + { + struct timespec req; + struct timespec rem; + + req.tv_sec = 0; + req.tv_nsec = usecs * 1000; + + while (nanosleep (&req, &rem) < 0 && errno == EINTR) + req = rem; + } + +#else /*Standard Unix*/ + + if (usecs) + { + struct timeval tv; + + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + select (0, NULL, NULL, NULL, &tv); + } + +#endif +} + + +/* This function is a NOP for POSIX systems but required under Windows + as the file handles as returned by OS calls (like CreateFile) are + different from the libc file descriptors (like open). This function + translates system file handles to libc file handles. FOR_WRITE + gives the direction of the handle. */ +int +translate_sys2libc_fd (gnupg_fd_t fd, int for_write) +{ +#if defined(HAVE_W32CE_SYSTEM) + (void)for_write; + return (int) fd; +#elif defined(HAVE_W32_SYSTEM) + int x; + + if (fd == GNUPG_INVALID_FD) + return -1; + + /* Note that _open_osfhandle is currently defined to take and return + a long. */ + x = _open_osfhandle ((long)fd, for_write ? 1 : 0); + if (x == -1) + log_error ("failed to translate osfhandle %p\n", (void *) fd); + return x; +#else /*!HAVE_W32_SYSTEM */ + (void)for_write; + return fd; +#endif +} + +/* This is the same as translate_sys2libc_fd but takes an integer + which is assumed to be such an system handle. On WindowsCE the + passed FD is a rendezvous ID and the function finishes the pipe + creation. */ +int +translate_sys2libc_fd_int (int fd, int for_write) +{ +#if HAVE_W32CE_SYSTEM + fd = (int) _assuan_w32ce_finish_pipe (fd, for_write); + return translate_sys2libc_fd ((void*)fd, for_write); +#elif HAVE_W32_SYSTEM + if (fd <= 2) + return fd; /* Do not do this for error, stdin, stdout, stderr. */ + + return translate_sys2libc_fd ((void*)fd, for_write); +#else + (void)for_write; + return fd; +#endif +} + + +/* Check whether FNAME has the form "-&nnnn", where N is a non-zero + * number. Returns this number or -1 if it is not the case. If the + * caller wants to use the file descriptor for writing FOR_WRITE shall + * be set to 1. If NOTRANSLATE is set the Windows spefic mapping is + * not done. */ +int +check_special_filename (const char *fname, int for_write, int notranslate) +{ + if (allow_special_filenames + && fname && *fname == '-' && fname[1] == '&') + { + int i; + + fname += 2; + for (i=0; digitp (fname+i); i++ ) + ; + if (!fname[i]) + return notranslate? atoi (fname) + /**/ : translate_sys2libc_fd_int (atoi (fname), for_write); + } + return -1; +} + + +/* Replacement for tmpfile(). This is required because the tmpfile + function of Windows' runtime library is broken, insecure, ignores + TMPDIR and so on. In addition we create a file with an inheritable + handle. */ +FILE * +gnupg_tmpfile (void) +{ +#ifdef HAVE_W32_SYSTEM + int attempts, n; +#ifdef HAVE_W32CE_SYSTEM + wchar_t buffer[MAX_PATH+7+12+1]; +# define mystrlen(a) wcslen (a) + wchar_t *name, *p; +#else + char buffer[MAX_PATH+7+12+1]; +# define mystrlen(a) strlen (a) + char *name, *p; +#endif + HANDLE file; + int pid = GetCurrentProcessId (); + unsigned int value; + int i; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = TRUE; + + n = GetTempPath (MAX_PATH+1, buffer); + if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH) + { + gpg_err_set_errno (ENOENT); + return NULL; + } + p = buffer + mystrlen (buffer); +#ifdef HAVE_W32CE_SYSTEM + wcscpy (p, L"_gnupg"); + p += 7; +#else + p = stpcpy (p, "_gnupg"); +#endif + /* We try to create the directory but don't care about an error as + it may already exist and the CreateFile would throw an error + anyway. */ + CreateDirectory (buffer, NULL); + *p++ = '\\'; + name = p; + for (attempts=0; attempts < 10; attempts++) + { + p = name; + value = (GetTickCount () ^ ((pid<<16) & 0xffff0000)); + for (i=0; i < 8; i++) + { + *p++ = tohex (((value >> 28) & 0x0f)); + value <<= 4; + } +#ifdef HAVE_W32CE_SYSTEM + wcscpy (p, L".tmp"); +#else + strcpy (p, ".tmp"); +#endif + file = CreateFile (buffer, + GENERIC_READ | GENERIC_WRITE, + 0, + &sec_attr, + CREATE_NEW, + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (file != INVALID_HANDLE_VALUE) + { + FILE *fp; +#ifdef HAVE_W32CE_SYSTEM + int fd = (int)file; + fp = _wfdopen (fd, L"w+b"); +#else + int fd = _open_osfhandle ((long)file, 0); + if (fd == -1) + { + CloseHandle (file); + return NULL; + } + fp = fdopen (fd, "w+b"); +#endif + if (!fp) + { + int save = errno; + close (fd); + gpg_err_set_errno (save); + return NULL; + } + return fp; + } + Sleep (1); /* One ms as this is the granularity of GetTickCount. */ + } + gpg_err_set_errno (ENOENT); + return NULL; +#undef mystrlen +#else /*!HAVE_W32_SYSTEM*/ + return tmpfile (); +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Make sure that the standard file descriptors are opened. Obviously + some folks close them before an exec and the next file we open will + get one of them assigned and thus any output (i.e. diagnostics) end + up in that file (e.g. the trustdb). Not actually a gpg problem as + this will happen with almost all utilities when called in a wrong + way. However we try to minimize the damage here and raise + awareness of the problem. + + Must be called before we open any files! */ +void +gnupg_reopen_std (const char *pgmname) +{ +#if defined(HAVE_STAT) && !defined(HAVE_W32_SYSTEM) + struct stat statbuf; + int did_stdin = 0; + int did_stdout = 0; + int did_stderr = 0; + FILE *complain; + + if (fstat (STDIN_FILENO, &statbuf) == -1 && errno ==EBADF) + { + if (open ("/dev/null",O_RDONLY) == STDIN_FILENO) + did_stdin = 1; + else + did_stdin = 2; + } + + if (fstat (STDOUT_FILENO, &statbuf) == -1 && errno == EBADF) + { + if (open ("/dev/null",O_WRONLY) == STDOUT_FILENO) + did_stdout = 1; + else + did_stdout = 2; + } + + if (fstat (STDERR_FILENO, &statbuf)==-1 && errno==EBADF) + { + if (open ("/dev/null", O_WRONLY) == STDERR_FILENO) + did_stderr = 1; + else + did_stderr = 2; + } + + /* It's hard to log this sort of thing since the filehandle we would + complain to may be closed... */ + if (!did_stderr) + complain = stderr; + else if (!did_stdout) + complain = stdout; + else + complain = NULL; + + if (complain) + { + if (did_stdin == 1) + fprintf (complain, "%s: WARNING: standard input reopened\n", pgmname); + if (did_stdout == 1) + fprintf (complain, "%s: WARNING: standard output reopened\n", pgmname); + if (did_stderr == 1) + fprintf (complain, "%s: WARNING: standard error reopened\n", pgmname); + + if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) + fprintf(complain,"%s: fatal: unable to reopen standard input," + " output, or error\n", pgmname); + } + + if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) + exit (3); +#else /* !(HAVE_STAT && !HAVE_W32_SYSTEM) */ + (void)pgmname; +#endif +} + + +/* Hack required for Windows. */ +void +gnupg_allow_set_foregound_window (pid_t pid) +{ + if (!pid) + log_info ("%s called with invalid pid %lu\n", + "gnupg_allow_set_foregound_window", (unsigned long)pid); +#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM) + else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid)) + log_info ("AllowSetForegroundWindow(%lu) failed: %s\n", + (unsigned long)pid, w32_strerror (-1)); +#endif +} + +int +gnupg_remove (const char *fname) +{ +#ifdef HAVE_W32CE_SYSTEM + int rc; + wchar_t *wfname; + + wfname = utf8_to_wchar (fname); + if (!wfname) + rc = 0; + else + { + rc = DeleteFile (wfname); + xfree (wfname); + } + if (!rc) + return -1; /* ERRNO is automagically provided by gpg-error.h. */ + return 0; +#else + return remove (fname); +#endif +} + + +/* Wrapper for rename(2) to handle Windows peculiarities. If + * BLOCK_SIGNALS is not NULL and points to a variable set to true, all + * signals will be blocked by calling gnupg_block_all_signals; the + * caller needs to call gnupg_unblock_all_signals if that variable is + * still set to true on return. */ +gpg_error_t +gnupg_rename_file (const char *oldname, const char *newname, int *block_signals) +{ + gpg_error_t err = 0; + + if (block_signals && *block_signals) + gnupg_block_all_signals (); + +#ifdef HAVE_DOSISH_SYSTEM + { + int wtime = 0; + + gnupg_remove (newname); + again: + if (rename (oldname, newname)) + { + if (GetLastError () == ERROR_SHARING_VIOLATION) + { + /* Another process has the file open. We do not use a + * lock for read but instead we wait until the other + * process has closed the file. This may take long but + * that would also be the case with a dotlock approach for + * read and write. Note that we don't need this on Unix + * due to the inode concept. + * + * So let's wait until the rename has worked. The retry + * intervals are 50, 100, 200, 400, 800, 50ms, ... */ + if (!wtime || wtime >= 800) + wtime = 50; + else + wtime *= 2; + + if (wtime >= 800) + log_info (_("waiting for file '%s' to become accessible ...\n"), + oldname); + + Sleep (wtime); + goto again; + } + err = my_error_from_syserror (); + } + } +#else /* Unix */ + { +#ifdef __riscos__ + gnupg_remove (newname); +#endif + if (rename (oldname, newname) ) + err = my_error_from_syserror (); + } +#endif /* Unix */ + + if (block_signals && *block_signals && err) + { + gnupg_unblock_all_signals (); + *block_signals = 0; + } + + if (err) + log_error (_("renaming '%s' to '%s' failed: %s\n"), + oldname, newname, gpg_strerror (err)); + return err; +} + + +#ifndef HAVE_W32_SYSTEM +static mode_t +modestr_to_mode (const char *modestr) +{ + mode_t mode = 0; + + if (modestr && *modestr) + { + modestr++; + if (*modestr && *modestr++ == 'r') + mode |= S_IRUSR; + if (*modestr && *modestr++ == 'w') + mode |= S_IWUSR; + if (*modestr && *modestr++ == 'x') + mode |= S_IXUSR; + if (*modestr && *modestr++ == 'r') + mode |= S_IRGRP; + if (*modestr && *modestr++ == 'w') + mode |= S_IWGRP; + if (*modestr && *modestr++ == 'x') + mode |= S_IXGRP; + if (*modestr && *modestr++ == 'r') + mode |= S_IROTH; + if (*modestr && *modestr++ == 'w') + mode |= S_IWOTH; + if (*modestr && *modestr++ == 'x') + mode |= S_IXOTH; + } + + return mode; +} +#endif + + +/* A wrapper around mkdir which takes a string for the mode argument. + This makes it easier to handle the mode argument which is not + defined on all systems. The format of the modestring is + + "-rwxrwxrwx" + + '-' is a don't care or not set. 'r', 'w', 'x' are read allowed, + write allowed, execution allowed with the first group for the user, + the second for the group and the third for all others. If the + string is shorter than above the missing mode characters are meant + to be not set. */ +int +gnupg_mkdir (const char *name, const char *modestr) +{ +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wname; + (void)modestr; + + wname = utf8_to_wchar (name); + if (!wname) + return -1; + if (!CreateDirectoryW (wname, NULL)) + { + xfree (wname); + return -1; /* ERRNO is automagically provided by gpg-error.h. */ + } + xfree (wname); + return 0; +#elif MKDIR_TAKES_ONE_ARG + (void)modestr; + /* Note: In the case of W32 we better use CreateDirectory and try to + set appropriate permissions. However using mkdir is easier + because this sets ERRNO. */ + return mkdir (name); +#else + return mkdir (name, modestr_to_mode (modestr)); +#endif +} + + +/* A wrapper around chmod which takes a string for the mode argument. + This makes it easier to handle the mode argument which is not + defined on all systems. The format of the modestring is the same + as for gnupg_mkdir. */ +int +gnupg_chmod (const char *name, const char *modestr) +{ +#ifdef HAVE_W32_SYSTEM + (void)name; + (void)modestr; + return 0; +#else + return chmod (name, modestr_to_mode (modestr)); +#endif +} + + +/* Our version of mkdtemp. The API is identical to POSIX.1-2008 + version. We do not use a system provided mkdtemp because we have a + good RNG instantly available and this way we don't have diverging + versions. */ +char * +gnupg_mkdtemp (char *tmpl) +{ + /* A lower bound on the number of temporary files to attempt to + generate. The maximum total number of temporary file names that + can exist for a given template is 62**6 (5*36**3 for Windows). + It should never be necessary to try all these combinations. + Instead if a reasonable number of names is tried (we define + reasonable as 62**3 or 5*36**3) fail to give the system + administrator the chance to remove the problems. */ +#ifdef HAVE_W32_SYSTEM + static const char letters[] = + "abcdefghijklmnopqrstuvwxyz0123456789"; +# define NUMBER_OF_LETTERS 36 +# define ATTEMPTS_MIN (5 * 36 * 36 * 36) +#else + static const char letters[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +# define NUMBER_OF_LETTERS 62 +# define ATTEMPTS_MIN (62 * 62 * 62) +#endif + int len; + char *XXXXXX; + uint64_t value; + unsigned int count; + int save_errno = errno; + /* The number of times to attempt to generate a temporary file. To + conform to POSIX, this must be no smaller than TMP_MAX. */ +#if ATTEMPTS_MIN < TMP_MAX + unsigned int attempts = TMP_MAX; +#else + unsigned int attempts = ATTEMPTS_MIN; +#endif + + len = strlen (tmpl); + if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + /* This is where the Xs start. */ + XXXXXX = &tmpl[len - 6]; + + /* Get a random start value. */ + gcry_create_nonce (&value, sizeof value); + + /* Loop until a directory was created. */ + for (count = 0; count < attempts; value += 7777, ++count) + { + uint64_t v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[1] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[2] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[3] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[4] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[5] = letters[v % NUMBER_OF_LETTERS]; + + if (!gnupg_mkdir (tmpl, "-rwx")) + { + gpg_err_set_errno (save_errno); + return tmpl; + } + if (errno != EEXIST) + return NULL; + } + + /* We got out of the loop because we ran out of combinations to try. */ + gpg_err_set_errno (EEXIST); + return NULL; +} + + +int +gnupg_setenv (const char *name, const char *value, int overwrite) +{ +#ifdef HAVE_W32CE_SYSTEM + (void)name; + (void)value; + (void)overwrite; + return 0; +#else /*!W32CE*/ +# ifdef HAVE_W32_SYSTEM + /* Windows maintains (at least) two sets of environment variables. + One set can be accessed by GetEnvironmentVariable and + SetEnvironmentVariable. This set is inherited by the children. + The other set is maintained in the C runtime, and is accessed + using getenv and putenv. We try to keep them in sync by + modifying both sets. */ + { + int exists; + char tmpbuf[10]; + exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf); + + if ((! exists || overwrite) && !SetEnvironmentVariable (name, value)) + { + gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ + return -1; + } + } +# endif /*W32*/ + +# ifdef HAVE_SETENV + return setenv (name, value, overwrite); +# else /*!HAVE_SETENV*/ + if (! getenv (name) || overwrite) + { + char *buf; + + (void)overwrite; + if (!name || !value) + { + gpg_err_set_errno (EINVAL); + return -1; + } + buf = strconcat (name, "=", value, NULL); + if (!buf) + return -1; +# if __GNUC__ +# warning no setenv - using putenv but leaking memory. +# endif + return putenv (buf); + } + return 0; +# endif /*!HAVE_SETENV*/ +#endif /*!W32CE*/ +} + + +int +gnupg_unsetenv (const char *name) +{ +#ifdef HAVE_W32CE_SYSTEM + (void)name; + return 0; +#else /*!W32CE*/ +# ifdef HAVE_W32_SYSTEM + /* Windows maintains (at least) two sets of environment variables. + One set can be accessed by GetEnvironmentVariable and + SetEnvironmentVariable. This set is inherited by the children. + The other set is maintained in the C runtime, and is accessed + using getenv and putenv. We try to keep them in sync by + modifying both sets. */ + if (!SetEnvironmentVariable (name, NULL)) + { + gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ + return -1; + } +# endif /*W32*/ + +# ifdef HAVE_UNSETENV + return unsetenv (name); +# else /*!HAVE_UNSETENV*/ + { + char *buf; + + if (!name) + { + gpg_err_set_errno (EINVAL); + return -1; + } + buf = xtrystrdup (name); + if (!buf) + return -1; +# if __GNUC__ +# warning no unsetenv - trying putenv but leaking memory. +# endif + return putenv (buf); + } +# endif /*!HAVE_UNSETENV*/ +#endif /*!W32CE*/ +} + + +/* Return the current working directory as a malloced string. Return + NULL and sets ERRNo on error. */ +char * +gnupg_getcwd (void) +{ + char *buffer; + size_t size = 100; + + for (;;) + { + buffer = xtrymalloc (size+1); + if (!buffer) + return NULL; +#ifdef HAVE_W32CE_SYSTEM + strcpy (buffer, "/"); /* Always "/". */ + return buffer; +#else + if (getcwd (buffer, size) == buffer) + return buffer; + xfree (buffer); + if (errno != ERANGE) + return NULL; + size *= 2; +#endif + } +} + + + +#ifdef HAVE_W32CE_SYSTEM +/* There is a isatty function declaration in cegcc but it does not + make sense, thus we redefine it. */ +int +_gnupg_isatty (int fd) +{ + (void)fd; + return 0; +} +#endif + + +#ifdef HAVE_W32CE_SYSTEM +/* Replacement for getenv which takes care of the our use of getenv. + The code is not thread safe but we expect it to work in all cases + because it is called for the first time early enough. */ +char * +_gnupg_getenv (const char *name) +{ + static int initialized; + static char *assuan_debug; + + if (!initialized) + { + assuan_debug = read_w32_registry_string (NULL, + "\\Software\\GNU\\libassuan", + "debug"); + initialized = 1; + } + + if (!strcmp (name, "ASSUAN_DEBUG")) + return assuan_debug; + else + return NULL; +} + +#endif /*HAVE_W32CE_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* Return the user's security identifier from the current process. */ +PSID +w32_get_user_sid (void) +{ + int okay = 0; + HANDLE proc = NULL; + HANDLE token = NULL; + TOKEN_USER *user = NULL; + PSID sid = NULL; + DWORD tokenlen, sidlen; + + proc = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + if (!proc) + goto leave; + + if (!OpenProcessToken (proc, TOKEN_QUERY, &token)) + goto leave; + + if (!GetTokenInformation (token, TokenUser, NULL, 0, &tokenlen) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto leave; + + user = xtrymalloc (tokenlen); + if (!user) + goto leave; + + if (!GetTokenInformation (token, TokenUser, user, tokenlen, &tokenlen)) + goto leave; + if (!IsValidSid (user->User.Sid)) + goto leave; + sidlen = GetLengthSid (user->User.Sid); + sid = xtrymalloc (sidlen); + if (!sid) + goto leave; + if (!CopySid (sidlen, sid, user->User.Sid)) + goto leave; + okay = 1; + + leave: + xfree (user); + if (token) + CloseHandle (token); + if (proc) + CloseHandle (proc); + + if (!okay) + { + xfree (sid); + sid = NULL; + } + return sid; +} +#endif /*HAVE_W32_SYSTEM*/ + + + +/* Support for inotify under Linux. */ + +/* Store a new inotify file handle for SOCKET_NAME at R_FD or return + * an error code. */ +gpg_error_t +gnupg_inotify_watch_socket (int *r_fd, const char *socket_name) +{ +#if HAVE_INOTIFY_INIT + gpg_error_t err; + char *fname; + int fd; + char *p; + + *r_fd = -1; + + if (!socket_name) + return my_error (GPG_ERR_INV_VALUE); + + fname = xtrystrdup (socket_name); + if (!fname) + return my_error_from_syserror (); + + fd = inotify_init (); + if (fd == -1) + { + err = my_error_from_syserror (); + xfree (fname); + return err; + } + + /* We need to watch the directory for the file because there won't + * be an IN_DELETE_SELF for a socket file. To handle a removal of + * the directory we also watch the directory itself. */ + p = strrchr (fname, '/'); + if (p) + *p = 0; + if (inotify_add_watch (fd, fname, + (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1) + { + err = my_error_from_syserror (); + close (fd); + xfree (fname); + return err; + } + + xfree (fname); + + *r_fd = fd; + return 0; +#else /*!HAVE_INOTIFY_INIT*/ + + (void)socket_name; + *r_fd = -1; + return my_error (GPG_ERR_NOT_SUPPORTED); + +#endif /*!HAVE_INOTIFY_INIT*/ +} + + +/* Read an inotify event and return true if it matches NAME or if it + * sees an IN_DELETE_SELF event for the directory of NAME. */ +int +gnupg_inotify_has_name (int fd, const char *name) +{ +#if USE_NPTH && HAVE_INOTIFY_INIT +#define BUFSIZE_FOR_INOTIFY (sizeof (struct inotify_event) + 255 + 1) + union { + struct inotify_event ev; + char _buf[sizeof (struct inotify_event) + 255 + 1]; + } buf; + struct inotify_event *evp; + int n; + + n = npth_read (fd, &buf, sizeof buf); + /* log_debug ("notify read: n=%d\n", n); */ + evp = &buf.ev; + while (n >= sizeof (struct inotify_event)) + { + /* log_debug (" mask=%x len=%u name=(%s)\n", */ + /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */ + if ((evp->mask & IN_UNMOUNT)) + { + /* log_debug (" found (dir unmounted)\n"); */ + return 3; /* Directory was unmounted. */ + } + if ((evp->mask & IN_DELETE_SELF)) + { + /* log_debug (" found (dir removed)\n"); */ + return 2; /* Directory was removed. */ + } + if ((evp->mask & IN_DELETE)) + { + if (evp->len >= strlen (name) && !strcmp (evp->name, name)) + { + /* log_debug (" found (file removed)\n"); */ + return 1; /* File was removed. */ + } + } + n -= sizeof (*evp) + evp->len; + evp = (struct inotify_event *)(void *) + ((char *)evp + sizeof (*evp) + evp->len); + } + +#else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ + + (void)fd; + (void)name; + +#endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ + + return 0; /* Not found. */ +} + + +/* Return a malloc'ed string that is the path to the passed + * unix-domain socket (or return NULL if this is not a valid + * unix-domain socket). We use a plain int here because it is only + * used on Linux. + * + * FIXME: This function needs to be moved to libassuan. */ +#ifndef HAVE_W32_SYSTEM +char * +gnupg_get_socket_name (int fd) +{ + struct sockaddr_un un; + socklen_t len = sizeof(un); + char *name = NULL; + + if (getsockname (fd, (struct sockaddr*)&un, &len) != 0) + log_error ("could not getsockname(%d): %s\n", fd, + gpg_strerror (my_error_from_syserror ())); + else if (un.sun_family != AF_UNIX) + log_error ("file descriptor %d is not a unix-domain socket\n", fd); + else if (len <= offsetof (struct sockaddr_un, sun_path)) + log_error ("socket name not present for file descriptor %d\n", fd); + else if (len > sizeof(un)) + log_error ("socket name for file descriptor %d was truncated " + "(passed %zu bytes, wanted %u)\n", fd, sizeof(un), len); + else + { + size_t namelen = len - offsetof (struct sockaddr_un, sun_path); + + /* log_debug ("file descriptor %d has path %s (%zu octets)\n", fd, */ + /* un.sun_path, namelen); */ + name = xtrymalloc (namelen + 1); + if (!name) + log_error ("failed to allocate memory for name of fd %d: %s\n", + fd, gpg_strerror (my_error_from_syserror ())); + else + { + memcpy (name, un.sun_path, namelen); + name[namelen] = 0; + } + } + + return name; +} +#endif /*!HAVE_W32_SYSTEM*/ diff --git a/common/sysutils.h b/common/sysutils.h new file mode 100644 index 0000000..a9316d7 --- /dev/null +++ b/common/sysutils.h @@ -0,0 +1,87 @@ +/* sysutils.h - System utility functions for Gnupg + * Copyright (C) 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_SYSUTILS_H +#define GNUPG_COMMON_SYSUTILS_H + +/* Because we use system handles and not libc low level file + descriptors on W32, we need to declare them as HANDLE (which + actually is a plain pointer). This is required to eventually + support 64 bits Windows systems. */ +#ifdef HAVE_W32_SYSTEM +typedef void *gnupg_fd_t; +#define GNUPG_INVALID_FD ((void*)(-1)) +#define INT2FD(s) ((void *)(s)) +#define FD2INT(h) ((unsigned int)(h)) +#else +typedef int gnupg_fd_t; +#define GNUPG_INVALID_FD (-1) +#define INT2FD(s) (s) +#define FD2INT(h) (h) +#endif + + +void trap_unaligned (void); +int disable_core_dumps (void); +int enable_core_dumps (void); +void enable_special_filenames (void); +const unsigned char *get_session_marker (size_t *rlen); +unsigned int get_uint_nonce (void); +/*int check_permissions (const char *path,int extension,int checkonly);*/ +void gnupg_sleep (unsigned int seconds); +void gnupg_usleep (unsigned int usecs); +int translate_sys2libc_fd (gnupg_fd_t fd, int for_write); +int translate_sys2libc_fd_int (int fd, int for_write); +int check_special_filename (const char *fname, int for_write, int notranslate); +FILE *gnupg_tmpfile (void); +void gnupg_reopen_std (const char *pgmname); +void gnupg_allow_set_foregound_window (pid_t pid); +int gnupg_remove (const char *fname); +gpg_error_t gnupg_rename_file (const char *oldname, const char *newname, + int *block_signals); +int gnupg_mkdir (const char *name, const char *modestr); +int gnupg_chmod (const char *name, const char *modestr); +char *gnupg_mkdtemp (char *template); +int gnupg_setenv (const char *name, const char *value, int overwrite); +int gnupg_unsetenv (const char *name); +char *gnupg_getcwd (void); +char *gnupg_get_socket_name (int fd); + +gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name); +int gnupg_inotify_has_name (int fd, const char *name); + + +#ifdef HAVE_W32_SYSTEM +void *w32_get_user_sid (void); + +#include "../common/w32help.h" + +#endif /*HAVE_W32_SYSTEM*/ + +#endif /*GNUPG_COMMON_SYSUTILS_H*/ diff --git a/common/t-b64.c b/common/t-b64.c new file mode 100644 index 0000000..3b63872 --- /dev/null +++ b/common/t-b64.c @@ -0,0 +1,181 @@ +/* t-b64.c - Module tests for b64enc.c and b64dec.c + * Copyright (C) 2008 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 3 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, see . + */ + +/* + + As of now this is only a test program for manual tests. + + */ + + + +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + } while(0) + +static int verbose; +static int errcount; + +static void +test_b64enc_pgp (const char *string) +{ + gpg_error_t err; + struct b64state state; + + if (!string) + string = "a"; + + err = b64enc_start (&state, stdout, "PGP MESSAGE"); + if (err) + fail (1); + + err = b64enc_write (&state, string, strlen (string)); + if (err) + fail (2); + + err = b64enc_finish (&state); + if (err) + fail (3); + + pass (); +} + + +static void +test_b64enc_file (const char *fname) +{ + gpg_error_t err; + struct b64state state; + FILE *fp; + char buffer[50]; + size_t nread; + + fp = fname ? fopen (fname, "r") : stdin; + if (!fp) + { + fprintf (stderr, "%s:%d: can't open '%s': %s\n", + __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno)); + fail (0); + } + + err = b64enc_start (&state, stdout, "DATA"); + if (err) + fail (1); + + while ( (nread = fread (buffer, 1, sizeof buffer, fp)) ) + { + err = b64enc_write (&state, buffer, nread); + if (err) + fail (2); + } + + err = b64enc_finish (&state); + if (err) + fail (3); + + fclose (fp); + pass (); +} + +static void +test_b64dec_file (const char *fname) +{ + gpg_error_t err; + struct b64state state; + FILE *fp; + char buffer[50]; + size_t nread, nbytes; + + fp = fname ? fopen (fname, "r") : stdin; + if (!fp) + { + fprintf (stderr, "%s:%d: can't open '%s': %s\n", + __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno)); + fail (0); + } + + err = b64dec_start (&state, ""); + if (err) + fail (1); + + while ( (nread = fread (buffer, 1, sizeof buffer, fp)) ) + { + err = b64dec_proc (&state, buffer, nread, &nbytes); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_EOF) + break; + fail (2); + } + else if (nbytes) + fwrite (buffer, 1, nbytes, stdout); + } + + err = b64dec_finish (&state); + if (err) + fail (3); + + fclose (fp); + pass (); +} + + + +int +main (int argc, char **argv) +{ + int do_encode = 0; + int do_decode = 0; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + if (argc && !strcmp (argv[0], "--encode")) + { + do_encode = 1; + argc--; argv++; + } + else if (argc && !strcmp (argv[0], "--decode")) + { + do_decode = 1; + argc--; argv++; + } + + if (do_encode) + test_b64enc_file (argc? *argv: NULL); + else if (do_decode) + test_b64dec_file (argc? *argv: NULL); + else + test_b64enc_pgp (argc? *argv: NULL); + + return !!errcount; +} diff --git a/common/t-ccparray.c b/common/t-ccparray.c new file mode 100644 index 0000000..eb96526 --- /dev/null +++ b/common/t-ccparray.c @@ -0,0 +1,93 @@ +/* t-ccparray.c - Module test for ccparray.c + * Copyright (C) 2016 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "ccparray.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +static void +run_test_1 (void) +{ + ccparray_t ccp; + const char **argv; + size_t nelem; + + ccparray_init (&ccp, 0); + ccparray_put (&ccp, "First arg"); + ccparray_put (&ccp, "Second arg"); + ccparray_put (&ccp, NULL); + ccparray_put (&ccp, "Fourth arg"); + argv = ccparray_get (&ccp, &nelem); + if (!argv) + { + fprintf (stderr, "error building array: %s\n", strerror (errno)); + exit (1); + } + + if (nelem != 4) + fail (1); + + /* for (i=0; argv[i]; i++) */ + /* printf ("[%d] = '%s'\n", i, argv[i]); */ + xfree (argv); +} + + +static void +run_test_var (int count) +{ + ccparray_t ccp; + size_t nelem; + int i; + + ccparray_init (&ccp, 0); + for (i=0; i < count; i++) + ccparray_put (&ccp, "An arg"); + xfree (ccparray_get (&ccp, &nelem)); + if (nelem != i) + fail (2); +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + run_test_1 (); + run_test_var (0); + run_test_var (7); + run_test_var (8); + run_test_var (9); + run_test_var (4096); + + return 0; +} diff --git a/common/t-convert.c b/common/t-convert.c new file mode 100644 index 0000000..e25de90 --- /dev/null +++ b/common/t-convert.c @@ -0,0 +1,463 @@ +/* t-convert.c - Module test for convert.c + * Copyright (C) 2006, 2008 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + /*exit (1)*/; \ + } while(0) + + +static void +test_hex2bin (void) +{ + static const char *valid[] = { + "00112233445566778899aabbccddeeff11223344", + "00112233445566778899AABBCCDDEEFF11223344", + "00112233445566778899AABBCCDDEEFF11223344 blah", + "00112233445566778899AABBCCDDEEFF11223344\tblah", + "00112233445566778899AABBCCDDEEFF11223344\nblah", + NULL + }; + static const char *invalid[] = { + "00112233445566778899aabbccddeeff1122334", + "00112233445566778899AABBCCDDEEFF1122334", + "00112233445566778899AABBCCDDEEFG11223344", + "00 112233445566778899aabbccddeeff11223344", + "00:112233445566778899aabbccddeeff11223344", + ":00112233445566778899aabbccddeeff11223344", + "0:0112233445566778899aabbccddeeff11223344", + "00112233445566778899aabbccddeeff11223344:", + "00112233445566778899aabbccddeeff112233445", + "00112233445566778899aabbccddeeff1122334455", + "00112233445566778899aabbccddeeff11223344blah", + NULL + }; + static const char *valid2[] = { + "00", + "00 x", + NULL + }; + static const char *invalid2[] = { + "", + "0", + "00:", + "00x", + " 00", + NULL + }; + unsigned char buffer[20]; + int len; + int i; + + + for (i=0; valid[i]; i++) + { + len = hex2bin (valid[i], buffer, sizeof buffer); + if (len < 0) + fail (i); + if (memcmp (buffer, ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44"), 20)) + fail (i); + } + if (hex2bin (valid[0], buffer, sizeof buffer) != 40) + fail (0); + if (hex2bin (valid[2], buffer, sizeof buffer) != 41) + fail (0); + + for (i=0; invalid[i]; i++) + { + len = hex2bin (invalid[i], buffer, sizeof buffer); + if (!(len < 0)) + fail (i); + } + + for (i=0; valid2[i]; i++) + { + len = hex2bin (valid2[i], buffer, 1); + if (len < 0) + fail (i); + if (memcmp (buffer, "\x00", 1)) + fail (i); + } + if (hex2bin (valid2[0], buffer, 1) != 2) + fail (0); + if (hex2bin (valid2[1], buffer, 1) != 3) + fail (0); + + for (i=0; invalid2[i]; i++) + { + len = hex2bin (invalid2[i], buffer, 1); + if (!(len < 0)) + fail (i); + } +} + + + +static void +test_hexcolon2bin (void) +{ + static const char *valid[] = { + "00112233445566778899aabbccddeeff11223344", + "00112233445566778899AABBCCDDEEFF11223344", + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00112233445566778899AABBCCDDEEFF11223344 blah", + "00112233445566778899AABBCCDDEEFF11223344\tblah", + "00112233445566778899AABBCCDDEEFF11223344\nblah", + NULL + }; + static const char *invalid[] = { + "00112233445566778899aabbccddeeff1122334", + "00112233445566778899AABBCCDDEEFF1122334", + "00112233445566778899AABBCCDDEEFG11223344", + ":00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44:", + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:3344", + "00:1122:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "0011:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00 11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00:11 22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00112233445566778899aabbccddeeff112233445", + "00112233445566778899aabbccddeeff1122334455", + "00112233445566778899aabbccddeeff11223344blah", + NULL + }; + static const char *valid2[] = { + "00", + "00 x", + NULL + }; + static const char *invalid2[] = { + "", + "0", + "00:", + ":00", + "0:0", + "00x", + " 00", + NULL + }; + unsigned char buffer[20]; + int len; + int i; + + + for (i=0; valid[i]; i++) + { + len = hexcolon2bin (valid[i], buffer, sizeof buffer); + if (len < 0) + fail (i); + if (memcmp (buffer, ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44"), 20)) + fail (i); + } + if (hexcolon2bin (valid[0], buffer, sizeof buffer) != 40) + fail (0); + if (hexcolon2bin (valid[3], buffer, sizeof buffer) != 41) + fail (0); + + for (i=0; invalid[i]; i++) + { + len = hexcolon2bin (invalid[i], buffer, sizeof buffer); + if (!(len < 0)) + fail (i); + } + + for (i=0; valid2[i]; i++) + { + len = hexcolon2bin (valid2[i], buffer, 1); + if (len < 0) + fail (i); + if (memcmp (buffer, "\x00", 1)) + fail (i); + } + if (hexcolon2bin (valid2[0], buffer, 1) != 2) + fail (0); + if (hexcolon2bin (valid2[1], buffer, 1) != 3) + fail (0); + + for (i=0; invalid2[i]; i++) + { + len = hexcolon2bin (invalid2[i], buffer, 1); + if (!(len < 0)) + fail (i); + } + + +} + + + +static void +test_bin2hex (void) +{ + char stuff[20+1] = ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x01\x10\x02\xa3"); + char hexstuff[] = "00112233445566778899AABBCCDDEEFF011002A3"; + char buffer[2*20+1]; + char *p; + + p = bin2hex (stuff, 20, buffer); + if (!p) + fail (0); + if (p != buffer) + fail (0); + if (strcmp (buffer, hexstuff)) + fail (0); + + p = bin2hex (stuff, 20, NULL); + if (!p) + fail (0); + else if (strcmp (p, hexstuff)) + fail (0); + xfree (p); + + p = bin2hex (stuff, (size_t)(-1), NULL); + if (p) + fail (0); + else if (errno != ENOMEM) + fail (1); +} + + +static void +test_bin2hexcolon (void) +{ + char stuff[20+1] = ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x01\x10\x02\xa3"); + char hexstuff[] = ("00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF" + ":01:10:02:A3"); + char buffer[3*20+1]; + char *p; + + p = bin2hexcolon (stuff, 20, buffer); + if (!p) + fail (0); + if (p != buffer) + fail (0); + if (strcmp (buffer, hexstuff)) + fail (0); + + p = bin2hexcolon (stuff, 20, NULL); + if (!p) + fail (0); + else if (strcmp (p, hexstuff)) + fail (0); + xfree (p); + + p = bin2hexcolon (stuff, (size_t)(-1), NULL); + if (p) + fail (0); + else if (errno != ENOMEM) + fail (1); +} + + + +static void +test_hex2str (void) +{ + static struct { + const char *hex; + const char *str; + int len; /* Length of STR. This may included embedded nuls. */ + int off; + int no_alloc_test; + } tests[] = { + /* Simple tests. */ + { "112233445566778899aabbccddeeff1122", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + { "112233445566778899aabbccddeeff1122 blah", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + { "112233445566778899aabbccddeeff1122\tblah", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + { "112233445566778899aabbccddeeff1122\nblah", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + /* Valid tests yielding an empty string. */ + { "00", + "", + 1, 2 }, + { "00 x", + "", + 1, 2 }, + { "", + "", + 0, 0 }, + { " ", + "", + 0, 0 }, + /* Test trailing Nul feature. */ + { "112233445566778899aabbccddeeff1100", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x00", + 17, 34 }, + { "112233445566778899aabbccddeeff1100 ", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x00", + 17, 34 }, + /* Test buffer size. (buffer is of length 20) */ + { "6162636465666768696A6b6c6D6e6f70717273", + "abcdefghijklmnopqrs", + 19, 38 }, + { "6162636465666768696A6b6c6D6e6f7071727300", + "abcdefghijklmnopqrs", + 20, 40 }, + { "6162636465666768696A6b6c6D6e6f7071727374", + NULL, + 0, 0, 1 }, + { "6162636465666768696A6b6c6D6e6f707172737400", + NULL, + 0, 0, 1 }, + { "6162636465666768696A6b6c6D6e6f707172737475", + NULL, + 0, 0, 1 }, + + /* Invalid tests. */ + { "112233445566778899aabbccddeeff1122334", NULL, 0, 0 }, + { "112233445566778899AABBCCDDEEFF1122334", NULL, 0, 0 }, + { "112233445566778899AABBCCDDEEFG11223344", NULL, 0, 0 }, + { "0:0112233445566778899aabbccddeeff11223344", NULL, 0, 0 }, + { "112233445566778899aabbccddeeff11223344:", NULL, 0, 0 }, + { "112233445566778899aabbccddeeff112233445", NULL, 0, 0 }, + { "112233445566778899aabbccddeeff1122334455", NULL, 0, 0, 1 }, + { "112233445566778899aabbccddeeff11223344blah", NULL, 0, 0 }, + { "0", NULL, 0, 0 }, + { "00:", NULL, 0, 0 }, + { "00x", NULL, 0, 0 }, + + { NULL, NULL, 0, 0 } + }; + + int idx; + char buffer[20]; + const char *tail; + size_t count; + char *result; + + for (idx=0; tests[idx].hex; idx++) + { + tail = hex2str (tests[idx].hex, buffer, sizeof buffer, &count); + if (tests[idx].str) + { + /* Good case test. */ + if (!tail) + fail (idx); + else if (strcmp (tests[idx].str, buffer)) + fail (idx); + else if (tail - tests[idx].hex != tests[idx].off) + fail (idx); + else if (tests[idx].len != count) + fail (idx); + } + else + { + /* Bad case test. */ + if (tail) + fail (idx); + } + } + + /* Same tests again using in-place conversion. */ + for (idx=0; tests[idx].hex; idx++) + { + char tmpbuf[100]; + + assert (strlen (tests[idx].hex)+1 < sizeof tmpbuf); + strcpy (tmpbuf, tests[idx].hex); + + /* Note: we still need to use 20 as buffer length because our + tests assume that. */ + tail = hex2str (tmpbuf, tmpbuf, 20, &count); + if (tests[idx].str) + { + /* Good case test. */ + if (!tail) + fail (idx); + else if (strcmp (tests[idx].str, tmpbuf)) + fail (idx); + else if (tail - tmpbuf != tests[idx].off) + fail (idx); + else if (tests[idx].len != count) + fail (idx); + } + else + { + /* Bad case test. */ + if (tail) + fail (idx); + if (strcmp (tmpbuf, tests[idx].hex)) + fail (idx); /* Buffer was modified. */ + } + } + + /* Test the allocation variant. */ + for (idx=0; tests[idx].hex; idx++) + { + if (tests[idx].no_alloc_test) + continue; + + result = hex2str_alloc (tests[idx].hex, &count); + if (tests[idx].str) + { + /* Good case test. */ + if (!result) + fail (idx); + else if (strcmp (tests[idx].str, result)) + fail (idx); + else if (count != tests[idx].off) + fail (idx); + } + else + { + /* Bad case test. */ + if (result) + fail (idx); + } + xfree (result); + } +} + + + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_hex2bin (); + test_hexcolon2bin (); + test_bin2hex (); + test_bin2hexcolon (); + test_hex2str (); + + return 0; +} diff --git a/common/t-exechelp.c b/common/t-exechelp.c new file mode 100644 index 0000000..cf967fc --- /dev/null +++ b/common/t-exechelp.c @@ -0,0 +1,188 @@ +/* t-exechelp.c - Module test for exechelp.c + * Copyright (C) 2009 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "exechelp.h" + +static int verbose; + + +static void +print_open_fds (int *array) +{ + int n; + + if (!verbose) + return; + + for (n=0; array[n] != -1; n++) + ; + printf ("open file descriptors: %d", n); + putchar (' '); + putchar (' '); + putchar ('('); + for (n=0; array[n] != -1; n++) + printf ("%d%s", array[n], array[n+1] == -1?"":" "); + putchar (')'); + putchar ('\n'); +} + + +static int * +xget_all_open_fds (void) +{ + int *array; + + array = get_all_open_fds (); + if (!array) + { + fprintf (stderr, "%s:%d: get_all_open_fds failed: %s\n", + __FILE__, __LINE__, strerror (errno)); + exit (1); + } + return array; +} + + +/* That is a very crude test. To do a proper test we would need to + fork a test process and best return information by some other means + than file descriptors. */ +static void +test_close_all_fds (void) +{ + int max_fd = get_max_fds (); + int *array; + int fd; + int initial_count, count, n; +#if 0 + char buffer[100]; + + snprintf (buffer, sizeof buffer, "/bin/ls -l /proc/%d/fd", (int)getpid ()); + system (buffer); +#endif + + if (verbose) + printf ("max. file descriptors: %d\n", max_fd); + array = xget_all_open_fds (); + print_open_fds (array); + for (initial_count=n=0; array[n] != -1; n++) + initial_count++; + free (array); + + /* Some dups to get more file descriptors and close one. */ + dup (1); + dup (1); + fd = dup (1); + dup (1); + close (fd); + + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + for (count=n=0; array[n] != -1; n++) + count++; + if (count != initial_count+3) + { + fprintf (stderr, "%s:%d: dup or close failed\n", + __FILE__, __LINE__); + exit (1); + } + free (array); + + /* Close the non standard ones. */ + close_all_fds (3, NULL); + + /* Get a list to check whether they are all closed. */ + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + for (count=n=0; array[n] != -1; n++) + count++; + if (count > initial_count) + { + fprintf (stderr, "%s:%d: not all files were closed\n", + __FILE__, __LINE__); + exit (1); + } + initial_count = count; + free (array); + + /* Now let's check the realloc we use. We do this and the next + tests only if we are allowed to open enought descriptors. */ + if (get_max_fds () > 32) + { + int except[] = { 20, 23, 24, -1 }; + + for (n=initial_count; n < 31; n++) + dup (1); + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + free (array); + for (n=0; n < 5; n++) + { + dup (1); + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + free (array); + } + + /* Check whether the except list works. */ + close_all_fds (3, except); + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + for (count=n=0; array[n] != -1; n++) + count++; + free (array); + + if (count != initial_count + DIM(except)-1) + { + fprintf (stderr, "%s:%d: close_all_fds failed\n", + __FILE__, __LINE__); + exit (1); + } + } + +} + + +int +main (int argc, char **argv) +{ + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_close_all_fds (); + + return 0; +} diff --git a/common/t-exectool.c b/common/t-exectool.c new file mode 100644 index 0000000..8b6ee6a --- /dev/null +++ b/common/t-exectool.c @@ -0,0 +1,223 @@ +/* t-exectool.c - Module test for exectool.c + * Copyright (C) 2016 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "exectool.h" + +static int verbose; + +#define fail(msg, err) \ + do { fprintf (stderr, "%s:%d: %s failed: %s\n", \ + __FILE__,__LINE__, (msg), gpg_strerror (err)); \ + exit (1); \ + } while(0) + +static void +test_executing_true (void) +{ + gpg_error_t err; + const char *argv[] = { "/bin/true", NULL }; + char *result; + size_t len; + + if (access (argv[0], X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s", + argv[0], strerror (errno)); + return; + } + + if (verbose) + fprintf (stderr, "Executing %s...\n", argv[0]); + + err = gnupg_exec_tool (argv[0], &argv[1], "", &result, &len); + if (err) + fail ("gnupg_exec_tool", err); + + assert (result); + assert (len == 0); + free (result); +} + +static void +test_executing_false (void) +{ + gpg_error_t err; + const char *argv[] = { "/bin/false", NULL }; + char *result; + size_t len; + + if (access (argv[0], X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s", + argv[0], strerror (errno)); + return; + } + + if (verbose) + fprintf (stderr, "Executing %s...\n", argv[0]); + + err = gnupg_exec_tool (argv[0], &argv[1], "", &result, &len); + assert (err == GPG_ERR_GENERAL); +} + +static void +test_executing_cat (const char *vector) +{ + gpg_error_t err; + const char *argv[] = { "/bin/cat", NULL }; + char *result; + size_t len; + + if (access (argv[0], X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s", + argv[0], strerror (errno)); + return; + } + + if (verbose) + fprintf (stderr, "Executing %s...\n", argv[0]); + + err = gnupg_exec_tool (argv[0], &argv[1], vector, &result, &len); + if (err) + fail ("gnupg_exec_tool", err); + + assert (result); + + /* gnupg_exec_tool returns the correct length... */ + assert (len == strlen (vector)); + /* ... but 0-terminates data for ease of use. */ + assert (result[len] == 0); + + assert (strcmp (result, vector) == 0); + free (result); +} + + +static void +test_catting_cat (void) +{ + gpg_error_t err; + const char *argv[] = { "/bin/cat", "/bin/cat", NULL }; + char *result; + size_t len; + estream_t in; + char *reference, *p; + size_t reference_len; + + if (access (argv[0], X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s", + argv[0], strerror (errno)); + return; + } + + in = es_fopen (argv[1], "r"); + if (in == NULL) + { + fprintf (stderr, "skipping test: could not open %s: %s", + argv[1], strerror (errno)); + return; + } + + err = es_fseek (in, 0L, SEEK_END); + if (err) + { + fprintf (stderr, "skipping test: could not seek in %s: %s", + argv[1], gpg_strerror (err)); + return; + } + + reference_len = es_ftell (in); + err = es_fseek (in, 0L, SEEK_SET); + assert (!err || !"rewinding failed"); + + reference = malloc (reference_len); + assert (reference || !"allocating reference buffer failed"); + + for (p = reference; p - reference < reference_len; ) + { + size_t bytes_read, left; + left = reference_len - (p - reference); + if (left > 4096) + left = 4096; + err = es_read (in, p, left, &bytes_read); + if (err) + { + fprintf (stderr, "error reading %s: %s", + argv[1], gpg_strerror (err)); + exit (1); + } + + p += bytes_read; + } + es_fclose (in); + + if (verbose) + fprintf (stderr, "Executing %s %s...\n", argv[0], argv[1]); + + err = gnupg_exec_tool (argv[0], &argv[1], "", &result, &len); + if (err) + fail ("gnupg_exec_tool", err); + + assert (result); + + /* gnupg_exec_tool returns the correct length... */ + assert (len == reference_len); + assert (memcmp (result, reference, reference_len) == 0); + free (reference); + free (result); +} + + +int +main (int argc, char **argv) +{ + int i; + char binjunk[256]; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_executing_true (); + test_executing_false (); + test_executing_cat ("Talking to myself here..."); + + for (i = 0; i < 255 /* one less */; i++) + binjunk[i] = i + 1; /* avoid 0 */ + binjunk[255] = 0; + + test_executing_cat (binjunk); + test_catting_cat (); + + return 0; +} diff --git a/common/t-gettime.c b/common/t-gettime.c new file mode 100644 index 0000000..9d9881a --- /dev/null +++ b/common/t-gettime.c @@ -0,0 +1,268 @@ +/* t-gettime.c - Module test for gettime.c + * Copyright (C) 2007, 2011 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 3 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, see . + */ + +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + } while(0) + +static int verbose; +static int errcount; +#define INVALID ((time_t)(-1)) + + +static void +test_isotime2epoch (void) +{ + struct { const char *string; time_t expected; } array [] = { + { "19700101T000001", 1 }, + { "19700101T235959", 86399 }, + { "19980815T143712", 903191832 }, + { "19700101T000000", 0 }, + { "19691231T235959", INVALID }, + { "19000101T000000", INVALID }, + { "", INVALID }, + { "19000101T00000", INVALID }, + { "20010101t123456", INVALID }, + { "20010101T123456", 978352496 }, + { "20070629T160000", 1183132800 }, + { "20070629T160000:", 1183132800 }, + { "20070629T160000,", 1183132800 }, + { "20070629T160000 ", 1183132800 }, + { "20070629T160000\n", 1183132800 }, + { "20070629T160000.", INVALID }, +#if SIZEOF_TIME_T > 4 + { "21060207T062815", (time_t)0x0ffffffff }, + { "21060207T062816", (time_t)0x100000000 }, + { "21060207T062817", (time_t)0x100000001 }, + { "21060711T120001", (time_t)4308292801 }, +#endif /*SIZEOF_TIME_T > 4*/ + { NULL, 0 } + }; + int idx; + time_t val; + gnupg_isotime_t tbuf; + + for (idx=0; array[idx].string; idx++) + { + val = isotime2epoch (array[idx].string); + if (val != array[idx].expected ) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' exp: %ld got: %ld\n", + array[idx].string, (long)array[idx].expected, + (long)val); + } + if (array[idx].expected != INVALID) + { + epoch2isotime (tbuf, val); + if (strlen (tbuf) != 15) + { + if (verbose) + fprintf (stderr, "string '%s', time-t %ld, revert: '%s'\n", + array[idx].string, (long)val, tbuf); + fail (idx); + } + if (strncmp (array[idx].string, tbuf, 15)) + fail (idx); + } + } +} + + + +static void +test_string2isotime (void) +{ + struct { + const char *string; + size_t result; + const char *expected; + } array [] = { + { "19700101T000001", 15, "19700101T000001" }, + { "19700101T235959", 15, "19700101T235959" }, + { "19980815T143712", 15, "19980815T143712" }, + { "19700101T000000", 15, "19700101T000000" }, + { "19691231T235959", 15, "19691231T235959" }, + { "19000101T000000", 15, "19000101T000000" }, + { "", 0, "" }, + { "19000101T00000", 0, "" }, + { "20010101t123456", 0, "" }, + { "20010101T123456", 15, "20010101T123456" }, + { "20070629T160000", 15, "20070629T160000" }, + { "20070629T160000:", 15, "20070629T160000" }, + { "20070629T160000,", 15, "20070629T160000" }, + { "20070629T160000 ", 15, "20070629T160000" }, + { "20070629T160000\n", 15,"20070629T160000" }, + { "20070629T160000.", 0, "" }, + { "1066-03-20", 10, "10660320T000000" }, + { "1066-03-20,", 10, "10660320T000000" }, + { "1066-03-20:", 0, "" }, + { "1066-03-20 00", 13, "10660320T000000" }, + { "1066-03-20 01", 13, "10660320T010000" }, + { "1066-03-20 23", 13, "10660320T230000" }, + { "1066-03-20 24", 0, "" }, + { "1066-03-20 00:", 0, "" }, + { "1066-03-20 00:3", 0, "" }, + { "1066-03-20 00:31", 16, "10660320T003100" }, + { "1066-03-20 00:31:47", 19, "10660320T003147" }, + { "1066-03-20 00:31:47 ", 19, "10660320T003147" }, + { "1066-03-20 00:31:47,", 19, "10660320T003147" }, + { "1066-03-20 00:31:47:", 0, "" }, + { "1-03-20 00:31:47:", 0, "" }, + { "10-03-20 00:31:47:", 0, "" }, + { "106-03-20 00:31:47:", 0, "" }, + { "1066-23-20 00:31:47:", 0, "" }, + { "1066-00-20 00:31:47:", 0, "" }, + { "1066-0-20 00:31:47:", 0, "" }, + { "1066-01-2 00:31:47:", 0, "" }, + { "1066-01-2 00:31:47:", 0, "" }, + { "1066-01-32 00:31:47:", 0, "" }, + { "1066-01-00 00:31:47:", 0, "" }, + { "1066-03-20 00:31:47:",11, "10660320T000000" }, + { "1066-03-2000:31:47:", 0, "" }, + { "10666-03-20 00:31:47:", 0, "" }, + { NULL, 0 } + }; + int idx; + size_t result; + gnupg_isotime_t tbuf; + + for (idx=0; array[idx].string; idx++) + { + result = string2isotime (tbuf, array[idx].string); + if (result != array[idx].result) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' expected: %d, got: %d\n", + array[idx].string, (int)array[idx].result, (int)result); + } + else if (result && strlen (tbuf) != 15) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' invalid isotime returned\n", + array[idx].string); + } + else if (result && strcmp (array[idx].expected, tbuf)) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' bad isotime '%s' returned\n", + array[idx].string, tbuf); + } + } +} + + +static void +test_isodate_human_to_tm (void) +{ + struct { + const char *string; + int okay; + int year, mon, mday; + } array [] = { + { "1970-01-01", 1, 1970, 1, 1 }, + { "1970-02-01", 1, 1970, 2, 1 }, + { "1970-12-31", 1, 1970, 12, 31 }, + { "1971-01-01", 1, 1971, 1, 1 }, + { "1998-08-15", 1, 1998, 8, 15 }, + { "2015-04-10", 1, 2015, 4, 10 }, + { "2015-04-10 11:30",1, 2015, 4, 10 }, + { "1969-12-31", 0, 0, 0, 0 }, + { "1900-01-01", 0, 0, 0, 0 }, + { "", 0, 0, 0, 0 }, + { "1970-12-32", 0, 0, 0, 0 }, + { "1970-13-01", 0, 0, 0, 0 }, + { "1970-01-00", 0, 0, 0, 0 }, + { "1970-00-01", 0, 0, 0, 0 }, + { "1970-00-01", 0, 0, 0, 0 }, + { "1970", 0, 0, 0, 0 }, + { "1970-01", 0, 0, 0, 0 }, + { "1970-01-1", 0, 0, 0, 0 }, + { "1970-1--01", 0, 0, 0, 0 }, + { "1970-01-01,", 1, 1970, 1, 1 }, + { "1970-01-01 ", 1, 1970, 1, 1 }, + { "1970-01-01\t", 1, 1970, 1, 1 }, + { "1970-01-01;", 0, 0, 0, 0 }, + { "1970-01-01:", 0, 0, 0, 0 }, + { "1970_01-01", 0, 0, 0, 0 }, + { "1970-01_01", 0, 0, 0, 0 }, + { NULL, 0 } + }; + int idx; + int okay; + struct tm tmbuf; + + for (idx=0; array[idx].string; idx++) + { + okay = !isodate_human_to_tm (array[idx].string, &tmbuf); + if (okay != array[idx].okay) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' expected: %d, got: %d\n", + array[idx].string, (int)array[idx].okay, okay); + } + else if (!okay) + ; + else if (tmbuf.tm_year + 1900 != array[idx].year + || tmbuf.tm_mon +1 != array[idx].mon + || tmbuf.tm_mday != array[idx].mday) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' returned %04d-%02d-%02d\n", + array[idx].string, + tmbuf.tm_year + 1900, tmbuf.tm_mon + 1, tmbuf.tm_mday); + } + else if (tmbuf.tm_sec || tmbuf.tm_min || tmbuf.tm_hour + || tmbuf.tm_isdst != -1) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' returned bad time part\n", + array[idx].string); + } + } +} + + +int +main (int argc, char **argv) +{ + if (argc > 1 && !strcmp (argv[1], "--verbose")) + verbose = 1; + + test_isotime2epoch (); + test_string2isotime (); + test_isodate_human_to_tm (); + + return !!errcount; +} diff --git a/common/t-helpfile.c b/common/t-helpfile.c new file mode 100644 index 0000000..0e2c79f --- /dev/null +++ b/common/t-helpfile.c @@ -0,0 +1,66 @@ +/* t-helpfile.c - Module test for helpfile.c + * Copyright (C) 2007 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 3 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, see . + */ + +#include +#include +#include + +#include "util.h" +#include "i18n.h" + +/* #define pass() do { ; } while(0) */ +/* #define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ */ +/* __FILE__,__LINE__, (a)); \ */ +/* errcount++; \ */ +/* } while(0) */ + +static int verbose; +static int errcount; + + + +int +main (int argc, char **argv) +{ + char *result; + + if (argc) + { argc--; argv++; } + i18n_init (); + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + result = gnupg_get_help_string (argc? argv[0]:NULL, 0); + if (!result) + { + fprintf (stderr, + "Error: nothing found for '%s'\n", argc?argv[0]:"(null)"); + errcount++; + } + else + { + printf ("key '%s' result='%s'\n", argc?argv[0]:"(null)", result); + xfree (result); + } + + return !!errcount; +} diff --git a/common/t-iobuf.c b/common/t-iobuf.c new file mode 100644 index 0000000..0e6f508 --- /dev/null +++ b/common/t-iobuf.c @@ -0,0 +1,392 @@ +#include +#include +#include +#include +#include + +#include "iobuf.h" +#include "stringhelp.h" + +/* Return every other byte. In particular, reads two bytes, returns + the second one. */ +static int +every_other_filter (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *len) +{ + (void) opaque; + + if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "every_other_filter", *len); + } + if (control == IOBUFCTRL_UNDERFLOW) + { + int c = iobuf_readbyte (chain); + int c2; + if (c == -1) + c2 = -1; + else + c2 = iobuf_readbyte (chain); + + /* printf ("Discarding %d (%c); return %d (%c)\n", c, c, c2, c2); */ + + if (c2 == -1) + { + *len = 0; + return -1; + } + + *buf = c2; + *len = 1; + + return 0; + } + + return 0; +} + +static int +double_filter (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *len) +{ + (void) opaque; + + if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "double_filter", *len); + } + if (control == IOBUFCTRL_FLUSH) + { + int i; + + for (i = 0; i < *len; i ++) + { + int rc; + + rc = iobuf_writebyte (chain, buf[i]); + if (rc) + return rc; + rc = iobuf_writebyte (chain, buf[i]); + if (rc) + return rc; + } + } + + return 0; +} + +struct content_filter_state +{ + int pos; + int len; + const char *buffer; +}; + +static struct content_filter_state * +content_filter_new (const char *buffer) +{ + struct content_filter_state *state + = malloc (sizeof (struct content_filter_state)); + + state->pos = 0; + state->len = strlen (buffer); + state->buffer = buffer; + + return state; +} + +static int +content_filter (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *len) +{ + struct content_filter_state *state = opaque; + + (void) chain; + + if (control == IOBUFCTRL_UNDERFLOW) + { + int remaining = state->len - state->pos; + int toread = *len; + assert (toread > 0); + + if (toread > remaining) + toread = remaining; + + memcpy (buf, &state->buffer[state->pos], toread); + + state->pos += toread; + + *len = toread; + + if (toread == 0) + return -1; + return 0; + } + + return 0; +} + +int +main (int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + /* A simple test to make sure filters work. We use a static buffer + and then add a filter in front of it that returns every other + character. */ + { + char *content = "0123456789abcdefghijklm"; + iobuf_t iobuf; + int c; + int n; + int rc; + + iobuf = iobuf_temp_with_content (content, strlen (content)); + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + + n = 0; + while ((c = iobuf_readbyte (iobuf)) != -1) + { + /* printf ("%d: %c\n", n + 1, (char) c); */ + assert (content[2 * n + 1] == c); + n ++; + } + /* printf ("Got EOF after reading %d bytes (content: %d)\n", */ + /* n, strlen (content)); */ + assert (n == strlen (content) / 2); + + iobuf_close (iobuf); + } + + /* A simple test to check buffering. Make sure that when we add a + filter to a pipeline, any buffered data gets processed by the */ + { + char *content = "0123456789abcdefghijklm"; + iobuf_t iobuf; + int c; + int n; + int rc; + int i; + + iobuf = iobuf_temp_with_content (content, strlen (content)); + + n = 0; + for (i = 0; i < 10; i ++) + { + c = iobuf_readbyte (iobuf); + assert (content[i] == c); + n ++; + } + + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + + while ((c = iobuf_readbyte (iobuf)) != -1) + { + /* printf ("%d: %c\n", n + 1, (char) c); */ + assert (content[2 * (n - 5) + 1] == c); + n ++; + } + assert (n == 10 + (strlen (content) - 10) / 2); + + iobuf_close (iobuf); + } + + + /* A simple test to check that iobuf_read_line works. */ + { + /* - 3 characters plus new line + - 4 characters plus new line + - 5 characters plus new line + - 5 characters, no new line + */ + char *content = "abc\ndefg\nhijkl\nmnopq"; + iobuf_t iobuf; + byte *buffer; + unsigned size; + unsigned max_len; + int n; + + iobuf = iobuf_temp_with_content (content, strlen(content)); + + /* We read a line with 3 characters plus a newline. If we + allocate a buffer that is 5 bytes long, then no reallocation + should be required. */ + size = 5; + buffer = malloc (size); + assert (buffer); + max_len = 100; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 4); + assert (strcmp (buffer, "abc\n") == 0); + assert (size == 5); + assert (max_len == 100); + free (buffer); + + /* We now read a line with 4 characters plus a newline. This + requires 6 bytes of storage. We pass a buffer that is 5 bytes + large and we allow the buffer to be grown. */ + size = 5; + buffer = malloc (size); + max_len = 100; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 5); + assert (strcmp (buffer, "defg\n") == 0); + assert (size >= 6); + /* The string shouldn't have been truncated (max_len == 0). */ + assert (max_len == 100); + free (buffer); + + /* We now read a line with 5 characters plus a newline. This + requires 7 bytes of storage. We pass a buffer that is 5 bytes + large and we don't allow the buffer to be grown. */ + size = 5; + buffer = malloc (size); + max_len = 5; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 4); + /* Note: the string should still have a trailing \n. */ + assert (strcmp (buffer, "hij\n") == 0); + assert (size == 5); + /* The string should have been truncated (max_len == 0). */ + assert (max_len == 0); + free (buffer); + + /* We now read a line with 6 characters without a newline. This + requires 7 bytes of storage. We pass a NULL buffer and we + don't allow the buffer to be grown larger than 5 bytes. */ + size = 5; + buffer = NULL; + max_len = 5; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 4); + /* Note: the string should still have a trailing \n. */ + assert (strcmp (buffer, "mno\n") == 0); + assert (size == 5); + /* The string should have been truncated (max_len == 0). */ + assert (max_len == 0); + free (buffer); + + iobuf_close (iobuf); + } + + { + /* - 10 characters, EOF + - 17 characters, EOF + */ + char *content = "abcdefghijklmnopq"; + char *content2 = "0123456789"; + iobuf_t iobuf; + int rc; + int c; + int n; + int lastc = 0; + struct content_filter_state *state; + + iobuf = iobuf_temp_with_content (content, strlen(content)); + rc = iobuf_push_filter (iobuf, + content_filter, + state=content_filter_new (content2)); + assert (rc == 0); + + n = 0; + while (1) + { + c = iobuf_readbyte (iobuf); + if (c == -1 && lastc == -1) + { + /* printf("Two EOFs in a row. Done.\n"); */ + assert (n == 27); + break; + } + + lastc = c; + + if (c == -1) + { + /* printf("After %d bytes, got EOF.\n", n); */ + assert (n == 10 || n == 27); + } + else + { + n ++; + /* printf ("%d: '%c' (%d)\n", n, c, c); */ + } + } + + iobuf_close (iobuf); + free (state); + } + + /* Write some data to a temporary filter. Push a new filter. The + already written data should not be processed by the new + filter. */ + { + iobuf_t iobuf; + int rc; + char *content = "0123456789"; + char *content2 = "abc"; + char buffer[4096]; + int n; + + iobuf = iobuf_temp (); + assert (iobuf); + + rc = iobuf_write (iobuf, content, strlen (content)); + assert (rc == 0); + + rc = iobuf_push_filter (iobuf, double_filter, NULL); + assert (rc == 0); + + /* Include a NUL. */ + rc = iobuf_write (iobuf, content2, strlen (content2) + 1); + assert (rc == 0); + + n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer)); +#if 0 + printf ("Got %d bytes\n", n); + printf ("buffer: `"); + fwrite (buffer, n, 1, stdout); + fputc ('\'', stdout); + fputc ('\n', stdout); +#endif + + assert (n == strlen (content) + 2 * (strlen (content2) + 1)); + assert (strcmp (buffer, "0123456789aabbcc") == 0); + + iobuf_close (iobuf); + } + + { + iobuf_t iobuf; + int rc; + char *content = "0123456789"; + int n; + int c; + char buffer[strlen (content)]; + + iobuf = iobuf_temp_with_content (content, strlen (content)); + assert (iobuf); + + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + + for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++) + { + /* printf ("%d: `%c'\n", n, c); */ + buffer[n] = c; + } + + assert (n == 2); + assert (buffer[0] == '3'); + assert (buffer[1] == '7'); + + iobuf_close (iobuf); + } + + return 0; +} diff --git a/common/t-mapstrings.c b/common/t-mapstrings.c new file mode 100644 index 0000000..0856c3c --- /dev/null +++ b/common/t-mapstrings.c @@ -0,0 +1,100 @@ +/* t-mapstrings.c - Regression tests for mapstrings.c + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "t-support.h" +#include "stringhelp.h" + +static void +test_map_static_macro_string (void) +{ + static struct { + const char *string; + const char *expected; + const char *lastresult; + } tests[] = { + { "@GPG@ (@GNUPG@)", + GPG_NAME " (" GNUPG_NAME ")" }, + { "@GPG@(@GNUPG@)", + GPG_NAME "(" GNUPG_NAME ")" }, + { "@GPG@@GNUPG@", + GPG_NAME GNUPG_NAME }, + { " @GPG@@GNUPG@", + " " GPG_NAME GNUPG_NAME }, + { " @GPG@@GNUPG@ ", + " " GPG_NAME GNUPG_NAME " " }, + { " @GPG@GNUPG@ ", + " " GPG_NAME "GNUPG@ " }, + { " @ GPG@GNUPG@ ", + " @ GPG" GNUPG_NAME " " }, + { "--@GPGTAR@", + "--" GPGTAR_NAME } + }; + int testno; + const char *result; + + for (testno=0; testno < DIM(tests); testno++) + { + result = map_static_macro_string (tests[testno].string); + if (!result) + fail (testno); + else if (strcmp (result, tests[testno].expected)) + fail (testno); + if (!tests[testno].lastresult) + tests[testno].lastresult = result; + } + + /* A second time to check that the same string is been returned. */ + for (testno=0; testno < DIM(tests); testno++) + { + result = map_static_macro_string (tests[testno].string); + if (!result) + fail (testno); + else if (strcmp (result, tests[testno].expected)) + fail (testno); + if (result != tests[testno].lastresult) + fail (testno); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_map_static_macro_string (); + + return 0; +} diff --git a/common/t-mbox-util.c b/common/t-mbox-util.c new file mode 100644 index 0000000..979d4b3 --- /dev/null +++ b/common/t-mbox-util.c @@ -0,0 +1,105 @@ +/* t-mbox-util.c - Module test for mbox-util.c + * Copyright (C) 2015 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "mbox-util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +static void +run_test (void) +{ + static struct + { + const char *userid; + const char *mbox; + } testtbl[] = + { + { "Werner Koch ", "wk@gnupg.org" }, + { "", "wk@gnupg.org" }, + { "wk@gnupg.org", "wk@gnupg.org" }, + { "wk@gnupg.org ", NULL }, + { " wk@gnupg.org", NULL }, + { "Werner Koch (test) ", "wk@gnupg.org" }, + { "Werner Koch (test)", "wk@gnupg.org" }, + { "Werner Koch ", NULL }, + { "Werner Koch ", NULL }, + { "", "foo@example.org" }, + { "", "foo.@example.org" }, + { "<.foo.@example.org>", ".foo.@example.org" }, + { "", "foo..@example.org" }, + { "", "foo..bar@example.org" }, + { "", NULL }, + { "", NULL }, + { "", NULL }, + { "<@example.org>", NULL }, + { "", NULL }, + { "<@foo@example.org>", NULL }, + { " ()", "foo@example.org" }, + { " ()", "fo()o@example.org" }, + { " ()", "fo()o@example.org" }, + { "fo()o@example.org", NULL}, + { "Mr. Foo ", "foo@example.org"}, + { NULL, NULL } + }; + int idx; + + for (idx=0; testtbl[idx].userid; idx++) + { + char *mbox = mailbox_from_userid (testtbl[idx].userid); + + if (!testtbl[idx].mbox) + { + if (mbox) + fail (idx); + } + else if (!mbox) + fail (idx); + else if (strcmp (mbox, testtbl[idx].mbox)) + fail (idx); + + xfree (mbox); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + run_test (); + + return 0; +} diff --git a/common/t-name-value.c b/common/t-name-value.c new file mode 100644 index 0000000..57f685f --- /dev/null +++ b/common/t-name-value.c @@ -0,0 +1,593 @@ +/* t-name-value.c - Module test for name-value.c + * Copyright (C) 2016 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "name-value.h" + +static int verbose; +static int private_key_mode; + + +static nvc_t +my_nvc_new (void) +{ + if (private_key_mode) + return nvc_new_private_key (); + else + return nvc_new (); +} + + +void +test_getting_values (nvc_t pk) +{ + nve_t e; + + e = nvc_lookup (pk, "Comment:"); + assert (e); + + /* Names are case-insensitive. */ + e = nvc_lookup (pk, "comment:"); + assert (e); + e = nvc_lookup (pk, "COMMENT:"); + assert (e); + + e = nvc_lookup (pk, "SomeOtherName:"); + assert (e); +} + + +void +test_key_extraction (nvc_t pk) +{ + gpg_error_t err; + gcry_sexp_t key; + + if (private_key_mode) + { + err = nvc_get_private_key (pk, &key); + assert (err == 0); + assert (key); + + if (verbose) + gcry_sexp_dump (key); + + gcry_sexp_release (key); + } + else + { + err = nvc_get_private_key (pk, &key); + assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY); + } +} + + +void +test_iteration (nvc_t pk) +{ + int i; + nve_t e; + + i = 0; + for (e = nvc_first (pk); e; e = nve_next (e)) + i++; + assert (i == 4); + + i = 0; + for (e = nvc_lookup (pk, "Comment:"); + e; + e = nve_next_value (e, "Comment:")) + i++; + assert (i == 3); +} + + +void +test_whitespace (nvc_t pk) +{ + nve_t e; + + e = nvc_lookup (pk, "One:"); + assert (e); + assert (strcmp (nve_value (e), "WithoutWhitespace") == 0); + + e = nvc_lookup (pk, "Two:"); + assert (e); + assert (strcmp (nve_value (e), "With Whitespace") == 0); + + e = nvc_lookup (pk, "Three:"); + assert (e); + assert (strcmp (nve_value (e), + "Blank lines in continuations encode newlines.\n" + "Next paragraph.") == 0); +} + + +struct +{ + char *value; + void (*test_func) (nvc_t); +} tests[] = + { + { + "# This is a comment followed by an empty line\n" + "\n", + NULL, + }, + { + "# This is a comment followed by two empty lines, Windows style\r\n" + "\r\n" + "\r\n", + NULL, + }, + { + "# Some name,value pairs\n" + "Comment: Some comment.\n" + "SomeOtherName: Some value.\n", + test_getting_values, + }, + { + " # Whitespace is preserved as much as possible\r\n" + "Comment:Some comment.\n" + "SomeOtherName: Some value. \n", + test_getting_values, + }, + { + "# Values may be continued in the next line as indicated by leading\n" + "# space\n" + "Comment: Some rather long\n" + " comment that is continued in the next line.\n" + "\n" + " Blank lines with or without whitespace are allowed within\n" + " continuations to allow paragraphs.\n" + "SomeOtherName: Some value.\n", + test_getting_values, + }, + { + "# Names may be given multiple times forming an array of values\n" + "Comment: Some comment, element 0.\n" + "Comment: Some comment, element 1.\n" + "Comment: Some comment, element 2.\n" + "SomeOtherName: Some value.\n", + test_iteration, + }, + { + "# One whitespace at the beginning of a continuation is swallowed.\n" + "One: Without\n" + " Whitespace\n" + "Two: With\n" + " Whitespace\n" + "Three: Blank lines in continuations encode newlines.\n" + "\n" + " Next paragraph.\n", + test_whitespace, + }, + { + "Description: Key to sign all GnuPG released tarballs.\n" + " The key is actually stored on a smart card.\n" + "Use-for-ssh: yes\n" + "OpenSSH-cert: long base64 encoded string wrapped so that this\n" + " key file can be easily edited with a standard editor.\n" + "Key: (shadowed-private-key\n" + " (rsa\n" + " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n" + " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n" + " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n" + " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n" + " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n" + " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n" + " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n" + " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n" + " E186A02BA2497FDC5D1221#)\n" + " (e #00010001#)\n" + " (shadowed t1-v1\n" + " (#D2760001240102000005000011730000# OPENPGP.1)\n" + " )))\n", + test_key_extraction, + }, + }; + + +static char * +nvc_to_string (nvc_t pk) +{ + gpg_error_t err; + char *buf; + size_t len; + estream_t sink; + + sink = es_fopenmem (0, "rw"); + assert (sink); + + err = nvc_write (pk, sink); + assert (err == 0); + + len = es_ftell (sink); + buf = xmalloc (len+1); + assert (buf); + + es_fseek (sink, 0, SEEK_SET); + es_read (sink, buf, len, NULL); + buf[len] = 0; + + es_fclose (sink); + return buf; +} + + +void dummy_free (void *p) { (void) p; } +void *dummy_realloc (void *p, size_t s) { (void) s; return p; } + +void +run_tests (void) +{ + gpg_error_t err; + nvc_t pk; + + int i; + for (i = 0; i < DIM (tests); i++) + { + estream_t source; + char *buf; + size_t len; + + len = strlen (tests[i].value); + source = es_mopen (tests[i].value, len, len, + 0, dummy_realloc, dummy_free, "r"); + assert (source); + + if (private_key_mode) + err = nvc_parse_private_key (&pk, NULL, source); + else + err = nvc_parse (&pk, NULL, source); + assert (err == 0); + assert (pk); + + if (verbose) + { + err = nvc_write (pk, es_stderr); + assert (err == 0); + } + + buf = nvc_to_string (pk); + assert (memcmp (tests[i].value, buf, len) == 0); + + es_fclose (source); + xfree (buf); + + if (tests[i].test_func) + tests[i].test_func (pk); + + nvc_release (pk); + } +} + + +void +run_modification_tests (void) +{ + gpg_error_t err; + nvc_t pk; + gcry_sexp_t key; + char *buf; + + pk = my_nvc_new (); + assert (pk); + + nvc_set (pk, "Foo:", "Bar"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Bar\n") == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "Baz"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\n") == 0); + xfree (buf); + + nvc_set (pk, "Bar:", "Bazzel"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_add (pk, "Foo:", "Bar"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_add (pk, "DontExistYet:", "Bar"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n") + == 0); + xfree (buf); + + nvc_delete (pk, nvc_lookup (pk, "DontExistYet:")); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_delete (pk, nve_next_value (nvc_lookup (pk, "Foo:"), "Foo:")); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_delete (pk, nvc_lookup (pk, "Foo:")); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Bar: Bazzel\n") == 0); + xfree (buf); + + nvc_delete (pk, nvc_first (pk)); + buf = nvc_to_string (pk); + assert (strcmp (buf, "") == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "A really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: A really long value spanning across multiple" + " lines that has to be\n wrapped at a convenient space.\n") + == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "XA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: XA really long value spanning across multiple" + " lines that has to\n be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple" + " lines that has\n to be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines" + "thathastobewrappedataconvenientspacethatisnotthere."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat" + "hastobewrappedataco\n nvenientspacethatisnotthere.\n") + == 0); + xfree (buf); + nvc_release (pk); + + pk = my_nvc_new (); + assert (pk); + + err = gcry_sexp_build (&key, NULL, "(hello world)"); + assert (err == 0); + assert (key); + + if (private_key_mode) + { + err = nvc_set_private_key (pk, key); + assert (err == 0); + + buf = nvc_to_string (pk); + assert (strcmp (buf, "Key: (hello world)\n") == 0); + xfree (buf); + } + else + { + err = nvc_set_private_key (pk, key); + assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY); + } + gcry_sexp_release (key); + nvc_release (pk); +} + + +void +convert (const char *fname) +{ + gpg_error_t err; + estream_t source; + gcry_sexp_t key; + char *buf; + size_t buflen; + struct stat st; + nvc_t pk; + + source = es_fopen (fname, "rb"); + if (source == NULL) + goto leave; + + if (fstat (es_fileno (source), &st)) + goto leave; + + buflen = st.st_size; + buf = xtrymalloc (buflen+1); + assert (buf); + + if (es_fread (buf, buflen, 1, source) != 1) + goto leave; + + err = gcry_sexp_sscan (&key, NULL, buf, buflen); + if (err) + { + fprintf (stderr, "malformed s-expression in %s\n", fname); + exit (1); + } + + pk = my_nvc_new (); + assert (pk); + + err = nvc_set_private_key (pk, key); + assert (err == 0); + + err = nvc_write (pk, es_stdout); + assert (err == 0); + + return; + + leave: + perror (fname); + exit (1); +} + + +void +parse (const char *fname) +{ + gpg_error_t err; + estream_t source; + char *buf; + nvc_t pk_a, pk_b; + nve_t e; + int line; + + source = es_fopen (fname, "rb"); + if (source == NULL) + { + perror (fname); + exit (1); + } + + if (private_key_mode) + err = nvc_parse_private_key (&pk_a, &line, source); + else + err = nvc_parse (&pk_a, &line, source); + if (err) + { + fprintf (stderr, "failed to parse %s line %d: %s\n", + fname, line, gpg_strerror (err)); + exit (1); + } + + buf = nvc_to_string (pk_a); + xfree (buf); + + pk_b = my_nvc_new (); + assert (pk_b); + + for (e = nvc_first (pk_a); e; e = nve_next (e)) + { + gcry_sexp_t key = NULL; + + if (private_key_mode && !strcasecmp (nve_name (e), "Key:")) + { + err = nvc_get_private_key (pk_a, &key); + if (err) + key = NULL; + } + + if (key) + { + err = nvc_set_private_key (pk_b, key); + assert (err == 0); + } + else + { + err = nvc_add (pk_b, nve_name (e), nve_value (e)); + assert (err == 0); + } + } + + buf = nvc_to_string (pk_b); + if (verbose) + fprintf (stdout, "%s", buf); + xfree (buf); +} + + +void +print_usage (void) +{ + fprintf (stderr, + "usage: t-private-keys [--verbose]" + " [--convert " + " || --parse-key " + " || --parse ]\n"); + exit (2); +} + + +int +main (int argc, char **argv) +{ + enum { TEST, CONVERT, PARSE, PARSEKEY } command = TEST; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + if (argc && !strcmp (argv[0], "--convert")) + { + command = CONVERT; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + if (argc && !strcmp (argv[0], "--parse-key")) + { + command = PARSEKEY; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + if (argc && !strcmp (argv[0], "--parse")) + { + command = PARSE; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + switch (command) + { + case TEST: + run_tests (); + run_modification_tests (); + private_key_mode = 1; + run_tests (); + run_modification_tests (); + break; + + case CONVERT: + convert (*argv); + break; + + case PARSEKEY: + private_key_mode = 1; + parse (*argv); + break; + + case PARSE: + parse (*argv); + break; + } + + return 0; +} diff --git a/common/t-openpgp-oid.c b/common/t-openpgp-oid.c new file mode 100644 index 0000000..cb5709d --- /dev/null +++ b/common/t-openpgp-oid.c @@ -0,0 +1,238 @@ +/* t-openpgp-oid.c - Module test for openpgp-oid.c + * Copyright (C) 2011 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a,e) \ + do { fprintf (stderr, "%s:%d: test %d failed (%s)\n", \ + __FILE__,__LINE__, (a), gpg_strerror (e)); \ + exit (1); \ + } while(0) + + +#define BADOID "1.3.6.1.4.1.11591.2.12242973" + + +static int verbose; + + + +static void +test_openpgp_oid_from_str (void) +{ + static char *sample_oids[] = + { + "0.0", + "1.0", + "1.2.3", + "1.2.840.10045.3.1.7", + "1.3.132.0.34", + "1.3.132.0.35", + NULL + }; + gpg_error_t err; + gcry_mpi_t a; + int idx; + char *string; + unsigned char *p; + unsigned int nbits; + size_t length; + + err = openpgp_oid_from_str ("", &a); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (0, err); + gcry_mpi_release (a); + + err = openpgp_oid_from_str (".", &a); + if (gpg_err_code (err) != GPG_ERR_INV_OID_STRING) + fail (0, err); + gcry_mpi_release (a); + + err = openpgp_oid_from_str ("0", &a); + if (gpg_err_code (err) != GPG_ERR_INV_OID_STRING) + fail (0, err); + gcry_mpi_release (a); + + for (idx=0; sample_oids[idx]; idx++) + { + err = openpgp_oid_from_str (sample_oids[idx], &a); + if (err) + fail (idx, err); + + string = openpgp_oid_to_str (a); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, sample_oids[idx])) + fail (idx, 0); + xfree (string); + + p = gcry_mpi_get_opaque (a, &nbits); + length = (nbits+7)/8; + if (!p || !length || p[0] != length - 1) + fail (idx, 0); + + gcry_mpi_release (a); + } + +} + + +static void +test_openpgp_oid_to_str (void) +{ + static struct { + const char *string; + unsigned char der[10]; + } samples[] = { + { "1.2.840.10045.3.1.7", + {8, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }}, + + { "1.3.132.0.34", + {5, 0x2B, 0x81, 0x04, 0x00, 0x22 }}, + + { "1.3.132.0.35", + { 5, 0x2B, 0x81, 0x04, 0x00, 0x23 }}, + + { BADOID, + { 9, 0x80, 0x02, 0x70, 0x50, 0x25, 0x46, 0xfd, 0x0c, 0xc0 }}, + + { BADOID, + { 1, 0x80 }}, + + { NULL }}; + gcry_mpi_t a; + int idx; + char *string; + unsigned char *p; + + for (idx=0; samples[idx].string; idx++) + { + p = xmalloc (samples[idx].der[0]+1); + memcpy (p, samples[idx].der, samples[idx].der[0]+1); + a = gcry_mpi_set_opaque (NULL, p, (samples[idx].der[0]+1)*8); + if (!a) + fail (idx, gpg_error_from_syserror ()); + + string = openpgp_oid_to_str (a); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, samples[idx].string)) + fail (idx, 0); + xfree (string); + gcry_mpi_release (a); + } + +} + + +static void +test_openpgp_oid_is_ed25519 (void) +{ + static struct + { + int yes; + const char *oidstr; + } samples[] = { + { 0, "0.0" }, + { 0, "1.3.132.0.35" }, + { 0, "1.3.6.1.4.1.3029.1.5.0" }, + { 0, "1.3.6.1.4.1.3029.1.5.1" }, /* Used during Libgcrypt development. */ + { 0, "1.3.6.1.4.1.3029.1.5.2" }, + { 0, "1.3.6.1.4.1.3029.1.5.1.0" }, + { 0, "1.3.6.1.4.1.3029.1.5" }, + { 0, "1.3.6.1.4.1.11591.15.0" }, + { 1, "1.3.6.1.4.1.11591.15.1" }, /* Your the one we want. */ + { 0, "1.3.6.1.4.1.11591.15.2" }, + { 0, "1.3.6.1.4.1.11591.15.1.0" }, + { 0, "1.3.6.1.4.1.11591.15" }, + { 0, NULL }, + }; + gpg_error_t err; + gcry_mpi_t a; + int idx; + + for (idx=0; samples[idx].oidstr; idx++) + { + err = openpgp_oid_from_str (samples[idx].oidstr, &a); + if (err) + fail (idx, err); + + if (openpgp_oid_is_ed25519 (a) != samples[idx].yes) + fail (idx, 0); + + gcry_mpi_release (a); + } + +} + + +static void +test_openpgp_enum_curves (void) +{ + int iter = 0; + const char *name; + int p256 = 0; + int p384 = 0; + int p521 = 0; + + while ((name = openpgp_enum_curves (&iter))) + { + if (verbose) + printf ("curve: %s\n", name); + if (!strcmp (name, "nistp256")) + p256++; + else if (!strcmp (name, "nistp384")) + p384++; + else if (!strcmp (name, "nistp521")) + p521++; + } + + if (p256 != 1 || p384 != 1 || p521 != 1) + { + /* We can only check the basic RFC-6637 requirements. */ + fputs ("standard ECC curve missing\n", stderr); + exit (1); + } +} + + +int +main (int argc, char **argv) +{ + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_openpgp_oid_from_str (); + test_openpgp_oid_to_str (); + test_openpgp_oid_is_ed25519 (); + test_openpgp_enum_curves (); + + return 0; +} diff --git a/common/t-percent.c b/common/t-percent.c new file mode 100644 index 0000000..145a89b --- /dev/null +++ b/common/t-percent.c @@ -0,0 +1,114 @@ +/* t-percent.c - Module test for percent.c + * Copyright (C) 2008 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + +static void +test_percent_plus_escape (void) +{ + static struct { + const char *string; + const char *expect; + } tbl[] = { + { + "", + "" + }, { + "a", + "a", + }, { + " ", + "+", + }, { + " ", + "++" + }, { + "+ +", + "%2B+%2B" + }, { + "\" \"", + "%22+%22" + }, { + "%22", + "%2522" + }, { + "%% ", + "%25%25+" + }, { + "\n ABC\t", + "%0A+ABC%09" + }, { NULL, NULL } + }; + char *buf, *buf2; + int i; + size_t len; + + for (i=0; tbl[i].string; i++) + { + buf = percent_plus_escape (tbl[i].string); + if (!buf) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf, tbl[i].expect)) + fail (i); + buf2 = percent_plus_unescape (buf, 0); + if (!buf2) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf2, tbl[i].string)) + fail (i); + xfree (buf2); + /* Now test the inplace conversion. */ + len = percent_plus_unescape_inplace (buf, 0); + buf[len] = 0; + if (strcmp (buf, tbl[i].string)) + fail (i); + xfree (buf); + } +} + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + /* FIXME: We escape_unescape is not tested - only + percent_plus_unescape. */ + test_percent_plus_escape (); + + return 0; +} diff --git a/common/t-recsel.c b/common/t-recsel.c new file mode 100644 index 0000000..f52d085 --- /dev/null +++ b/common/t-recsel.c @@ -0,0 +1,438 @@ +/* t-recsel.c - Module test for recsel.c + * Copyright (C) 2016 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "init.h" +#include "recsel.h" + +#define PGM "t-recsel" + +#define pass() do { ; } while(0) +#define fail(a,e) do { log_error ("line %d: test %d failed: %s\n", \ + __LINE__, (a), gpg_strerror ((e))); \ + exit (1); \ + } while(0) + +static int verbose; +static int debug; + + +#define FREEEXPR() do { recsel_release (se); se = NULL; } while (0) +#define ADDEXPR(a) do { \ + err = recsel_parse_expr (&se, (a)); \ + if (err) \ + fail (0, err); \ + } while (0) + + +static const char * +test_1_getval (void *cookie, const char *name) +{ + if (strcmp (name, "uid")) + fail (0, 0); + return cookie; +} + +static void +run_test_1 (void) +{ + static const char *expr[] = { + "uid =~ Alfa", + "&& uid !~ Test ", + "|| uid =~ Alpha", + " uid !~ Test" + }; + gpg_error_t err; + recsel_expr_t se = NULL; + int i; + + for (i=0; i < DIM (expr); i++) + { + err = recsel_parse_expr (&se, expr[i]); + if (err) + fail (i, err); + } + + if (debug) + recsel_dump (se); + + /* The example from recsel.c in several variants. */ + if (!recsel_select (se, test_1_getval, "Alfa")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "Alpha")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alfa Test")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alpha Test")) + fail (0, 0); + + /* Some modified versions from above. */ + if (!recsel_select (se, test_1_getval, " AlfA Tes")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " AlfA Tes ")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " Tes AlfA")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "TesAlfA")) + fail (0, 0); + + /* Simple cases. */ + if (recsel_select (se, NULL, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, "")) + fail (0, 0); + + FREEEXPR(); +} + + +/* Same as test1 but using a combined expression.. */ +static void +run_test_1b (void) +{ + gpg_error_t err; + recsel_expr_t se = NULL; + + err = recsel_parse_expr + (&se, "uid =~ Alfa && uid !~ Test || uid =~ Alpha && uid !~ Test" ); + if (err) + fail (0, err); + + if (debug) + recsel_dump (se); + + /* The example from recsel.c in several variants. */ + if (!recsel_select (se, test_1_getval, "Alfa")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "Alpha")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alfa Test")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alpha Test")) + fail (0, 0); + + /* Some modified versions from above. */ + if (!recsel_select (se, test_1_getval, " AlfA Tes")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " AlfA Tes ")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " Tes AlfA")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "TesAlfA")) + fail (0, 0); + + /* Simple cases. */ + if (recsel_select (se, NULL, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, "")) + fail (0, 0); + + FREEEXPR(); +} + + +static const char * +test_2_getval (void *cookie, const char *name) +{ + if (!strcmp (name, "uid")) + return "foo@example.org"; + else if (!strcmp (name, "keyid")) + return "0x12345678"; + else if (!strcmp (name, "zero")) + return "0"; + else if (!strcmp (name, "one")) + return "1"; + else if (!strcmp (name, "blanks")) + return " "; + else if (!strcmp (name, "letters")) + return "abcde"; + else if (!strcmp (name, "str1")) + return "aaa"; + else + return cookie; +} + +static void +run_test_2 (void) +{ + gpg_error_t err; + recsel_expr_t se = NULL; + + ADDEXPR ("uid = foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid = Foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("-c uid = Foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("uid =~ foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid =~ Foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("-c uid =~ Foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("uid !~ foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid !~ Foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("-c uid !~ Foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("uid =~ @"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid =~ @"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("keyid == 0x12345678"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid != 0x12345678"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid >= 0x12345678"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid <= 0x12345678"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid > 0x12345677"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid < 0x12345679"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("keyid > 0x12345678"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid < 0x12345678"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); + ADDEXPR ("str1 -gt aa"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -gt aaa"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -ge aaa"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -lt aab"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -le aaa"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("-c str1 -lt AAB"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -lt AAB"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); + ADDEXPR ("uid -n"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid -z"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("nothing -z"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("nothing -n"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("blanks -n"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("blanks -z"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("letters -n"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("letters -z"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); + ADDEXPR ("nothing -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("nothing -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("zero -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("zero -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("one -t"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("one -f"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("blanks -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("blanks -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("letter -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("letters -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); +} + + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX); + init_common_subsystems (&argc, &argv); + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [options]\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n", + stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + log_error ("unknown option '%s'\n", *argv); + exit (2); + } + } + + run_test_1 (); + run_test_1b (); + run_test_2 (); + /* Fixme: We should add test for complex conditions. */ + + return 0; +} diff --git a/common/t-session-env.c b/common/t-session-env.c new file mode 100644 index 0000000..aa9d596 --- /dev/null +++ b/common/t-session-env.c @@ -0,0 +1,304 @@ +/* t-session-env.c - Module test for session-env.c + * Copyright (C) 2009 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 3 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, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "session-env.h" + +#define pass() do { ; } while(0) +#define fail(e) do { fprintf (stderr, "%s:%d: function failed: %s\n", \ + __FILE__,__LINE__, gpg_strerror (e)); \ + exit (1); \ + } while(0) + +static int verbose; + +static void +listall (session_env_t se) +{ + int iterator = 0; + const char *name, *value; + int def; + + if (verbose) + printf ("environment of %p\n", se); + while ( (name = session_env_listenv (se, &iterator, &value, &def)) ) + if (verbose) + printf (" %s%s=%s\n", def? "[def] ":" ", name, value); + +} + + +static void +show_stdnames (void) +{ + const char *name, *assname; + int iterator = 0; + int count; + + printf (" > Known envvars:"); + count = 20; + while ((name = session_env_list_stdenvnames (&iterator, &assname))) + { + if (count > 60) + { + printf ("\n >"); + count = 7; + } + printf ( " %s", name); + count += strlen (name) + 1; + if (assname) + { + printf ( "(%s)", assname); + count += strlen (assname) + 2; + } + } + putchar('\n'); +} + + +static void +test_all (void) +{ + gpg_error_t err; + session_env_t se_0, se; + const char *s, *s2; + int idx; + + se_0 = session_env_new (); + if (!se_0) + fail (gpg_error_from_syserror ()); + se = session_env_new (); + if (!se) + fail (gpg_error_from_syserror ()); + + err = session_env_putenv (se, NULL); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (err); + err = session_env_putenv (se, ""); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (err); + err = session_env_putenv (se, "="); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (err); + + /* Delete some nonexistant variables. */ + err = session_env_putenv (se, "A"); + if (err) + fail (err); + err = session_env_putenv (se, "a"); + if (err) + fail (err); + err = session_env_putenv (se, "_aaaa aaaaaasssssssssssss\nddd"); + if (err) + fail (err); + + /* Create a few variables. */ + err = session_env_putenv (se, "EMPTY="); + if (err) + fail (err); + err = session_env_putenv (se, "foo=value_of_foo"); + if (err) + fail (err); + err = session_env_putenv (se, "bar=the value_of_bar"); + if (err) + fail (err); + err = session_env_putenv (se, "baz=this-is-baz"); + if (err) + fail (err); + err = session_env_putenv (se, "BAZ=this-is-big-baz"); + if (err) + fail (err); + + listall (se); + + /* Update one. */ + err = session_env_putenv (se, "baz=this-is-another-baz"); + if (err) + fail (err); + + listall (se); + + /* Delete one. */ + err = session_env_putenv (se, "bar"); + if (err) + fail (err); + + listall (se); + + /* Insert a new one. */ + err = session_env_putenv (se, "FOO=value_of_foo"); + if (err) + fail (err); + + listall (se); + + /* Retrieve a default one. */ + s = session_env_getenv_or_default (se, "HOME", NULL); + if (!s) + { + fprintf (stderr, "failed to get default of HOME\n"); + exit (1); + } + + s = session_env_getenv (se, "HOME"); + if (s) + fail(0); /* This is a default value, thus we should not see it. */ + + s = session_env_getenv_or_default (se, "HOME", NULL); + if (!s) + fail(0); /* But here we should see it. */ + + /* Add a few more. */ + err = session_env_putenv (se, "X1=A value"); + if (err) + fail (err); + err = session_env_putenv (se, "X2=Another value"); + if (err) + fail (err); + err = session_env_putenv (se, "X3=A value"); + if (err) + fail (err); + + listall (se); + + /* Check that we can overwrite a default value. */ + err = session_env_putenv (se, "HOME=/this/is/my/new/home"); + if (err) + fail (err); + /* And that we get this string back. */ + s = session_env_getenv (se, "HOME"); + if (!s) + fail (0); + if (strcmp (s, "/this/is/my/new/home")) + fail (0); + /* A new get default should return the very same string. */ + s2 = session_env_getenv_or_default (se, "HOME", NULL); + if (!s2) + fail (0); + if (s2 != s) + fail (0); + + listall (se); + + /* Check that the other object is clean. */ + { + int iterator = 0; + + if (session_env_listenv (se_0, &iterator, NULL, NULL)) + fail (0); + } + + + session_env_release (se); + + /* Use a new session for quick mass test. */ + se = session_env_new (); + if (!se) + fail (gpg_error_from_syserror ()); + + /* Create. */ + for (idx=0; idx < 500; idx++) + { + char buf[100]; + + snprintf (buf, sizeof buf, "FOO_%d=Value for %x", idx, idx); + err = session_env_putenv (se, buf); + if (err) + fail (err); + } + err = session_env_setenv (se, "TEST1", "value1"); + if (err) + fail (err); + err = session_env_setenv (se, "TEST1", "value1-updated"); + if (err) + fail (err); + + listall (se); + + /* Delete all. */ + for (idx=0; idx < 500; idx++) + { + char buf[100]; + + snprintf (buf, sizeof buf, "FOO_%d", idx); + err = session_env_putenv (se, buf); + if (err) + fail (err); + } + err = session_env_setenv (se, "TEST1", NULL); + if (err) + fail (err); + + /* Check that all are deleted. */ + { + int iterator = 0; + + if (session_env_listenv (se, &iterator, NULL, NULL)) + fail (0); + } + + /* Add a few strings again. */ + for (idx=0; idx < 500; idx++) + { + char buf[100]; + + if (!(idx % 10)) + { + if ( !(idx % 3)) + snprintf (buf, sizeof buf, "FOO_%d=", idx); + else + snprintf (buf, sizeof buf, "FOO_%d=new value for %x", idx, idx); + err = session_env_putenv (se, buf); + if (err) + fail (err); + } + } + + listall (se); + + session_env_release (se); + + session_env_release (se_0); +} + + + +int +main (int argc, char **argv) +{ + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + + show_stdnames (); + test_all (); + + return 0; +} diff --git a/common/t-sexputil.c b/common/t-sexputil.c new file mode 100644 index 0000000..ceb8280 --- /dev/null +++ b/common/t-sexputil.c @@ -0,0 +1,191 @@ +/* t-sexputil.c - Module test for sexputil.c + * Copyright (C) 2007 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 3 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, see . + */ + +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +static void +test_hash_algo_from_sigval (void) +{ + int algo; + /* A real world example. */ + unsigned char example1_rsa_sha1[] = + ("\x28\x37\x3A\x73\x69\x67\x2D\x76\x61\x6C\x28\x33\x3A\x72\x73\x61" + "\x28\x31\x3A\x73\x31\x32\x38\x3A\x17\xD2\xE9\x5F\xB4\x24\xD4\x1E" + "\x8C\xEE\x94\xDA\x41\x42\x1F\x26\x5E\xF4\x6D\xEC\x5B\xBD\x5B\x89" + "\x7A\x69\x11\x43\xE9\xD2\x23\x21\x25\x64\xA6\xB0\x56\xEF\xB4\xE9" + "\x06\xB2\x44\xF6\x80\x1E\xFF\x41\x23\xEB\xC9\xFA\xFD\x09\xBF\x9C" + "\x8E\xCF\x7F\xC3\x7F\x3A\x40\x48\x89\xDC\xBA\xB7\xDB\x9E\xF1\xBA" + "\x7C\x08\xEA\x74\x1D\x49\xE7\x65\xEF\x67\x79\xBC\x23\xD9\x49\xCD" + "\x05\x99\xD3\xD8\xB7\x7B\xC7\x0E\xF2\xB3\x01\x48\x0F\xC8\xEB\x05" + "\x7B\xFB\x61\xCC\x41\x04\x74\x6D\x33\x84\xB1\xE6\x6A\xD8\x0F\xBC" + "\x27\xAC\x43\x45\xFA\x04\xD1\x22\x29\x29\x28\x34\x3A\x68\x61\x73" + "\x68\x34\x3A\x73\x68\x61\x31\x29\x29"); + /* The same but without the hash algo. */ + unsigned char example1_rsa[] = + ("\x28\x37\x3A\x73\x69\x67\x2D\x76\x61\x6C\x28\x33\x3A\x72\x73\x61" + "\x28\x31\x3A\x73\x31\x32\x38\x3A\x17\xD2\xE9\x5F\xB4\x24\xD4\x1E" + "\x8C\xEE\x94\xDA\x41\x42\x1F\x26\x5E\xF4\x6D\xEC\x5B\xBD\x5B\x89" + "\x7A\x69\x11\x43\xE9\xD2\x23\x21\x25\x64\xA6\xB0\x56\xEF\xB4\xE9" + "\x06\xB2\x44\xF6\x80\x1E\xFF\x41\x23\xEB\xC9\xFA\xFD\x09\xBF\x9C" + "\x8E\xCF\x7F\xC3\x7F\x3A\x40\x48\x89\xDC\xBA\xB7\xDB\x9E\xF1\xBA" + "\x7C\x08\xEA\x74\x1D\x49\xE7\x65\xEF\x67\x79\xBC\x23\xD9\x49\xCD" + "\x05\x99\xD3\xD8\xB7\x7B\xC7\x0E\xF2\xB3\x01\x48\x0F\xC8\xEB\x05" + "\x7B\xFB\x61\xCC\x41\x04\x74\x6D\x33\x84\xB1\xE6\x6A\xD8\x0F\xBC" + "\x27\xAC\x43\x45\xFA\x04\xD1\x22\x29\x29\x29"); + + algo = hash_algo_from_sigval (example1_rsa_sha1); + if (algo != GCRY_MD_SHA1) + fail (0); + algo = hash_algo_from_sigval (example1_rsa); + if (algo) + fail (0); +} + + +static void +test_make_canon_sexp_from_rsa_pk (void) +{ + struct { + unsigned char *m; + size_t mlen; + unsigned char *e; + size_t elen; + unsigned char *result; + size_t resultlen; + gpg_err_code_t reverr; /* Expected error from the reverse function. */ + } tests[] = { + { + "\x82\xB4\x12\x48\x08\x48\xC0\x76\xAA\x8E\xF1\xF8\x7F\x5E\x9B\x89" + "\xA9\x62\x92\xA2\x16\x1B\xF5\x9F\xE1\x41\xF3\xF0\x42\xB5\x5C\x46" + "\xB8\x83\x9F\x39\x97\x73\xFF\xC5\xB2\xF4\x59\x5F\xBA\xC7\x0E\x03" + "\x9D\x27\xC0\x86\x37\x31\x46\xE0\xA1\xFE\xA1\x41\xD4\xE3\xE9\xB3" + "\x9B\xD5\x84\x65\xA5\x37\x35\x34\x07\x58\xB6\xBA\x21\xCA\x21\x72" + "\x4C\xF3\xFC\x91\x47\xD1\x3C\x1D\xA5\x9C\x38\x4D\x58\x39\x92\x16" + "\xB1\xE5\x43\xFE\xB5\x46\x4B\x43\xD1\x47\xB0\xE8\x2A\xDB\xF8\x34" + "\xB0\x5A\x22\x3D\x14\xBB\xEA\x63\x65\xA7\xF1\xF2\xF8\x97\x74\xA7", + 128, + "\x40\x00\x00\x81", + 4, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x32\x39\x3a\x00\x82\xb4\x12" + "\x48\x08\x48\xc0\x76\xaa\x8e\xf1\xf8\x7f\x5e\x9b\x89\xa9\x62\x92" + "\xa2\x16\x1b\xf5\x9f\xe1\x41\xf3\xf0\x42\xb5\x5c\x46\xb8\x83\x9f" + "\x39\x97\x73\xff\xc5\xb2\xf4\x59\x5f\xba\xc7\x0e\x03\x9d\x27\xc0" + "\x86\x37\x31\x46\xe0\xa1\xfe\xa1\x41\xd4\xe3\xe9\xb3\x9b\xd5\x84" + "\x65\xa5\x37\x35\x34\x07\x58\xb6\xba\x21\xca\x21\x72\x4c\xf3\xfc" + "\x91\x47\xd1\x3c\x1d\xa5\x9c\x38\x4d\x58\x39\x92\x16\xb1\xe5\x43" + "\xfe\xb5\x46\x4b\x43\xd1\x47\xb0\xe8\x2a\xdb\xf8\x34\xb0\x5a\x22" + "\x3d\x14\xbb\xea\x63\x65\xa7\xf1\xf2\xf8\x97\x74\xa7\x29\x28\x31" + "\x3a\x65\x34\x3a\x40\x00\x00\x81\x29\x29\x29", + 171 + }, + { + "\x63\xB4\x12\x48\x08\x48\xC0\x76\xAA\x8E\xF1\xF8\x7F\x5E\x9B\x89", + 16, + "\x03", + 1, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x36\x3a\x63\xb4\x12\x48\x08" + "\x48\xc0\x76\xaa\x8e\xf1\xf8\x7f\x5e\x9b\x89\x29\x28\x31\x3a\x65" + "\x31\x3a\x03\x29\x29\x29", + 54, + }, + { + "", + 0, + "", + 0, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x3a\x00\x29\x28\x31\x3a\x65" + "\x31\x3a\x00\x29\x29\x29", + 38, + GPG_ERR_BAD_PUBKEY + }, + { + NULL + } + }; + int idx; + gpg_error_t err; + unsigned char *sexp; + size_t length; + const unsigned char *rsa_n, *rsa_e; + size_t rsa_n_len, rsa_e_len; + + for (idx=0; tests[idx].m; idx++) + { + sexp = make_canon_sexp_from_rsa_pk (tests[idx].m, tests[idx].mlen, + tests[idx].e, tests[idx].elen, + &length); + if (!sexp) + { + fprintf (stderr, "%s:%d: out of core\n", __FILE__, __LINE__); + exit (1); + } + + if (length != tests[idx].resultlen) + fail (idx); + if (memcmp (sexp, tests[idx].result, tests[idx].resultlen)) + fail (idx); + + /* Test the reverse function. */ + err = get_rsa_pk_from_canon_sexp (sexp, length, + &rsa_n, &rsa_n_len, + &rsa_e, &rsa_e_len); + if (gpg_err_code (err) != tests[idx].reverr) + fail (idx); + if (!err) + { + if (tests[idx].mlen != rsa_n_len) + fail (idx); + if (memcmp (tests[idx].m, rsa_n, rsa_n_len)) + fail (idx); + if (tests[idx].elen != rsa_e_len) + fail (idx); + if (memcmp (tests[idx].e, rsa_e, rsa_e_len)) + fail (idx); + } + + xfree (sexp); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_hash_algo_from_sigval (); + test_make_canon_sexp_from_rsa_pk (); + + return 0; +} diff --git a/common/t-ssh-utils.c b/common/t-ssh-utils.c new file mode 100644 index 0000000..f63ea95 --- /dev/null +++ b/common/t-ssh-utils.c @@ -0,0 +1,314 @@ +/* t-ssh-utils.c - Module test for ssh-utils.c + * Copyright (C) 2011 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "ssh-utils.h" + + +static struct { const char *key; const char *fpr; } sample_keys[] = { + { "(protected-private-key " + "(rsa " + "(n #" + "00D88E47BCE0DA99D6180E8A9F4E6A673CC16F5BB6CF930E0E868BAABA715A8E1D3E2BEA" + "5477170E1F6CAFC0F8907B9892993C70AC476BBB301669F68EE0593532FB522DD60755A3" + "2F8B08649E856271A7F9BCB25F29554DF11707F812EA377683A99DD4698C4DBF797A0ABF" + "43C8EBB364B9FFC9EE78CBEA348C590507A4EA390312153DDD905EC4F1A63D5DA56C08FD" + "C3F6E5707BFC5DBDC09D19723B1AC6E466906F13AA2ECDBD258148F86C980D45CF233415" + "38C5857C2CF0B4C9AB2B4E6A4517FF084FDB009A33553A68907A29691B6FAE994E864F78" + "7B83F714730BEDB0AF1723D636E034D73EB7EC9BA127BB4BE80FD805813E3F45E7FAE514" + "AD2ECA9607#)" + "(e \"#\")" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #B5847F9A2DB4E0C5# \"5242880\")" + "#342D81BDE21301F18FDCE169A99A47C5#)" + "#23512602219EC7A97DBA89347CCD59D2072D80CE3F7DD6C97A058B83DAB3C829D97DF5" + "DFE9181F27DBF58258C4CDBD562A5B20BB5BC35EDCA7B1E57B8CDBF92D798F46EE5567BD" + "8A67EF3BE09908A49D41AA166A3398B64227BC75021C69A8FE8354E2903EF52DC91B1FE3" + "EF9558E5C2D34CF38BFC29E99A49AE30B0C22CE81EE14FC71E986E7C7CB5FCF807433FDA" + "EF1D00985767265BA0BE333754E44CCF622CBB98A029D78A6A9AADBC24613127B6448350" + "23DA355ED31CF089DD11A7FC6003CEEB53FB327A05604D053C99996F9E01CB355983F66E" + "7BEB9687A9277BBF440ED5FAF1A8396C9B06C9B47BA7A994E1931B08DAD34449952CD343" + "9A691477682C324EA07CCCE5DF0F0E9DAEFAE3A4717AACA6DC18ED91DD5A820C924BD36B" + "B3BA85BD63B3180C7F94EE58956940621280B9628FA5CC560BB14331AF1A7B5B499F8F03" + "0ED464ABD4E26C5FD610697EDD0FD1203983E73418F3776568A613D3CEFF17199473052A" + "18807A6F5C52A2A643185801D087EE4DC930ABEEB67C5B8A1CB2F29D0ACBD855972BEC0B" + "DE6E52387CFCC54B4C2B87EE947C97173BFCAE3E2658EB819D87F542C9A9FE6C410D08F5" + "3CD5451FB50253F4A848DFE136B3A5861D58B76A26A7E3E4E7A8F8D4BD5B80430674A6B9" + "A2C8EDD53DB37865D1ACBB07E1758DFF64A944E0126F948BF088C0FC0C3607E39522EC94" + "91483A90D9498D7F6C3C8720124C7E3F6E271E78E1CFFB4EF64F070F7424F30372A07D02" + "2355D8B17BB0DEBCBE101F621E0526551A35A56830D74E0F5BD6313DF114D1E46D4844AA" + "E4EB6268637D04B27D200D7F40AFA9AD2CFAA5415E5FC08358FFA79A9E743CCDF6668FE5" + "D79FA03D61941E57244F066A31F1C9D6A34DC62BC738C52B604F00B19EB9FD0173F3B139" + "42932066B7DC94DC4C563392F798A1CE2D5D75B8FF93E440433263CFB7016143A9923CD9" + "634E964A8056946F462B06F320F44449D85B07FA26A324505C858274F89EDBD8346950DE" + "5F#)" + "(protected-at \"20110720T135431\")" + ")" + "(comment passphrase_is_abc)" + ")", + "c7:c6:a7:ec:04:6c:87:59:54:f2:88:58:09:e0:f2:b1" + }, + { + "(protected-private-key " + "(dsa " + "(p #00FC7DC086F4517079BCCFA7FD229477FE88B0231038DFC21B29CCBD74C6F6FE04FD" + "7248C0473D5028BE106D7A7C8F54B269225789E781763527D1432CD46E416C2D14DDCA70" + "27DA4B92D1E222B5BDF4B9C8C761CACCFBD108F7729412E8835653BE5073447287A6BDEB" + "4645A5411752405EE7F503E44B1DFDCA6054CD3C44630B#)" + "(q #00D498505BF0E7EE01239EB51F2B400B8EF6329B17#)" + "(g #00A127B3DD5106F0A463312E42ECB83790E6F3BEA7AC3FAF7A42FB2C00F376323676" + "C9E48984F0D4AC3FE5856F1C2765E9BC3C8A5C9C9CD3166C057E82569D187C48591AA66B" + "8966BFF2B827BE36BD0BA4B895B42136F1381D52DDA708B2A3D181F648228DFFFEB153DA" + "ACCAEBB51EF08A7807CD628024CEFF96FEE97DE95C8CBE#)" + "(y #008E2B0915A3A299D83B4333C848C5D312F25903773E8C4D50691CAF81C3B768FA41" + "7D19F0FD437B377CCF51D3AE598649656D4D74D210CDBC2B76209B16EAAFCB14D6F4D691" + "20164885852AF1CEBB4D8602AD6755DFA7163645B4DB7926CD44D2DD9F840BFEF57F3DB0" + "933C85EB6B0AAC20BC67E73F47B8DDBEC8EFAA64286EF1#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 \"ü¿jy²üa4\" \"5242880\")" + "#FF12BEE0B03F842349717AE1AB6D7AC2#)" + "#95570487C8B5C49492D4E662259F2CF9B6D7E64F728F17A1FE1B2DA616E5976FE32861E" + "C4B1F0DA03D9006C432CF2136871266E9444377ACEF04340B36B4550B5C1E4CC69AD4380" + "A709FB0DAA5104A8B#)" + "(protected-at \"20110720T142801\")" + ")" + "(comment sample_dsa_passphrase_is_abc)" + ")", + "2d:b1:70:1a:04:9e:41:a3:ce:27:a5:c7:22:fe:3a:a3" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecdsa " + "(curve \"NIST P-256\")" + "(q #041F17ED5E3D637181DFA68157270F94A46C089B6F5D4518564600551C0A60A063B3" + "31EDE027A23CAB58A5BAD469600229DC8DED06380A92F86460ED400F963319#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #43F887516D94A502# \"20971520\")" + "#B135DEDA02CF36F126BA661FB22A35CF#)" + "#37E74BEC054B17723C106BA69214CFDA245512E40F4848ECF5719E3700002C940BC7EEC" + "283537CA4D8779107E07F03AAA9FAF155BA9BF6286080C35EF72DDAAF303FD9069475B03" + "C99D9FC93C58CD83A852964D2C7BFD1D803E2ECD1331937C3#)" + "(protected-at \"20150922T071259\")" + ")" + "(comment \"ecdsa w/o comment\")" + ")", /* Passphrase="abc" */ + "93:4f:08:02:7d:cb:16:9b:0c:39:21:4b:cf:28:5a:19" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecdsa " + "(curve \"NIST P-384\") " + "(q #04B6E747AC2F179F96088D1DB58EB8600BB23FAEF5F58EFE712A7478FB7BF735" + "B015EA2DFBBA965D8C6EB135A2B9B9599D65BF0167D2DB6ABF00F641F0F5FC15A4C3" + "EFE432DA331B7C8A66D6C4C2B0EBB5ED11A80301C4E57C1EBD25665CEBF123#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #3B13710B67D756EA# \"20971520\")" + "#720599AC095BF1BD73ED72F49FB77BFA#)" + "#F1A522F4533E3A6E40821D67CEA6C28A7FF07ACA4BEE81E0F39193B2E469E0C583D" + "A42E0E2D52ADB5ACFAB9C4CA7F1C3556FD7FD2770717FB3CE7C59474A3E2A7AF3D93" + "9EC01E067DAAA60D3D355D9BABCCD1F013E8637C555DDFA61F8FA5AFB010FF02979D" + "35BBBEED71BFD8BB508F7#)" + "(protected-at \"20150922T070806\")" + ")" + "(comment \"ecdsa w/o comment\")" + ")", /* Passphrase="abc" */ + "a3:cb:44:c8:56:15:25:62:85:fd:e8:04:7a:26:dc:76" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecdsa " + "(curve \"NIST P-521\")" + "(q #04005E460058F37DB5ADA670040203C4D7E18D9FC8A7087165904A4E25EE5EEE" + "3046406D922616DA7E71016A1CB9E57A45E3D3727D7C8DF0F11AE2BD75FAD3355CAA" + "E1019D89D33CC77424E5DA233588207444FC9F67BBE428A9528B7DC77AF8261A1D45" + "ACC1A657C99E361E93C1E5C0F214104C18807670F4CDC1E038B7C950FDBAAECB40#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #FB2E36984DE2E17C# \"19737600\")" + "#85DB6445B37012F9A449E5AC0D5017E9#)" + "#B4C7CCDFE9B5D32B31BA7C763B80485A62EBF34FD68D8E306DA75FD2BDDBABAA098" + "9B51972BA3B731DA5261E0ADC3FAEF9BB4C8284C53D3E88E738AEF1490941903A5B2" + "9F3747E83C4D80B6A89E0B7BDEE5C6638332F4AAEA5983F760B2887A43A1C4BE0564" + "3F72C6943987D97FDAA7D9C235C6D31973A2400DA9BAB564A16EA#)" + "(protected-at \"20150922T075611\")" + ")" + "(comment \"ecdsa w/o comment\")" + ")", /* Passphrase="abc" */ + "1e:a6:94:ab:bd:81:73:5f:22:bc:0e:c7:89:f6:68:df" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecc " + "(curve Ed25519)" + "(flags eddsa)" + "(q #40A3577AA7830C50EBC15B538E9505DB2F0D2FFCD57EA477DD83dcaea530f3c277#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #FA8123F1A37CBC1F# \"3812352\")" + "#7671C7387E2DD931CC62C35CBBE08A28#)" + "#75e928f4698172b61dffe9ef2ada1d3473f690f3879c5386e2717e5b2fa46884" + "b189ee409827aab0ff37f62996e040b5fa7e75fc4d8152c8734e2e648dff90c9" + "e8c3e39ea7485618d05c34b1b74ff59676e9a3d932245cc101b5904777a09f86#)" + "(protected-at \"20150928T050210\")" + ")" + "(comment \"eddsa w/o comment\")" + ")", /* Passphrase="abc" */ + "f1:fa:c8:a6:40:bb:b9:a1:65:d7:62:65:ac:26:78:0e" + }, + { + NULL, + NULL + } +}; + + + +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + char *buf; + size_t buflen; + struct stat st; + + fp = fopen (fname, "rb"); + if (!fp) + { + fprintf (stderr, "%s:%d: can't open '%s': %s\n", + __FILE__, __LINE__, fname, strerror (errno)); + exit (1); + } + + if (fstat (fileno(fp), &st)) + { + fprintf (stderr, "%s:%d: can't stat '%s': %s\n", + __FILE__, __LINE__, fname, strerror (errno)); + exit (1); + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + fprintf (stderr, "%s:%d: error reading '%s': %s\n", + __FILE__, __LINE__, fname, strerror (errno)); + exit (1); + } + fclose (fp); + + *r_length = buflen; + return buf; +} + + +static gcry_sexp_t +read_key (const char *fname) +{ + gpg_error_t err; + char *buf; + size_t buflen; + gcry_sexp_t key; + + buf = read_file (fname, &buflen); + + err = gcry_sexp_sscan (&key, NULL, buf, buflen); + if (err) + { + fprintf (stderr, "%s:%d: gcry_sexp_sscan failed: %s\n", + __FILE__, __LINE__, gpg_strerror (err)); + exit (1); \ + } + + xfree (buf); + return key; +} + + +int +main (int argc, char **argv) +{ + gpg_error_t err; + gcry_sexp_t key; + char *string; + int idx; + + if (argc == 2) + { + key = read_key (argv[1]); + err = ssh_get_fingerprint_string (key, &string); + if (err) + { + fprintf (stderr, "%s:%d: error getting fingerprint: %s\n", + __FILE__, __LINE__, gpg_strerror (err)); + exit (1); + } + puts (string); + xfree (string); + gcry_sexp_release (key); + } + else + { + for (idx=0; sample_keys[idx].key; idx++) + { + err = gcry_sexp_sscan (&key, NULL, sample_keys[idx].key, + strlen (sample_keys[idx].key)); + if (err) + { + fprintf (stderr, "%s:%d: gcry_sexp_sscan failed for " + "sample key %d: %s\n", + __FILE__, __LINE__, idx, gpg_strerror (err)); + exit (1); + } + + err = ssh_get_fingerprint_string (key, &string); + gcry_sexp_release (key); + if (err) + { + fprintf (stderr, "%s:%d: error getting fingerprint for " + "sample key %d: %s\n", + __FILE__, __LINE__, idx, gpg_strerror (err)); + exit (1); + } + + if (strcmp (string, sample_keys[idx].fpr)) + { + fprintf (stderr, "%s:%d: fingerprint mismatch for " + "sample key %d\n", + __FILE__, __LINE__, idx); + fprintf (stderr, "want: %s\n got: %s\n", + sample_keys[idx].fpr, string); + exit (1); + } + xfree (string); + } + } + + return 0; +} diff --git a/common/t-stringhelp.c b/common/t-stringhelp.c new file mode 100644 index 0000000..d86d896 --- /dev/null +++ b/common/t-stringhelp.c @@ -0,0 +1,999 @@ +/* t-stringhelp.c - Regression tests for stringhelp.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PWD_H +# include +#endif +#include +#include +#include + +#include "t-support.h" +#include "stringhelp.h" + + +static char *home_buffer; + + +const char * +gethome (void) +{ + if (!home_buffer) + { + char *home = getenv("HOME"); + + if(home) + home_buffer = xstrdup (home); +#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H) + else + { + struct passwd *pwd; + + pwd = getpwuid (getuid()); + if (pwd) + home_buffer = xstrdup (pwd->pw_dir); + } +#endif + } + return home_buffer; +} + + +static char * +mygetcwd (void) +{ + char *buffer; + size_t size = 100; + + for (;;) + { + buffer = xmalloc (size+1); +#ifdef HAVE_W32CE_SYSTEM + strcpy (buffer, "/"); /* Always "/". */ + return buffer; +#else + if (getcwd (buffer, size) == buffer) + return buffer; + xfree (buffer); + if (errno != ERANGE) + { + fprintf (stderr,"error getting current cwd: %s\n", + strerror (errno)); + exit (2); + } + size *= 2; +#endif + } +} + + +static void +test_percent_escape (void) +{ + char *result; + static struct { + const char *extra; + const char *value; + const char *expected; + } tests[] = + { + { NULL, "", "" }, + { NULL, "%", "%25" }, + { NULL, "%%", "%25%25" }, + { NULL, " %", " %25" }, + { NULL, ":", "%3a" }, + { NULL, " :", " %3a" }, + { NULL, ": ", "%3a " }, + { NULL, " : ", " %3a " }, + { NULL, "::", "%3a%3a" }, + { NULL, ": :", "%3a %3a" }, + { NULL, "%:", "%25%3a" }, + { NULL, ":%", "%3a%25" }, + { "\\\n:", ":%", "%3a%25" }, + { "\\\n:", "\\:%", "%5c%3a%25" }, + { "\\\n:", "\n:%", "%0a%3a%25" }, + { "\\\n:", "\xff:%", "\xff%3a%25" }, + { "\\\n:", "\xfe:%", "\xfe%3a%25" }, + { "\\\n:", "\x01:%", "\x01%3a%25" }, + { "\x01", "\x01:%", "%01%3a%25" }, + { "\xfe", "\xfe:%", "%fe%3a%25" }, + { "\xfe", "\xff:%", "\xff%3a%25" }, + + { NULL, NULL, NULL } + }; + int testno; + + result = percent_escape (NULL, NULL); + if (result) + fail (0); + for (testno=0; tests[testno].value; testno++) + { + result = percent_escape (tests[testno].value, tests[testno].extra); + if (!result) + fail (testno); + else if (strcmp (result, tests[testno].expected)) + fail (testno); + xfree (result); + } + +} + + +static void +test_compare_filenames (void) +{ + struct { + const char *a; + const char *b; + int result; + } tests[] = { + { "", "", 0 }, + { "", "a", -1 }, + { "a", "", 1 }, + { "a", "a", 0 }, + { "a", "aa", -1 }, + { "aa", "a", 1 }, + { "a", "b", -1 }, + +#ifdef HAVE_W32_SYSTEM + { "a", "A", 0 }, + { "A", "a", 0 }, + { "foo/bar", "foo\\bar", 0 }, + { "foo\\bar", "foo/bar", 0 }, + { "foo\\", "foo/", 0 }, + { "foo/", "foo\\", 0 }, +#endif /*HAVE_W32_SYSTEM*/ + { NULL, NULL, 0} + }; + int testno, result; + + for (testno=0; tests[testno].a; testno++) + { + result = compare_filenames (tests[testno].a, tests[testno].b); + result = result < 0? -1 : result > 0? 1 : 0; + if (result != tests[testno].result) + fail (testno); + } +} + + +static void +test_strconcat (void) +{ + char *out; + + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + else + xfree (out); + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = strconcat (NULL); + if (!out || *out) + fail (1); +#endif + out = strconcat (NULL, NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = strconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = strconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = strconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = strconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = strconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = strconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + +static void +test_xstrconcat (void) +{ + char *out; + + out = xstrconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + xfree (out); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = xstrconcat (NULL); + if (!out) + fail (1); +#endif + out = xstrconcat (NULL, NULL); + if (!out) + fail (1); + xfree (out); + + out = xstrconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = xstrconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = xstrconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = xstrconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = xstrconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = xstrconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + + +static void +test_make_filename_try (void) +{ + char *out; + const char *home = gethome (); + size_t homelen = home? strlen (home):0; + + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", NULL); + if (!out || strcmp (out, + "1/2/3/4/5/6/7/8/9/10/" + "1/2/3/4/5/6/7/8/9/10/" + "1/2/3/4/5/6/7/8/9/10/" + "1/2")) + fail (0); + xfree (out); + + out = make_filename_try ("foo", "~/bar", "baz/cde", NULL); + if (!out || strcmp (out, "foo/~/bar/baz/cde")) + fail (1); + xfree (out); + + out = make_filename_try ("foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("/foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "/foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("//foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "//foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("", "~/bar", "baz/cde", NULL); + if (!out || strcmp (out, "/~/bar/baz/cde")) + fail (1); + xfree (out); + + + out = make_filename_try ("~/foo", "bar", NULL); + if (!out) + fail (2); + else if (home) + { + if (strlen (out) < homelen + 7) + fail (2); + else if (strncmp (out, home, homelen)) + fail (2); + else if (strcmp (out+homelen, "/foo/bar")) + fail (2); + } + else + { + if (strcmp (out, "~/foo/bar")) + fail (2); + } + xfree (out); + + out = make_filename_try ("~", "bar", NULL); + if (!out) + fail (2); + else if (home) + { + if (strlen (out) < homelen + 3) + fail (2); + else if (strncmp (out, home, homelen)) + fail (2); + else if (strcmp (out+homelen, "/bar")) + fail (2); + } + else + { + if (strcmp (out, "~/bar")) + fail (2); + } + xfree (out); +} + + +static void +test_make_absfilename_try (void) +{ + char *out; + char *cwd = mygetcwd (); + size_t cwdlen = strlen (cwd); + + out = make_absfilename_try ("foo", "bar", NULL); + if (!out) + fail (0); + else if (strlen (out) < cwdlen + 7) + fail (0); + else if (strncmp (out, cwd, cwdlen)) + fail (0); + else if (strcmp (out+cwdlen, "/foo/bar")) + fail (0); + xfree (out); + + out = make_absfilename_try ("./foo", NULL); + if (!out) + fail (1); + else if (strlen (out) < cwdlen + 5) + fail (1); + else if (strncmp (out, cwd, cwdlen)) + fail (1); + else if (strcmp (out+cwdlen, "/./foo")) + fail (1); + xfree (out); + + out = make_absfilename_try (".", NULL); + if (!out) + fail (2); + else if (strlen (out) < cwdlen) + fail (2); + else if (strncmp (out, cwd, cwdlen)) + fail (2); + else if (strcmp (out+cwdlen, "")) + fail (2); + xfree (out); + + xfree (cwd); +} + +static void +test_strsplit (void) +{ + struct { + const char *s; + char delim; + char replacement; + const char *fields_expected[10]; + } tv[] = { + { + "a:bc:cde:fghi:jklmn::foo:", ':', '\0', + { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL } + }, + { + ",a,bc,,def,", ',', '!', + { "!a!bc!!def!", "a!bc!!def!", "bc!!def!", "!def!", "def!", "", NULL } + }, + { + "", ':', ',', + { "", NULL } + } + }; + + int tidx; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + char *s2; + int field_count; + char **fields; + int field_count_expected; + int i; + + /* Count the fields. */ + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + + /* We need to copy s since strsplit modifies it in place. */ + s2 = xstrdup (tv[tidx].s); + fields = strsplit (s2, tv[tidx].delim, tv[tidx].replacement, + &field_count); + + if (field_count != field_count_expected) + fail (tidx * 1000); + + for (i = 0; i < field_count_expected; i ++) + if (strcmp (tv[tidx].fields_expected[i], fields[i]) != 0) + { + printf ("For field %d, expected '%s', but got '%s'\n", + i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + + xfree (fields); + xfree (s2); + } +} + + + +static void +test_strtokenize (void) +{ + struct { + const char *s; + const char *delim; + const char *fields_expected[10]; + } tv[] = { + { + "", ":", + { "", NULL } + }, + { + "a", ":", + { "a", NULL } + }, + { + ":", ":", + { "", "", NULL } + }, + { + "::", ":", + { "", "", "", NULL } + }, + { + "a:b:c", ":", + { "a", "b", "c", NULL } + }, + { + "a:b:", ":", + { "a", "b", "", NULL } + }, + { + "a:b", ":", + { "a", "b", NULL } + }, + { + "aa:b:cd", ":", + { "aa", "b", "cd", NULL } + }, + { + "aa::b:cd", ":", + { "aa", "", "b", "cd", NULL } + }, + { + "::b:cd", ":", + { "", "", "b", "cd", NULL } + }, + { + "aa: : b:cd ", ":", + { "aa", "", "b", "cd", NULL } + }, + { + " aa: : b: cd ", ":", + { "aa", "", "b", "cd", NULL } + }, + { + " ", ":", + { "", NULL } + }, + { + " :", ":", + { "", "", NULL } + }, + { + " : ", ":", + { "", "", NULL } + }, + { + ": ", ":", + { "", "", NULL } + }, + { + ": x ", ":", + { "", "x", NULL } + }, + { + "a:bc:cde:fghi:jklmn::foo:", ":", + { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL } + }, + { + ",a,bc,,def,", ",", + { "", "a", "bc", "", "def", "", NULL } + }, + { + " a ", " ", + { "", "a", "", NULL } + }, + { + " ", " ", + { "", "", NULL } + }, + { + "", " ", + { "", NULL } + } + }; + + int tidx; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + char **fields; + int field_count; + int field_count_expected; + int i; + + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + + fields = strtokenize (tv[tidx].s, tv[tidx].delim); + if (!fields) + fail (tidx * 1000); + else + { + for (field_count = 0; fields[field_count]; field_count++) + ; + if (field_count != field_count_expected) + fail (tidx * 1000); + else + { + for (i = 0; i < field_count_expected; i++) + if (strcmp (tv[tidx].fields_expected[i], fields[i])) + { + printf ("For field %d, expected '%s', but got '%s'\n", + i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + } + } + + xfree (fields); + } +} + + +static void +test_split_fields (void) +{ + struct { + const char *s; + int nfields; + const char *fields_expected[10]; + } tv[] = { + { + "a bc cde fghi jklmn foo ", 6, + { "a", "bc", "cde", "fghi", "jklmn", "foo", NULL } + }, + { + " a bc def ", 2, + { "a", "bc", "def", NULL } + }, + { + " a bc def ", 3, + { "a", "bc", "def", NULL } + }, + { + " a bc def ", 4, + { "a", "bc", "def", NULL } + }, + { + "", 0, + { NULL } + } + }; + + int tidx; + char *fields[10]; + int field_count_expected, nfields, field_count, i; + char *s2; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + nfields = tv[tidx].nfields; + assert (nfields <= DIM (fields)); + + /* Count the fields. */ + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + if (field_count_expected > nfields) + field_count_expected = nfields; + + /* We need to copy s since split_fields modifies in place. */ + s2 = xstrdup (tv[tidx].s); + field_count = split_fields (s2, fields, nfields); + + if (field_count != field_count_expected) + { + printf ("%s: tidx %d: expected %d, got %d\n", + __func__, tidx, field_count_expected, field_count); + fail (tidx * 1000); + } + else + { + for (i = 0; i < field_count_expected; i ++) + if (strcmp (tv[tidx].fields_expected[i], fields[i])) + { + printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n", + __func__, + tidx, i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + } + + xfree (s2); + } +} + + +static char * +stresc (char *s) +{ + char *p; + int l = strlen (s) + 1; + + for (p = s; *p; p ++) + if (*p == '\n') + l ++; + + p = xmalloc (l); + for (l = 0; *s; s ++, l ++) + { + if (*s == ' ') + p[l] = '_'; + else if (*p == '\n') + { + p[l ++] = '\\'; + p[l ++] = 'n'; + p[l] = '\n'; + } + else + p[l] = *s; + } + p[l] = *s; + + return p; +} + + +static void +test_format_text (void) +{ + struct test + { + int target_cols, max_cols; + char *input; + char *expected; + }; + + struct test tests[] = { + { + 10, 12, + "", + "", + }, + { + 10, 12, + " ", + "", + }, + { + 10, 12, + " ", + "", + }, + { + 10, 12, + " \n ", + " \n", + }, + { + 10, 12, + " \n \n ", + " \n \n", + }, + { + 10, 12, + "0123456789 0123456789 0", + "0123456789\n0123456789\n0", + }, + { + 10, 12, + " 0123456789 0123456789 0 ", + " 0123456789\n0123456789\n0", + }, + { + 10, 12, + "01 34 67 90 23 56 89 12 45 67 89 1", + "01 34 67\n90 23 56\n89 12 45\n67 89 1" + }, + { + 10, 12, + "01 34 67 90 23 56 89 12 45 67 89 1", + "01 34 67\n90 23 56\n89 12 45\n67 89 1" + }, + { + 72, 80, + "Warning: if you think you've seen more than 10 messages " + "signed by this key, then this key might be a forgery! " + "Carefully examine the email address for small variations " + "(e.g., additional white space). If the key is suspect, " + "then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as being bad.\n", + "Warning: if you think you've seen more than 10 messages signed by this\n" + "key, then this key might be a forgery! Carefully examine the email\n" + "address for small variations (e.g., additional white space). If the key\n" + "is suspect, then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as\n" + "being bad.\n" + + }, + { + 72, 80, + "Normally, there is only a single key associated with an email " + "address. However, people sometimes generate a new key if " + "their key is too old or they think it might be compromised. " + "Alternatively, a new key may indicate a man-in-the-middle " + "attack! Before accepting this key, you should talk to or " + "call the person to make sure this new key is legitimate.", + "Normally, there is only a single key associated with an email " + "address.\nHowever, people sometimes generate a new key if " + "their key is too old or\nthey think it might be compromised. " + "Alternatively, a new key may indicate\na man-in-the-middle " + "attack! Before accepting this key, you should talk\nto or " + "call the person to make sure this new key is legitimate.", + } + }; + + int i; + int failed = 0; + + for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i ++) + { + struct test *test = &tests[i]; + char *result = + format_text (test->input, 0, test->target_cols, test->max_cols); + if (strcmp (result, test->expected) != 0) + { + printf ("%s: Test #%d failed.\nExpected: '%s'\nResult: '%s'\n", + __func__, i + 1, stresc (test->expected), stresc (result)); + failed ++; + } + xfree (result); + } + + if (failed) + fail(0); +} + + +static void +test_compare_version_strings (void) +{ + struct { const char *a; const char *b; int okay; } tests[] = { + { "1.0.0", "1.0.0", 0 }, + { "1.0.0-", "1.0.0", 1 }, + { "1.0.0-1", "1.0.0", 1 }, + { "1.0.0.1", "1.0.0", 1 }, + { "1.0.0", "1.0.1", -1 }, + { "1.0.0-", "1.0.1", -1 }, + { "1.0.0-1", "1.0.1", -1 }, + { "1.0.0.1", "1.0.1", -1 }, + { "1.0.0", "1.1.0", -1 }, + { "1.0.0-", "1.1.0", -1 }, + { "1.0.0-1", "1.1.0", -1 }, + { "1.0.0.1", "1.1.0", -1 }, + + { "1.0.0", "1.0.0-", -1 }, + { "1.0.0", "1.0.0-1", -1 }, + { "1.0.0", "1.0.0.1", -1 }, + { "1.1.0", "1.0.0", 1 }, + { "1.1.1", "1.1.0", 1 }, + { "1.1.2", "1.1.2", 0 }, + { "1.1.2", "1.0.2", 1 }, + { "1.1.2", "0.0.2", 1 }, + { "1.1.2", "1.1.3", -1 }, + + { "0.99.1", "0.9.9", 1 }, + { "0.9.1", "0.91.0", -1 }, + + { "1.5.3", "1.5", 1 }, + { "1.5.0", "1.5", 0 }, + { "1.4.99", "1.5", -1 }, + { "1.5", "1.4.99", 1 }, + { "1.5", "1.5.0", 0 }, + { "1.5", "1.5.1", -1 }, + + { "1.5.3-x17", "1.5-23", 1 }, + + { "1.5.3a", "1.5.3", 1 }, + { "1.5.3a", "1.5.3b", -1 }, + + { "3.1.4-ab", "3.1.4-ab", 0 }, + { "3.1.4-ab", "3.1.4-ac", -1 }, + { "3.1.4-ac", "3.1.4-ab", 1 }, + { "3.1.4-ab", "3.1.4-abb", -1 }, + { "3.1.4-abb", "3.1.4-ab", 1 }, + + { "", "", INT_MIN }, + { NULL, "", INT_MIN }, + { "1.2.3", "", INT_MIN }, + { "1.2.3", "2", INT_MIN }, + + /* Test cases for validity of A. */ + { "", NULL, INT_MIN }, + { "1", NULL, INT_MIN }, + { "1.", NULL, 0 }, + { "1.0", NULL, 0 }, + { "1.0.", NULL, 0 }, + { "a1.2", NULL, INT_MIN }, + { NULL, NULL, INT_MIN } + }; + int idx; + int res; + + for (idx=0; idx < DIM(tests); idx++) + { + res = compare_version_strings (tests[idx].a, tests[idx].b); + /* printf ("test %d: '%s' '%s' %d -> %d\n", */ + /* idx, tests[idx].a, tests[idx].b, tests[idx].okay, res); */ + if (res != tests[idx].okay) + fail (idx); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_percent_escape (); + test_compare_filenames (); + test_strconcat (); + test_xstrconcat (); + test_make_filename_try (); + test_make_absfilename_try (); + test_strsplit (); + test_strtokenize (); + test_split_fields (); + test_compare_version_strings (); + test_format_text (); + + xfree (home_buffer); + return !!errcount; +} diff --git a/common/t-strlist.c b/common/t-strlist.c new file mode 100644 index 0000000..bd835ca --- /dev/null +++ b/common/t-strlist.c @@ -0,0 +1,84 @@ +/* t-strlist.c - Regression tests for strist.c + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include + +#include "strlist.h" + +#include "t-support.h" + +static void +test_strlist_rev (void) +{ + strlist_t s = NULL; + + /* Reversing an empty list should yield the empty list. */ + if (! (strlist_rev (&s) == NULL)) + fail (1); + + add_to_strlist (&s, "1"); + add_to_strlist (&s, "2"); + add_to_strlist (&s, "3"); + + if (strcmp (s->d, "3") != 0) + fail (2); + if (strcmp (s->next->d, "2") != 0) + fail (2); + if (strcmp (s->next->next->d, "1") != 0) + fail (2); + if (s->next->next->next) + fail (2); + + strlist_rev (&s); + + if (strcmp (s->d, "1") != 0) + fail (2); + if (strcmp (s->next->d, "2") != 0) + fail (2); + if (strcmp (s->next->next->d, "3") != 0) + fail (2); + if (s->next->next->next) + fail (2); + + free_strlist (s); +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_strlist_rev (); + + return 0; +} diff --git a/common/t-support.h b/common/t-support.h new file mode 100644 index 0000000..5449a56 --- /dev/null +++ b/common/t-support.h @@ -0,0 +1,84 @@ +/* t-support.h - Helper for the regression tests + * Copyright (C) 2007 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_T_SUPPORT_H +#define GNUPG_COMMON_T_SUPPORT_H 1 + +#ifdef GCRYPT_VERSION +#error The regression tests should not include with gcrypt.h +#endif + +#include +#include + +#include + + +#ifndef HAVE_GETENV +# define getenv(a) (NULL) +#endif + +#ifndef DIM +# define DIM(v) (sizeof(v)/sizeof((v)[0])) +# define DIMof(type,member) DIM(((type *)0)->member) +#endif + + +/* Replacement prototypes. */ +void *gcry_xmalloc (size_t n); +void *gcry_xcalloc (size_t n, size_t m); +void *gcry_xrealloc (void *a, size_t n); +char *gcry_xstrdup (const char * a); +void gcry_free (void *a); + +/* Map the used xmalloc functions to those implemented by t-support.c */ +#define xmalloc(a) gcry_xmalloc ( (a) ) +#define xcalloc(a,b) gcry_xcalloc ( (a), (b) ) +#define xrealloc(a,n) gcry_xrealloc ( (a), (n) ) +#define xstrdup(a) gcry_xstrdup ( (a) ) +#define xfree(a) gcry_free ( (a) ) + + +/* Macros to print the result of a test. */ +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + if (!no_exit_on_fail) \ + exit (1); \ + } while(0) + +/* If this flag is set the fail macro does not call exit. */ +static int no_exit_on_fail; +/* Error counter. */ +static int errcount; + + +#endif /*GNUPG_COMMON_T_SUPPORT_H*/ diff --git a/common/t-sysutils.c b/common/t-sysutils.c new file mode 100644 index 0000000..79f8385 --- /dev/null +++ b/common/t-sysutils.c @@ -0,0 +1,89 @@ +/* t-sysutils.c - Module test for sysutils.c + * Copyright (C) 2007 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 3 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, see . + */ + +#include +#include +#include + +#include "util.h" +#include "sysutils.h" + +#ifdef HAVE_W32CE_SYSTEM +# define rewind(f) do { fseek (f, 0, SEEK_SET); clearerr (f); } while (0) +#endif + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + } while(0) + +static int verbose; +static int errcount; + + +static void +test_gnupg_tmpfile (void) +{ + FILE *fparr[10]; + int fparridx; + int idx; + FILE *fp; + char buffer[100]; + +#define ASTRING "fooooooooooooooo\n" /* Needs to be shorter than BUFFER. */ + + for (fparridx=0; fparridx < DIM (fparr); fparridx++) + { + fp = gnupg_tmpfile (); + fparr[fparridx] = fp; + if (!fp) + fail (fparridx); + else + { + fputs ( ASTRING, fp); + rewind (fp); + if (!fgets (buffer, sizeof (buffer), fp)) + fail (fparridx); + if (strcmp (buffer, ASTRING)) + fail (fparridx); + if (fgets (buffer, sizeof (buffer), fp)) + fail (fparridx); + } + } + for (idx=0; idx < fparridx; idx++) + { + if (fparr[idx]) + fclose (fparr[idx]); + } +} + + + +int +main (int argc, char **argv) +{ + if (argc > 1 && !strcmp (argv[1], "--verbose")) + verbose = 1; + + test_gnupg_tmpfile (); + /* Fixme: Add tests for setenv and unsetenv. */ + + return !!errcount; +} diff --git a/common/t-timestuff.c b/common/t-timestuff.c new file mode 100644 index 0000000..1e524f5 --- /dev/null +++ b/common/t-timestuff.c @@ -0,0 +1,175 @@ +/* t-timestuff.c - Regression tests for time functions + * Copyright (C) 2007 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "mischelp.h" + +#include "t-support.h" + + +static int +cmp_time_s (struct tm *a, struct tm *b) +{ + if (a->tm_year != b->tm_year + || a->tm_mon != b->tm_mon + || a->tm_mday != b->tm_mday + || a->tm_hour != b->tm_hour + || a->tm_min != b->tm_min + || a->tm_sec != b->tm_sec + || a->tm_wday != b->tm_wday + || a->tm_yday != b->tm_yday + || !a->tm_isdst != !b->tm_isdst) + return -1; + return 0; +} + + + +static void +test_timegm (void) +{ + static struct { + int year, mon, mday, hour, min, sec; + } tvalues[] = { + { -1 }, + { -2, 1 }, + { -2, 2 }, + { -2, 86399 }, + { -2, 86400 }, + { -2, 0x7ffffffe }, + { -2, 0x7fffffff }, + /* Note: Because we use mktime below we can only start with the + day after Epoch. */ + { 1970, 0, 2, 0, 0 , 1}, + { 1970, 0, 2, 0, 0 , 2}, + { 1970, 0, 2, 12, 0 , 0}, + { 1970, 0, 2, 23, 59 , 59}, + { 1999, 11, 31, 23, 59 , 59}, + { 2000, 0, 1, 0, 0, 0}, + { 2000, 0, 1, 0, 0, 1}, + { 2010, 11, 31, 23, 59 , 59}, + { 2010, 0, 1, 0, 0, 0}, + { 2010, 0, 1, 0, 0, 1}, + /* On GNU based 32 bit systems the end of all ticks will be on + 20380119T031408 (unless Uli takes compassion on us and changes + time_t to a u64). We check that the previous day is okay. */ + { 2038, 0, 18, 23, 59, 59} + + }; + int tidx; + time_t now, atime; + struct tm tbuf, tbuf2, *tp; + + for (tidx=0; tidx < DIM (tvalues); tidx++) + { + if (tvalues[tidx].year == -1) + { + now = time (NULL); + } + else if (tvalues[tidx].year == -2) + { + now = tvalues[tidx].mon; + } + else + { + memset (&tbuf, 0, sizeof tbuf); + tbuf.tm_year = tvalues[tidx].year - 1900; + tbuf.tm_mon = tvalues[tidx].mon; + tbuf.tm_mday = tvalues[tidx].mday; + tbuf.tm_hour = tvalues[tidx].hour; + tbuf.tm_min = tvalues[tidx].min; + tbuf.tm_sec = tvalues[tidx].sec; +#ifdef HAVE_TIMEGM + now = timegm (&tbuf); +#else + now = mktime (&tbuf); +#endif + } + if (now == (time_t)(-1)) + fail (tidx); + + tp = gmtime (&now); + if (!tp) + fail (tidx); + else + { + tbuf = *tp; + tbuf2 = tbuf; +#ifdef HAVE_TIMEGM + atime = timegm (&tbuf); +#else + atime = mktime (&tbuf); +#endif + if (atime == (time_t)(-1)) + fail (tidx); + else if (atime != now) + fail (tidx); + + tp = gmtime (&atime); + if (!tp) + fail (tidx); + else if (cmp_time_s (tp, &tbuf)) + fail (tidx); + else if (cmp_time_s (tp, &tbuf2)) + fail (tidx); + } + } +} + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + /* If we do not have timegm, we use mktime. However, we need to use + UTC in this case so that the 20380118T235959 test does not fail + for other timezones. */ +#ifndef HAVE_TIMEGM +# ifdef HAVE_SETENV + setenv ("TZ", "UTC", 1); +#else + putenv (xstrdup ("TZ=UTC")); +#endif + tzset (); +#endif + + test_timegm (); + + return 0; +} diff --git a/common/t-w32-reg.c b/common/t-w32-reg.c new file mode 100644 index 0000000..48ea0d4 --- /dev/null +++ b/common/t-w32-reg.c @@ -0,0 +1,80 @@ +/* t-w32-reg.c - Regression tests for W32 registry functions + * Copyright (C) 2010 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "mischelp.h" + +#include "t-support.h" +#include "w32help.h" + + +static void +test_read_registry (void) +{ + char *string; + +#ifdef HAVE_W32CE_SYSTEM + string = read_w32_registry_string ("HKEY_CLASSES_ROOT", + "BOOTSTRAP\\CLSID", NULL); + if (!string) + fail (0); + fprintf (stderr, "Bootstrap clsid: %s\n", string); + xfree (string); +#endif + + string = read_w32_registry_string + ("HKEY_CURRENT_USER", + "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + "User Agent"); + if (!string) + fail (0); + fprintf (stderr, "User agent: %s\n", string); + xfree (string); +} + + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_read_registry (); + + return 0; +} diff --git a/common/t-zb32.c b/common/t-zb32.c new file mode 100644 index 0000000..956c2f5 --- /dev/null +++ b/common/t-zb32.c @@ -0,0 +1,305 @@ +/* t-zb32.c - Module tests for zb32.c + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_DOSISH_SYSTEM +# include +#endif + +#include "zb32.h" +#include "t-support.h" + +#define PGM "t-zb32" + +static int verbose; +static int debug; +static int errcount; + + +static void +test_zb32enc (void) +{ + static struct { + size_t datalen; + char *data; + const char *expected; + } tests[] = { + /* From the DESIGN document. */ + { 1, "\x00", "y" }, + { 1, "\x80", "o" }, + { 2, "\x40", "e" }, + { 2, "\xc0", "a" }, + { 10, "\x00\x00", "yy" }, + { 10, "\x80\x80", "on" }, + { 20, "\x8b\x88\x80", "tqre" }, + { 24, "\xf0\xbf\xc7", "6n9hq" }, + { 24, "\xd4\x7a\x04", "4t7ye" }, + /* The next vector is strange: The DESIGN document from 2007 gives + "8ik66o" as result, the revision from 2009 gives "6im5sd". I + look at it for quite some time and came to the conclusion that + "6im54d" is the right encoding. */ + { 30, "\xf5\x57\xbd\x0c", "6im54d" }, + /* From ccrtp's Java code. */ + { 40, "\x01\x01\x01\x01\x01", "yryonyeb" }, + { 15, "\x01\x01", "yry" }, + { 80, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", "yryonyebyryonyeb" }, + { 15, "\x81\x81", "ogy" }, + { 16, "\x81\x81", "ogyo" }, + { 20, "\x81\x81\x81", "ogya" }, + { 64, "\x81\x81\x81\x81\x81\x81\x81\x81", "ogyadycbogyan" }, + /* More tests. */ + { 160, "\x80\x61\x58\x70\xF5\xBA\xD6\x90\x33\x36" + /* */"\x86\xD0\xF2\xAD\x85\xAC\x1E\x42\xB3\x67", + /* */"oboioh8izmmjyc3so5exfmcfioxrfc58" }, + { 0, "", "" } + }; + int tidx; + char *output; + + for (tidx = 0; tidx < DIM(tests); tidx++) + { + output = zb32_encode (tests[tidx].data, tests[tidx].datalen); + if (!output) + { + fprintf (stderr, PGM": error encoding test %d: %s\n", + tidx, strerror (errno)); + exit (1); + } + /* puts (output); */ + if (strcmp (output, tests[tidx].expected)) + fail (tidx); + xfree (output); + } +} + + +/* Read the file FNAME or stdin if FNAME is NULL and return a malloced + buffer with the content. R_LENGTH received the length of the file. + Print a diagnostic and returns NULL on error. */ +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + char *buf; + size_t buflen; + + if (!fname) + { + size_t nread, bufsize = 0; + + fp = stdin; +#ifdef HAVE_DOSISH_SYSTEM + setmode (fileno(fp) , O_BINARY ); +#endif + buf = NULL; + buflen = 0; +#define NCHUNK 8192 + do + { + bufsize += NCHUNK; + if (!buf) + buf = xmalloc (bufsize); + else + buf = xrealloc (buf, bufsize); + + nread = fread (buf+buflen, 1, NCHUNK, fp); + if (nread < NCHUNK && ferror (fp)) + { + fprintf (stderr, PGM": error reading '[stdin]': %s\n", + strerror (errno)); + xfree (buf); + return NULL; + } + buflen += nread; + } + while (nread == NCHUNK); +#undef NCHUNK + + } + else + { + struct stat st; + + fp = fopen (fname, "rb"); + if (!fp) + { + fprintf (stderr, PGM": can't open '%s': %s\n", + fname, strerror (errno)); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + fprintf (stderr, PGM": can't stat '%s': %s\n", + fname, strerror (errno)); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + fprintf (stderr, PGM": error reading '%s': %s\n", + fname, strerror (errno)); + fclose (fp); + xfree (buf); + return NULL; + } + fclose (fp); + } + + *r_length = buflen; + return buf; +} + + +/* Debug helper to encode or decode to/from zb32. */ +static void +endecode_file (const char *fname, int decode) +{ + char *buffer; + size_t buflen; + char *result; + + if (decode) + { + fprintf (stderr, PGM": decode mode has not yet been implemented\n"); + errcount++; + return; + } + +#ifdef HAVE_DOSISH_SYSTEM + if (decode) + setmode (fileno (stdout), O_BINARY); +#endif + + + buffer = read_file (fname, &buflen); + if (!buffer) + { + errcount++; + return; + } + + result = zb32_encode (buffer, 8 * buflen); + if (!result) + { + fprintf (stderr, PGM": error encoding data: %s\n", strerror (errno)); + errcount++; + xfree (buffer); + return; + } + + fputs (result, stdout); + putchar ('\n'); + + xfree (result); + xfree (buffer); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + int opt_endecode = 0; + + no_exit_on_fail = 1; + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [FILE]\n" + "Options:\n" + " --verbose Print timings etc.\n" + " --debug Flyswatter\n" + " --encode Encode FILE or stdin\n" + " --decode Decode FILE or stdin\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--encode")) + { + opt_endecode = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--decode")) + { + opt_endecode = -1; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + if (argc > 1) + { + fprintf (stderr, PGM ": to many arguments given\n"); + exit (1); + } + + if (opt_endecode) + { + endecode_file (argc? *argv : NULL, (opt_endecode < 0)); + } + else + test_zb32enc (); + + return !!errcount; +} diff --git a/common/tlv.c b/common/tlv.c new file mode 100644 index 0000000..6813c58 --- /dev/null +++ b/common/tlv.c @@ -0,0 +1,312 @@ +/* tlv.c - Tag-Length-Value Utilities + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include + +#include +#include +#include +#include + +#if GNUPG_MAJOR_VERSION == 1 +#define GPG_ERR_EOF (-1) +#define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/ +#define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/ +typedef int gpg_error_t; +#define gpg_make_err(x,n) (n) +#else +#include +#endif + +#include "util.h" +#include "tlv.h" + +static const unsigned char * +do_find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes, int nestlevel) +{ + const unsigned char *s = buffer; + size_t n = length; + size_t len; + int this_tag; + int composite; + + for (;;) + { + if (n < 2) + return NULL; /* Buffer definitely too short for tag and length. */ + if (!*s || *s == 0xff) + { /* Skip optional filler between TLV objects. */ + s++; + n--; + continue; + } + composite = !!(*s & 0x20); + if ((*s & 0x1f) == 0x1f) + { /* more tag bytes to follow */ + s++; + n--; + if (n < 2) + return NULL; /* buffer definitely too short for tag and length. */ + if ((*s & 0x1f) == 0x1f) + return NULL; /* We support only up to 2 bytes. */ + this_tag = (s[-1] << 8) | (s[0] & 0x7f); + } + else + this_tag = s[0]; + len = s[1]; + s += 2; n -= 2; + if (len < 0x80) + ; + else if (len == 0x81) + { /* One byte length follows. */ + if (!n) + return NULL; /* we expected 1 more bytes with the length. */ + len = s[0]; + s++; n--; + } + else if (len == 0x82) + { /* Two byte length follows. */ + if (n < 2) + return NULL; /* We expected 2 more bytes with the length. */ + len = ((size_t)s[0] << 8) | s[1]; + s += 2; n -= 2; + } + else + return NULL; /* APDU limit is 65535, thus it does not make + sense to assume longer length fields. */ + + if (composite && nestlevel < 100) + { /* Dive into this composite DO after checking for a too deep + nesting. */ + const unsigned char *tmp_s; + size_t tmp_len; + + tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1); + if (tmp_s) + { + *nbytes = tmp_len; + return tmp_s; + } + } + + if (this_tag == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* Buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found or if the object does not fit into the buffer. */ +const unsigned char * +find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + const unsigned char *p; + + p = do_find_tlv (buffer, length, tag, nbytes, 0); + if (p && *nbytes > (length - (p-buffer))) + p = NULL; /* Object longer than buffer. */ + return p; +} + + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +const unsigned char * +find_tlv_unchecked (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + return do_find_tlv (buffer, length, tag, nbytes, 0); +} + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t +parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, int *r_ndef, + size_t *r_length, size_t *r_nhdr) +{ + int c; + unsigned long tag; + const unsigned char *buf = *buffer; + size_t length = *size; + + *r_ndef = 0; + *r_length = 0; + *r_nhdr = 0; + + /* Get the tag. */ + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + *r_class = (c & 0xc0) >> 6; + *r_constructed = !!(c & 0x20); + tag = c & 0x1f; + + if (tag == 0x1f) + { + tag = 0; + do + { + tag <<= 7; + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + tag |= c & 0x7f; + + } + while (c & 0x80); + } + *r_tag = tag; + + /* Get the length. */ + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + if ( !(c & 0x80) ) + *r_length = c; + else if (c == 0x80) + *r_ndef = 1; + else if (c == 0xff) + return gpg_err_make (default_errsource, GPG_ERR_BAD_BER); + else + { + unsigned long len = 0; + int count = c & 0x7f; + + if (count > sizeof (len) || count > sizeof (size_t)) + return gpg_err_make (default_errsource, GPG_ERR_BAD_BER); + + for (; count; count--) + { + len <<= 8; + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + len |= c & 0xff; + } + *r_length = len; + } + + /* Without this kludge some example certs can't be parsed. */ + if (*r_class == CLASS_UNIVERSAL && !*r_tag) + *r_length = 0; + + *buffer = buf; + *size = length; + return 0; +} + + +/* FIXME: The following function should not go into this file but for + now it is easier to keep it here. */ + +/* Return the next token of an canonical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the original buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error an error code + is returned and no pointer is not guaranteed to point to + a meaningful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return. + + depth = 0; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth) + process_token (tok, toklen); + if (err) + handle_error (); + */ +gpg_error_t +parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen) +{ + const unsigned char *s; + size_t n, vlen; + + s = *buf; + n = *buflen; + *tok = NULL; + *toklen = 0; + if (!n) + return *depth ? gpg_err_make (default_errsource, GPG_ERR_INV_SEXP) : 0; + if (*s == '(') + { + s++; n--; + (*depth)++; + *buf = s; + *buflen = n; + return 0; + } + if (*s == ')') + { + if (!*depth) + return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + *toklen = 1; + s++; n--; + (*depth)--; + *buf = s; + *buflen = n; + return 0; + } + for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--) + vlen = vlen*10 + (*s - '0'); + if (!n || *s != ':') + return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + s++; n--; + if (vlen > n) + return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + *tok = s; + *toklen = vlen; + s += vlen; + n -= vlen; + *buf = s; + *buflen = n; + return 0; +} diff --git a/common/tlv.h b/common/tlv.h new file mode 100644 index 0000000..ba4ea2e --- /dev/null +++ b/common/tlv.h @@ -0,0 +1,116 @@ +/* tlv.h - Tag-Length-Value Utilities + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef SCD_TLV_H +#define SCD_TLV_H 1 + + +enum tlv_tag_class { + CLASS_UNIVERSAL = 0, + CLASS_APPLICATION = 1, + CLASS_CONTEXT = 2, + CLASS_PRIVATE =3 +}; + +enum tlv_tag_type { + TAG_NONE = 0, + TAG_BOOLEAN = 1, + TAG_INTEGER = 2, + TAG_BIT_STRING = 3, + TAG_OCTET_STRING = 4, + TAG_NULL = 5, + TAG_OBJECT_ID = 6, + TAG_OBJECT_DESCRIPTOR = 7, + TAG_EXTERNAL = 8, + TAG_REAL = 9, + TAG_ENUMERATED = 10, + TAG_EMBEDDED_PDV = 11, + TAG_UTF8_STRING = 12, + TAG_REALTIVE_OID = 13, + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_NUMERIC_STRING = 18, + TAG_PRINTABLE_STRING = 19, + TAG_TELETEX_STRING = 20, + TAG_VIDEOTEX_STRING = 21, + TAG_IA5_STRING = 22, + TAG_UTC_TIME = 23, + TAG_GENERALIZED_TIME = 24, + TAG_GRAPHIC_STRING = 25, + TAG_VISIBLE_STRING = 26, + TAG_GENERAL_STRING = 27, + TAG_UNIVERSAL_STRING = 28, + TAG_CHARACTER_STRING = 29, + TAG_BMP_STRING = 30 +}; + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found or if the object does not fit into the buffer. */ +const unsigned char *find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes); + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found. Note, that the function does not check whether + the value fits into the provided buffer.*/ +const unsigned char *find_tlv_unchecked (const unsigned char *buffer, + size_t length, + int tag, size_t *nbytes); + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, + int *r_ndef, size_t *r_length, size_t *r_nhdr); + + +/* Return the next token of an canonical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the original buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error an error code + is returned and no pointer is not guaranteed to point to + a meaningful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return. */ +gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen); + + + +#endif /* SCD_TLV_H */ diff --git a/common/ttyio.c b/common/ttyio.c new file mode 100644 index 0000000..5fb620d --- /dev/null +++ b/common/ttyio.c @@ -0,0 +1,766 @@ +/* ttyio.c - tty i/O functions + * Copyright (C) 1998,1999,2000,2001,2002,2003,2004,2006,2007, + * 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM) +# define USE_W32_CONSOLE 1 +#endif + +#ifdef HAVE_TCGETATTR +#include +#else +#ifdef HAVE_TERMIO_H +/* simulate termios with termio */ +#include +#define termios termio +#define tcsetattr ioctl +#define TCSAFLUSH TCSETAF +#define tcgetattr(A,B) ioctl(A,TCGETA,B) +#define HAVE_TCGETATTR +#endif +#endif +#ifdef USE_W32_CONSOLE +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +# ifdef HAVE_TCGETATTR +# error mingw32 and termios +# endif +#endif +#include +#include + +#include "util.h" +#include "ttyio.h" +#include "common-defs.h" + +#define CONTROL_D ('D' - 'A' + 1) + + +#ifdef USE_W32_CONSOLE +static struct { + HANDLE in, out; +} con; +#define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \ + |ENABLE_PROCESSED_INPUT ) +#define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT ) +#define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT) + +#else /* yeah, we have a real OS */ +static FILE *ttyfp = NULL; +#endif + +static int initialized; +static int last_prompt_len; +static int batchmode; +static int no_terminal; + +#ifdef HAVE_TCGETATTR + static struct termios termsave; + static int restore_termios; +#endif + +/* Hooks set by gpgrlhelp.c if required. */ +static void (*my_rl_set_completer) (rl_completion_func_t *); +static void (*my_rl_inhibit_completion) (int); +static void (*my_rl_cleanup_after_signal) (void); +static void (*my_rl_init_stream) (FILE *); +static char *(*my_rl_readline) (const char*); +static void (*my_rl_add_history) (const char*); + + +/* This is a wrapper around ttyname so that we can use it even when + the standard streams are redirected. It figures the name out the + first time and returns it in a statically allocated buffer. */ +const char * +tty_get_ttyname (void) +{ + static char *name; + + /* On a GNU system ctermid() always return /dev/tty, so this does + not make much sense - however if it is ever changed we do the + Right Thing now. */ +#ifdef HAVE_CTERMID + static int got_name; + + if (!got_name) + { + const char *s; + /* Note that despite our checks for these macros the function is + not necessarily thread save. We mainly do this for + portability reasons, in case L_ctermid is not defined. */ +# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) || defined(_POSIX_TRHEADS) + char buffer[L_ctermid]; + s = ctermid (buffer); +# else + s = ctermid (NULL); +# endif + if (s) + name = strdup (s); + got_name = 1; + } +#endif /*HAVE_CTERMID*/ + /* Assume the standard tty on memory error or when there is no + ctermid. */ + return name? name : "/dev/tty"; +} + + + +#ifdef HAVE_TCGETATTR +static void +cleanup(void) +{ + if( restore_termios ) { + restore_termios = 0; /* do it prios in case it is interrupted again */ + if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) ) + log_error("tcsetattr() failed: %s\n", strerror(errno) ); + } +} +#endif + +static void +init_ttyfp(void) +{ + if( initialized ) + return; + +#if defined(USE_W32_CONSOLE) + { + SECURITY_ATTRIBUTES sa; + + memset(&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0 ); + if( con.out == INVALID_HANDLE_VALUE ) + log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() ); + memset(&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0 ); + if( con.in == INVALID_HANDLE_VALUE ) + log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() ); + } + SetConsoleMode(con.in, DEF_INPMODE ); + SetConsoleMode(con.out, DEF_OUTMODE ); + +#elif defined(__EMX__) + ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */ + if (my_rl_init_stream) + my_rl_init_stream (ttyfp); +#elif defined (HAVE_W32CE_SYSTEM) + ttyfp = stderr; +#else + ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+"); + if( !ttyfp ) { + log_error("cannot open '%s': %s\n", tty_get_ttyname (), + strerror(errno) ); + exit(2); + } + if (my_rl_init_stream) + my_rl_init_stream (ttyfp); +#endif + + +#ifdef HAVE_TCGETATTR + atexit( cleanup ); +#endif + initialized = 1; +} + + +int +tty_batchmode( int onoff ) +{ + int old = batchmode; + if( onoff != -1 ) + batchmode = onoff; + return old; +} + +int +tty_no_terminal(int onoff) +{ + int old = no_terminal; + no_terminal = onoff ? 1 : 0; + return old; +} + +void +tty_printf( const char *fmt, ... ) +{ + va_list arg_ptr; + + if (no_terminal) + return; + + if( !initialized ) + init_ttyfp(); + + va_start( arg_ptr, fmt ) ; +#ifdef USE_W32_CONSOLE + { + char *buf = NULL; + int n; + DWORD nwritten; + + n = vasprintf(&buf, fmt, arg_ptr); + if( !buf ) + log_bug("vasprintf() failed\n"); + + if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) ) + log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() ); + if( n != nwritten ) + log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten ); + last_prompt_len += n; + xfree (buf); + } +#else + last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ; + fflush(ttyfp); +#endif + va_end(arg_ptr); +} + + +/* Same as tty_printf but if FP is not NULL, behave like a regular + fprintf. */ +void +tty_fprintf (estream_t fp, const char *fmt, ... ) +{ + va_list arg_ptr; + + if (fp) + { + va_start (arg_ptr, fmt) ; + es_vfprintf (fp, fmt, arg_ptr ); + va_end (arg_ptr); + return; + } + + if (no_terminal) + return; + + if (!initialized) + init_ttyfp (); + + va_start (arg_ptr, fmt); +#ifdef USE_W32_CONSOLE + { + char *buf = NULL; + int n; + DWORD nwritten; + + n = vasprintf(&buf, fmt, arg_ptr); + if (!buf) + log_bug("vasprintf() failed\n"); + + if (!WriteConsoleA( con.out, buf, n, &nwritten, NULL )) + log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() ); + if (n != nwritten) + log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten ); + last_prompt_len += n; + xfree (buf); + } +#else + last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ; + fflush(ttyfp); +#endif + va_end(arg_ptr); +} + + +/**************** + * Print a string, but filter all control characters out. If FP is + * not NULL print to that stream instead to the tty. + */ +void +tty_print_string (estream_t fp, const byte *p, size_t n ) +{ + if (no_terminal && !fp) + return; + + if( !initialized & !fp) + init_ttyfp(); + +#ifdef USE_W32_CONSOLE + /* not so effective, change it if you want */ + if (fp) + { + for( ; n; n--, p++ ) + { + if( iscntrl( *p ) ) + { + if( *p == '\n' ) + tty_fprintf (fp, "\\n"); + else if( !*p ) + tty_fprintf (fp, "\\0"); + else + tty_fprintf (fp, "\\x%02x", *p); + } + else + tty_fprintf (fp, "%c", *p); + } + } + else + { + for( ; n; n--, p++ ) + { + if( iscntrl( *p ) ) + { + if( *p == '\n' ) + tty_printf ("\\n"); + else if( !*p ) + tty_printf ("\\0"); + else + tty_printf ("\\x%02x", *p); + } + else + tty_printf ("%c", *p); + } + } +#else + if (fp) + { + for( ; n; n--, p++ ) + { + if (iscntrl (*p)) + { + es_putc ('\\', fp); + if ( *p == '\n' ) + es_putc ('n', fp); + else if ( !*p ) + es_putc ('0', fp); + else + es_fprintf (fp, "x%02x", *p); + } + else + es_putc (*p, fp); + } + } + else + { + for (; n; n--, p++) + { + if (iscntrl (*p)) + { + putc ('\\', ttyfp); + if ( *p == '\n' ) + putc ('n', ttyfp); + else if ( !*p ) + putc ('0', ttyfp); + else + fprintf (ttyfp, "x%02x", *p ); + } + else + putc (*p, ttyfp); + } + } +#endif +} + +void +tty_print_utf8_string2 (estream_t fp, const byte *p, size_t n, size_t max_n) +{ + size_t i; + char *buf; + + if (no_terminal && !fp) + return; + + /* we can handle plain ascii simpler, so check for it first */ + for(i=0; i < n; i++ ) { + if( p[i] & 0x80 ) + break; + } + if( i < n ) { + buf = utf8_to_native( (const char *)p, n, 0 ); + if( max_n && (strlen( buf ) > max_n )) { + buf[max_n] = 0; + } + /*(utf8 conversion already does the control character quoting)*/ + tty_fprintf (fp, "%s", buf); + xfree (buf); + } + else { + if( max_n && (n > max_n) ) { + n = max_n; + } + tty_print_string (fp, p, n ); + } +} + + +void +tty_print_utf8_string( const byte *p, size_t n ) +{ + tty_print_utf8_string2 (NULL, p, n, 0); +} + + +static char * +do_get( const char *prompt, int hidden ) +{ + char *buf; +#ifndef __riscos__ + byte cbuf[1]; +#endif + int c, n, i; + + if( batchmode ) { + log_error("Sorry, we are in batchmode - can't get input\n"); + exit(2); + } + + if (no_terminal) { + log_error("Sorry, no terminal at all requested - can't get input\n"); + exit(2); + } + + if( !initialized ) + init_ttyfp(); + + last_prompt_len = 0; + tty_printf( "%s", prompt ); + buf = xmalloc((n=50)); + i = 0; + +#ifdef USE_W32_CONSOLE + if( hidden ) + SetConsoleMode(con.in, HID_INPMODE ); + + for(;;) { + DWORD nread; + + if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) ) + log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() ); + if( !nread ) + continue; + if( *cbuf == '\n' ) + break; + + if( !hidden ) + last_prompt_len++; + c = *cbuf; + if( c == '\t' ) + c = ' '; + else if( c > 0xa0 ) + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + else if( iscntrl(c) ) + continue; + if( !(i < n-1) ) { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; + } + + if( hidden ) + SetConsoleMode(con.in, DEF_INPMODE ); + +#elif defined(__riscos__) || defined(HAVE_W32CE_SYSTEM) + do { +#ifdef HAVE_W32CE_SYSTEM + /* Using getchar is not a correct solution but for now it + doesn't matter because we have no real console at all. We + should rework this as soon as we have switched this entire + module to estream. */ + c = getchar(); +#else + c = riscos_getchar(); +#endif + if (c == 0xa || c == 0xd) { /* Return || Enter */ + c = (int) '\n'; + } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */ + if (i>0) { + i--; + if (!hidden) { + last_prompt_len--; + fputc(8, ttyfp); + fputc(32, ttyfp); + fputc(8, ttyfp); + fflush(ttyfp); + } + } else { + fputc(7, ttyfp); + fflush(ttyfp); + } + continue; + } else if (c == (int) '\t') { /* Tab */ + c = ' '; + } else if (c > 0xa0) { + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + } else if (iscntrl(c)) { + continue; + } + if(!(i < n-1)) { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; + if (!hidden) { + last_prompt_len++; + fputc(c, ttyfp); + fflush(ttyfp); + } + } while (c != '\n'); + i = (i>0) ? i-1 : 0; +#else /* Other systems. */ + if( hidden ) { +#ifdef HAVE_TCGETATTR + struct termios term; + + if( tcgetattr(fileno(ttyfp), &termsave) ) + log_fatal("tcgetattr() failed: %s\n", strerror(errno) ); + restore_termios = 1; + term = termsave; + term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) ) + log_fatal("tcsetattr() failed: %s\n", strerror(errno) ); +#endif + } + + /* fixme: How can we avoid that the \n is echoed w/o disabling + * canonical mode - w/o this kill_prompt can't work */ + while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) { + if( !hidden ) + last_prompt_len++; + c = *cbuf; + if( c == CONTROL_D ) + log_info("control d found\n"); + if( c == '\t' ) + c = ' '; + else if( c > 0xa0 ) + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + else if( iscntrl(c) ) + continue; + if( !(i < n-1) ) { + n += 50; + buf = xrealloc (buf, n ); + } + buf[i++] = c; + } + if( *cbuf != '\n' ) { + buf[0] = CONTROL_D; + i = 1; + } + + if( hidden ) { +#ifdef HAVE_TCGETATTR + if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) ) + log_error("tcsetattr() failed: %s\n", strerror(errno) ); + restore_termios = 0; +#endif + } +#endif /* end unix version */ + buf[i] = 0; + return buf; +} + + +char * +tty_get( const char *prompt ) +{ + if (!batchmode && !no_terminal && my_rl_readline && my_rl_add_history) + { + char *line; + char *buf; + + if (!initialized) + init_ttyfp(); + + last_prompt_len = 0; + + line = my_rl_readline (prompt?prompt:""); + + /* We need to copy it to memory controlled by our malloc + implementations; further we need to convert an EOF to our + convention. */ + buf = xmalloc(line? strlen(line)+1:2); + if (line) + { + strcpy (buf, line); + trim_spaces (buf); + if (strlen (buf) > 2 ) + my_rl_add_history (line); /* Note that we test BUF but add LINE. */ + free (line); + } + else + { + buf[0] = CONTROL_D; + buf[1] = 0; + } + return buf; + } + else + return do_get ( prompt, 0 ); +} + +/* Variable argument version of tty_get. The prompt is is actually a + format string with arguments. */ +char * +tty_getf (const char *promptfmt, ... ) +{ + va_list arg_ptr; + char *prompt; + char *answer; + + va_start (arg_ptr, promptfmt); + if (gpgrt_vasprintf (&prompt, promptfmt, arg_ptr) < 0) + log_fatal ("estream_vasprintf failed: %s\n", strerror (errno)); + va_end (arg_ptr); + answer = tty_get (prompt); + xfree (prompt); + return answer; +} + + + +char * +tty_get_hidden( const char *prompt ) +{ + return do_get( prompt, 1 ); +} + + +void +tty_kill_prompt() +{ + if ( no_terminal ) + return; + + if( !initialized ) + init_ttyfp(); + + if( batchmode ) + last_prompt_len = 0; + if( !last_prompt_len ) + return; +#ifdef USE_W32_CONSOLE + tty_printf("\r%*s\r", last_prompt_len, ""); +#else + { + int i; + putc('\r', ttyfp); + for(i=0; i < last_prompt_len; i ++ ) + putc(' ', ttyfp); + putc('\r', ttyfp); + fflush(ttyfp); + } +#endif + last_prompt_len = 0; +} + + +int +tty_get_answer_is_yes( const char *prompt ) +{ + int yes; + char *p = tty_get( prompt ); + tty_kill_prompt(); + yes = answer_is_yes(p); + xfree(p); + return yes; +} + + +/* Called by gnupg_rl_initialize to setup the readline support. */ +void +tty_private_set_rl_hooks (void (*init_stream) (FILE *), + void (*set_completer) (rl_completion_func_t*), + void (*inhibit_completion) (int), + void (*cleanup_after_signal) (void), + char *(*readline_fun) (const char*), + void (*add_history_fun) (const char*)) +{ + my_rl_init_stream = init_stream; + my_rl_set_completer = set_completer; + my_rl_inhibit_completion = inhibit_completion; + my_rl_cleanup_after_signal = cleanup_after_signal; + my_rl_readline = readline_fun; + my_rl_add_history = add_history_fun; +} + + +#ifdef HAVE_LIBREADLINE +void +tty_enable_completion (rl_completion_func_t *completer) +{ + if (no_terminal || !my_rl_set_completer ) + return; + + if (!initialized) + init_ttyfp(); + + my_rl_set_completer (completer); +} + +void +tty_disable_completion (void) +{ + if (no_terminal || !my_rl_inhibit_completion) + return; + + if (!initialized) + init_ttyfp(); + + my_rl_inhibit_completion (1); +} +#endif + +void +tty_cleanup_after_signal (void) +{ +#ifdef HAVE_TCGETATTR + cleanup (); +#endif +} + +void +tty_cleanup_rl_after_signal (void) +{ + if (my_rl_cleanup_after_signal) + my_rl_cleanup_after_signal (); +} diff --git a/common/ttyio.h b/common/ttyio.h new file mode 100644 index 0000000..004aa85 --- /dev/null +++ b/common/ttyio.h @@ -0,0 +1,74 @@ +/* ttyio.h + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, + * 2009 Free Software Foundation, Inc. + * + * This file is part of GNUPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ +#ifndef GNUPG_COMMON_TTYIO_H +#define GNUPG_COMMON_TTYIO_H + +#include "util.h" /* Make sure our readline typedef is available. */ + + +const char *tty_get_ttyname (void); +int tty_batchmode (int onoff); +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) +void tty_printf (const char *fmt, ... ) + __attribute__ ((format (printf,1,2))); +void tty_fprintf (estream_t fp, const char *fmt, ... ) + __attribute__ ((format (printf,2,3))); +char *tty_getf (const char *promptfmt, ... ) + __attribute__ ((format (printf,1,2))); +#else +void tty_printf (const char *fmt, ... ); +void tty_fprintf (estream_t fp, const char *fmt, ... ); +char *tty_getf (const char *promptfmt, ... ); +#endif +void tty_print_string (estream_t fp, const unsigned char *p, size_t n); +void tty_print_utf8_string (const unsigned char *p, size_t n); +void tty_print_utf8_string2 (estream_t fp, + const unsigned char *p, size_t n, size_t max_n); +char *tty_get (const char *prompt); +char *tty_get_hidden (const char *prompt); +void tty_kill_prompt (void); +int tty_get_answer_is_yes (const char *prompt); +int tty_no_terminal (int onoff); + +#ifdef HAVE_LIBREADLINE +void tty_enable_completion (rl_completion_func_t *completer); +void tty_disable_completion (void); +#else +/* Use a macro to stub out these functions since a macro has no need + to typedef a "rl_completion_func_t" which would be undefined + without readline. */ +#define tty_enable_completion(x) +#define tty_disable_completion() +#endif +void tty_cleanup_after_signal (void); +void tty_cleanup_rl_after_signal (void); + + +#endif /*GNUPG_COMMON_TTYIO_H*/ diff --git a/common/types.h b/common/types.h new file mode 100644 index 0000000..7d85a35 --- /dev/null +++ b/common/types.h @@ -0,0 +1,116 @@ +/* types.h - define some extra types + * Copyright (C) 1999, 2000, 2001, 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_TYPES_H +#define GNUPG_COMMON_TYPES_H + +#ifdef HAVE_INTTYPES_H +# include +#endif + +/* The AC_CHECK_SIZEOF() in configure fails for some machines. + * we provide some fallback values here */ +#if !SIZEOF_UNSIGNED_SHORT +# undef SIZEOF_UNSIGNED_SHORT +# define SIZEOF_UNSIGNED_SHORT 2 +#endif +#if !SIZEOF_UNSIGNED_INT +# undef SIZEOF_UNSIGNED_INT +# define SIZEOF_UNSIGNED_INT 4 +#endif +#if !SIZEOF_UNSIGNED_LONG +# undef SIZEOF_UNSIGNED_LONG +# define SIZEOF_UNSIGNED_LONG 4 +#endif + + +#include + + +/* We use byte as an abbreviation for unsigned char. On some + platforms this needs special treatment: + + - RISC OS: + Norcroft C treats char = unsigned char as legal assignment + but char* = unsigned char* as illegal assignment + and the same applies to the signed variants as well. Thus we use + char which is anyway unsigned. + + - Windows: + Windows typedefs byte in the RPC headers but we need to avoid a + warning about a double definition. + */ +#ifndef HAVE_BYTE_TYPEDEF +# undef byte /* There might be a macro with this name. */ +# ifdef __riscos__ + typedef char byte; +# elif !(defined(_WIN32) && defined(cbNDRContext)) + typedef unsigned char byte; +# endif +# define HAVE_BYTE_TYPEDEF +#endif /*!HAVE_BYTE_TYPEDEF*/ + +#ifndef HAVE_USHORT_TYPEDEF +# undef ushort /* There might be a macro with this name. */ + typedef unsigned short ushort; +# define HAVE_USHORT_TYPEDEF +#endif + +#ifndef HAVE_ULONG_TYPEDEF +# undef ulong /* There might be a macro with this name. */ + typedef unsigned long ulong; +# define HAVE_ULONG_TYPEDEF +#endif + +#ifndef HAVE_U16_TYPEDEF +# undef u16 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 2 + typedef unsigned int u16; +# elif SIZEOF_UNSIGNED_SHORT == 2 + typedef unsigned short u16; +# else +# error no typedef for u16 +# endif +# define HAVE_U16_TYPEDEF +#endif + +#ifndef HAVE_U32_TYPEDEF +# undef u32 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 4 + typedef unsigned int u32; +# elif SIZEOF_UNSIGNED_LONG == 4 + typedef unsigned long u32; +# else +# error no typedef for u32 +# endif +# define HAVE_U32_TYPEDEF +#endif + +#endif /*GNUPG_COMMON_TYPES_H*/ diff --git a/common/userids.c b/common/userids.c new file mode 100644 index 0000000..01f2cd8 --- /dev/null +++ b/common/userids.c @@ -0,0 +1,434 @@ +/* userids.c - Utility functions for user ids. + * Copyright (C) 2001, 2003, 2004, 2006, + * 2009 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" +#include "userids.h" + + +/* Parse the user-id NAME and build a search description for it. + * Returns 0 on success or an error code. DESC may be NULL to merely + * check the validity of a user-id. + * + * Some used rules: + * - If the username starts with 8,9,16 or 17 hex-digits (the first one + * must be in the range 0..9), this is considered a keyid; depending + * on the length a short or complete one. + * - If the username starts with 32,33,40 or 41 hex-digits (the first one + * must be in the range 0..9), this is considered a fingerprint. + * - If the username starts with a left angle, we assume it is a complete + * email address and look only at this part. + * - If the username starts with a colon we assume it is a unified + * key specfification. + * - If the username starts with a '.', we assume it is the ending + * part of an email address + * - If the username starts with an '@', we assume it is a part of an + * email address + * - If the userid start with an '=' an exact compare is done. + * - If the userid starts with a '*' a case insensitive substring search is + * done (This is the default). + * - If the userid starts with a '+' we will compare individual words + * and a match requires that all the words are in the userid. + * Words are delimited by white space or "()<>[]{}.@-+_,;/&!" + * (note that you can't search for these characters). Compare + * is not case sensitive. + * - If the userid starts with a '&' a 40 hex digits keygrip is expected. + */ + +gpg_error_t +classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) +{ + const char *s; + char *s2 = NULL; + int rc = 0; + int hexprefix = 0; + int hexlength; + int mode = 0; + KEYDB_SEARCH_DESC dummy_desc; + + if (!desc) + desc = &dummy_desc; + + /* Clear the structure so that the mode field is set to zero unless + we set it to the correct value right at the end of this + function. */ + memset (desc, 0, sizeof *desc); + + /* Skip leading and trailing spaces. */ + for(s = name; *s && spacep (s); s++ ) + ; + if (*s && spacep (s + strlen(s) - 1)) + { + s2 = xtrystrdup (s); + if (!s2) + { + rc = gpg_error_from_syserror (); + goto out; + } + trim_trailing_spaces (s2); + s = s2; + } + + switch (*s) + { + case 0: /* Empty string is an error. */ + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + + case '.': /* An email address, compare from end. Note that this + has not yet been implemented in the search code. */ + mode = KEYDB_SEARCH_MODE_MAILEND; + s++; + desc->u.name = s; + break; + + case '<': /* An email address. */ + mode = KEYDB_SEARCH_MODE_MAIL; + /* FIXME: The keyring code in g10 assumes that the mail name is + prefixed with an '<'. However the keybox code used for sm/ + assumes it has been removed. For now we use this simple hack + to overcome the problem. */ + if (!openpgp_hack) + s++; + desc->u.name = s; + break; + + case '@': /* Part of an email address. */ + mode = KEYDB_SEARCH_MODE_MAILSUB; + s++; + desc->u.name = s; + break; + + case '=': /* Exact compare. */ + mode = KEYDB_SEARCH_MODE_EXACT; + s++; + desc->u.name = s; + break; + + case '*': /* Case insensitive substring search. */ + mode = KEYDB_SEARCH_MODE_SUBSTR; + s++; + desc->u.name = s; + break; + + case '+': /* Compare individual words. Note that this has not + yet been implemented in the search code. */ + mode = KEYDB_SEARCH_MODE_WORDS; + s++; + desc->u.name = s; + break; + + case '/': /* Subject's DN. */ + s++; + if (!*s || spacep (s)) /* No DN or prefixed with a space. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBJECT; + break; + + case '#': /* S/N with optional issuer id or just issuer id. */ + { + const char *si; + + s++; + if ( *s == '/') + { /* "#/" indicates an issuer's DN. */ + s++; + if (!*s || spacep (s)) /* No DN or prefixed with a space. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_ISSUER; + } + else + { /* Serialnumber + optional issuer ID. */ + for (si=s; *si && *si != '/'; si++) + { + /* Check for an invalid digit in the serial number. */ + if (!strchr("01234567890abcdefABCDEF", *si)) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + } + desc->sn = (const unsigned char*)s; + desc->snlen = -1; + if (!*si) + mode = KEYDB_SEARCH_MODE_SN; + else + { + s = si+1; + if (!*s || spacep (s)) /* No DN or prefixed with a space. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_ISSUER_SN; + } + } + } + break; + + case ':': /* Unified fingerprint. */ + { + const char *se, *si; + int i; + + se = strchr (++s,':'); + if (!se) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + for (i=0,si=s; si < se; si++, i++ ) + { + if (!strchr("01234567890abcdefABCDEF", *si)) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */ + goto out; + } + } + if (i != 32 && i != 40) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */ + goto out; + } + for (i=0,si=s; si < se; i++, si +=2) + desc->u.fpr[i] = hextobyte(si); + for (; i < 20; i++) + desc->u.fpr[i]= 0; + mode = KEYDB_SEARCH_MODE_FPR; + } + break; + + case '&': /* Keygrip*/ + { + if (hex2bin (s+1, desc->u.grip, 20) < 0) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */ + goto out; + } + mode = KEYDB_SEARCH_MODE_KEYGRIP; + } + break; + + default: + if (s[0] == '0' && s[1] == 'x') + { + hexprefix = 1; + s += 2; + } + + hexlength = strspn(s, "0123456789abcdefABCDEF"); + if (hexlength >= 8 && s[hexlength] =='!') + { + desc->exact = 1; + hexlength++; /* Just for the following check. */ + } + + /* Check if a hexadecimal number is terminated by EOS or blank. */ + if (hexlength && s[hexlength] && !spacep (s+hexlength)) + { + if (hexprefix) /* A "0x" prefix without a correct + termination is an error. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + /* The first characters looked like a hex number, but the + entire string is not. */ + hexlength = 0; + } + + if (desc->exact) + hexlength--; /* Remove the bang. */ + + if ((hexlength == 8 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 9 && *s == '0')) + { + /* Short keyid. */ + if (hexlength == 9) + s++; + desc->u.kid[1] = strtoul( s, NULL, 16 ); + mode = KEYDB_SEARCH_MODE_SHORT_KID; + } + else if ((hexlength == 16 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 17 && *s == '0')) + { + /* Long keyid. */ + char buf[9]; + if (hexlength == 17) + s++; + mem2str (buf, s, 9); + desc->u.kid[0] = strtoul (buf, NULL, 16); + desc->u.kid[1] = strtoul (s+8, NULL, 16); + mode = KEYDB_SEARCH_MODE_LONG_KID; + } + else if ((hexlength == 32 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 33 && *s == '0')) + { + /* MD5 fingerprint. */ + int i; + if (hexlength == 33) + s++; + memset (desc->u.fpr+16, 0, 4); + for (i=0; i < 16; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.fpr[i] = c; + } + mode = KEYDB_SEARCH_MODE_FPR16; + } + else if ((hexlength == 40 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 41 && *s == '0')) + { + /* SHA1/RMD160 fingerprint. */ + int i; + if (hexlength == 41) + s++; + for (i=0; i < 20; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.fpr[i] = c; + } + mode = KEYDB_SEARCH_MODE_FPR20; + } + else if (!hexprefix) + { + /* The fingerprint in an X.509 listing is often delimited by + colons, so we try to single this case out. */ + mode = 0; + hexlength = strspn (s, ":0123456789abcdefABCDEF"); + if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) + { + int i; + + for (i=0; i < 20; i++, s += 3) + { + int c = hextobyte(s); + if (c == -1 || (i < 19 && s[2] != ':')) + break; + desc->u.fpr[i] = c; + } + if (i == 20) + mode = KEYDB_SEARCH_MODE_FPR20; + } + if (!mode) + { + /* Still not found. Now check for a space separated + OpenPGP v4 fingerprint like: + 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + or + 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + */ + hexlength = strspn (s, " 0123456789abcdefABCDEF"); + if (s[hexlength] && s[hexlength] != ' ') + hexlength = 0; /* Followed by non-space. */ + while (hexlength && s[hexlength-1] == ' ') + hexlength--; /* Trim trailing spaces. */ + if ((hexlength == 49 || hexlength == 50) + && (!s[hexlength] || s[hexlength] == ' ')) + { + int i, c; + + for (i=0; i < 20; i++) + { + if (i && !(i % 2)) + { + if (*s != ' ') + break; + s++; + /* Skip the double space in the middle but + don't require it to help copying + fingerprints from sources which fold + multiple space to one. */ + if (i == 10 && *s == ' ') + s++; + } + + c = hextobyte(s); + if (c == -1) + break; + desc->u.fpr[i] = c; + s += 2; + } + if (i == 20) + mode = KEYDB_SEARCH_MODE_FPR20; + } + } + if (!mode) /* Default to substring search. */ + { + desc->exact = 0; + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBSTR; + } + } + else + { + /* Hex number with a prefix but with a wrong length. */ + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + } + + desc->mode = mode; + out: + xfree (s2); + return rc; +} diff --git a/common/userids.h b/common/userids.h new file mode 100644 index 0000000..c60bc33 --- /dev/null +++ b/common/userids.h @@ -0,0 +1,39 @@ +/* userids.h - Utility functions for user ids. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_USERIDS_H +#define GNUPG_COMMON_USERIDS_H + +#include "../kbx/keybox-search-desc.h" + +gpg_error_t classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, + int openpgp_hack); + + +#endif /*GNUPG_COMMON_USERIDS_H*/ diff --git a/common/utf8conv.c b/common/utf8conv.c new file mode 100644 index 0000000..bce9e3a --- /dev/null +++ b/common/utf8conv.c @@ -0,0 +1,838 @@ +/* utf8conf.c - UTF8 character set conversion + * Copyright (C) 1994, 1998, 1999, 2000, 2001, 2003, 2006, + * 2008, 2010 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_LANGINFO_CODESET +#include +#endif +#include + +#if HAVE_W32_SYSTEM +# /* Tell libgpg-error to provide the iconv macros. */ +# define GPGRT_ENABLE_W32_ICONV_MACROS 1 +#elif HAVE_ANDROID_SYSTEM +# /* No iconv support. */ +#else +# include +#endif + + +#include "util.h" +#include "common-defs.h" +#include "i18n.h" +#include "stringhelp.h" +#include "utf8conv.h" + +#ifndef MB_LEN_MAX +#define MB_LEN_MAX 16 +#endif + +static const char *active_charset_name = "iso-8859-1"; +static int no_translation; /* Set to true if we let simply pass through. */ +static int use_iconv; /* iconv conversion functions required. */ + + +#ifdef HAVE_ANDROID_SYSTEM +/* Fake stuff to get things building. */ +typedef void *iconv_t; +#define ICONV_CONST + +static iconv_t +iconv_open (const char *tocode, const char *fromcode) +{ + (void)tocode; + (void)fromcode; + return (iconv_t)(-1); +} + +static size_t +iconv (iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + (void)cd; + (void)inbuf; + (void)inbytesleft; + (void)outbuf; + (void)outbytesleft; + return (size_t)(0); +} + +static int +iconv_close (iconv_t cd) +{ + (void)cd; + return 0; +} +#endif /*HAVE_ANDROID_SYSTEM*/ + + +/* Error handler for iconv failures. This is needed to not clutter the + output with repeated diagnostics about a missing conversion. */ +static void +handle_iconv_error (const char *to, const char *from, int use_fallback) +{ + if (errno == EINVAL) + { + static int shown1, shown2; + int x; + + if (to && !strcmp (to, "utf-8")) + { + x = shown1; + shown1 = 1; + } + else + { + x = shown2; + shown2 = 1; + } + + if (!x) + log_info (_("conversion from '%s' to '%s' not available\n"), + from, to); + } + else + { + static int shown; + + if (!shown) + log_info (_("iconv_open failed: %s\n"), strerror (errno)); + shown = 1; + } + + if (use_fallback) + { + /* To avoid further error messages we fallback to UTF-8 for the + native encoding. Nowadays this seems to be the best bet in + case of errors from iconv or nl_langinfo. */ + active_charset_name = "utf-8"; + no_translation = 0; + use_iconv = 0; + } +} + + + +int +set_native_charset (const char *newset) +{ + const char *full_newset; + + if (!newset) + { +#ifdef HAVE_ANDROID_SYSTEM + newset = "utf-8"; +#elif defined HAVE_W32_SYSTEM + static char codepage[30]; + unsigned int cpno; + const char *aliases; + + /* We are a console program thus we need to use the + GetConsoleOutputCP function and not the the GetACP which + would give the codepage for a GUI program. Note this is not + a bulletproof detection because GetConsoleCP might return a + different one for console input. Not sure how to cope with + that. If the console Code page is not known we fall back to + the system code page. */ +#ifndef HAVE_W32CE_SYSTEM + cpno = GetConsoleOutputCP (); + if (!cpno) +#endif + cpno = GetACP (); + sprintf (codepage, "CP%u", cpno ); + /* Resolve alias. We use a long string string and not the usual + array to optimize if the code is taken to a DSO. Taken from + libiconv 1.9.2. */ + newset = codepage; + for (aliases = ("CP936" "\0" "GBK" "\0" + "CP1361" "\0" "JOHAB" "\0" + "CP20127" "\0" "ASCII" "\0" + "CP20866" "\0" "KOI8-R" "\0" + "CP21866" "\0" "KOI8-RU" "\0" + "CP28591" "\0" "ISO-8859-1" "\0" + "CP28592" "\0" "ISO-8859-2" "\0" + "CP28593" "\0" "ISO-8859-3" "\0" + "CP28594" "\0" "ISO-8859-4" "\0" + "CP28595" "\0" "ISO-8859-5" "\0" + "CP28596" "\0" "ISO-8859-6" "\0" + "CP28597" "\0" "ISO-8859-7" "\0" + "CP28598" "\0" "ISO-8859-8" "\0" + "CP28599" "\0" "ISO-8859-9" "\0" + "CP28605" "\0" "ISO-8859-15" "\0" + "CP65001" "\0" "UTF-8" "\0"); + *aliases; + aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1) + { + if (!strcmp (codepage, aliases) ||(*aliases == '*' && !aliases[1])) + { + newset = aliases + strlen (aliases) + 1; + break; + } + } + +#else /*!HAVE_W32_SYSTEM && !HAVE_ANDROID_SYSTEM*/ + +#ifdef HAVE_LANGINFO_CODESET + newset = nl_langinfo (CODESET); +#else /*!HAVE_LANGINFO_CODESET*/ + /* Try to get the used charset from environment variables. */ + static char codepage[30]; + const char *lc, *dot, *mod; + + strcpy (codepage, "iso-8859-1"); + lc = getenv ("LC_ALL"); + if (!lc || !*lc) + { + lc = getenv ("LC_CTYPE"); + if (!lc || !*lc) + lc = getenv ("LANG"); + } + if (lc && *lc) + { + dot = strchr (lc, '.'); + if (dot) + { + mod = strchr (++dot, '@'); + if (!mod) + mod = dot + strlen (dot); + if (mod - dot < sizeof codepage && dot != mod) + { + memcpy (codepage, dot, mod - dot); + codepage [mod - dot] = 0; + } + } + } + newset = codepage; +#endif /*!HAVE_LANGINFO_CODESET*/ +#endif /*!HAVE_W32_SYSTEM && !HAVE_ANDROID_SYSTEM*/ + } + + full_newset = newset; + if (strlen (newset) > 3 && !ascii_memcasecmp (newset, "iso", 3)) + { + newset += 3; + if (*newset == '-' || *newset == '_') + newset++; + } + + /* Note that we silently assume that plain ASCII is actually meant + as Latin-1. This makes sense because many Unix system don't have + their locale set up properly and thus would get annoying error + messages and we have to handle all the "bug" reports. Latin-1 has + traditionally been the character set used for 8 bit characters on + Unix systems. */ + if ( !*newset + || !ascii_strcasecmp (newset, "8859-1" ) + || !ascii_strcasecmp (newset, "646" ) + || !ascii_strcasecmp (newset, "ASCII" ) + || !ascii_strcasecmp (newset, "ANSI_X3.4-1968" ) + ) + { + active_charset_name = "iso-8859-1"; + no_translation = 0; + use_iconv = 0; + } + else if ( !ascii_strcasecmp (newset, "utf8" ) + || !ascii_strcasecmp(newset, "utf-8") ) + { + active_charset_name = "utf-8"; + no_translation = 1; + use_iconv = 0; + } + else + { + iconv_t cd; + + cd = iconv_open (full_newset, "utf-8"); + if (cd == (iconv_t)-1) + { + handle_iconv_error (full_newset, "utf-8", 0); + return -1; + } + iconv_close (cd); + cd = iconv_open ("utf-8", full_newset); + if (cd == (iconv_t)-1) + { + handle_iconv_error ("utf-8", full_newset, 0); + return -1; + } + iconv_close (cd); + active_charset_name = full_newset; + no_translation = 0; + use_iconv = 1; + } + return 0; +} + +const char * +get_native_charset () +{ + return active_charset_name; +} + +/* Return true if the native charset is utf-8. */ +int +is_native_utf8 (void) +{ + return no_translation; +} + + +/* Convert string, which is in native encoding to UTF8 and return a + new allocated UTF-8 string. This function terminates the process + on memory shortage. */ +char * +native_to_utf8 (const char *orig_string) +{ + const unsigned char *string = (const unsigned char *)orig_string; + const unsigned char *s; + char *buffer; + unsigned char *p; + size_t length = 0; + + if (no_translation) + { + /* Already utf-8 encoded. */ + buffer = xstrdup (orig_string); + } + else if (!use_iconv) + { + /* For Latin-1 we can avoid the iconv overhead. */ + for (s = string; *s; s++) + { + length++; + if (*s & 0x80) + length++; + } + buffer = xmalloc (length + 1); + for (p = (unsigned char *)buffer, s = string; *s; s++) + { + if ( (*s & 0x80 )) + { + *p++ = 0xc0 | ((*s >> 6) & 3); + *p++ = 0x80 | (*s & 0x3f); + } + else + *p++ = *s; + } + *p = 0; + } + else + { + /* Need to use iconv. */ + iconv_t cd; + const char *inptr; + char *outptr; + size_t inbytes, outbytes; + + cd = iconv_open ("utf-8", active_charset_name); + if (cd == (iconv_t)-1) + { + handle_iconv_error ("utf-8", active_charset_name, 1); + return native_to_utf8 (string); + } + + for (s=string; *s; s++ ) + { + length++; + if ((*s & 0x80)) + length += 5; /* We may need up to 6 bytes for the utf8 output. */ + } + buffer = xmalloc (length + 1); + + inptr = string; + inbytes = strlen (string); + outptr = buffer; + outbytes = length; + if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes, + &outptr, &outbytes) == (size_t)-1) + { + static int shown; + + if (!shown) + log_info (_("conversion from '%s' to '%s' failed: %s\n"), + active_charset_name, "utf-8", strerror (errno)); + shown = 1; + /* We don't do any conversion at all but use the strings as is. */ + strcpy (buffer, string); + } + else /* Success. */ + { + *outptr = 0; + /* We could realloc the buffer now but I doubt that it makes + much sense given that it will get freed anyway soon + after. */ + } + iconv_close (cd); + } + return buffer; +} + + + +static char * +do_utf8_to_native (const char *string, size_t length, int delim, + int with_iconv) +{ + int nleft; + int i; + unsigned char encbuf[8]; + int encidx; + const unsigned char *s; + size_t n; + char *buffer = NULL; + char *p = NULL; + unsigned long val = 0; + size_t slen; + int resync = 0; + + /* First pass (p==NULL): count the extended utf-8 characters. */ + /* Second pass (p!=NULL): create string. */ + for (;;) + { + for (slen = length, nleft = encidx = 0, n = 0, + s = (const unsigned char *)string; + slen; + s++, slen--) + { + if (resync) + { + if (!(*s < 128 || (*s >= 0xc0 && *s <= 0xfd))) + { + /* Still invalid. */ + if (p) + { + sprintf (p, "\\x%02x", *s); + p += 4; + } + n += 4; + continue; + } + resync = 0; + } + if (!nleft) + { + if (!(*s & 0x80)) + { + /* Plain ascii. */ + if ( delim != -1 + && (*s < 0x20 || *s == 0x7f || *s == delim + || (delim && *s == '\\'))) + { + n++; + if (p) + *p++ = '\\'; + switch (*s) + { + case '\n': n++; if ( p ) *p++ = 'n'; break; + case '\r': n++; if ( p ) *p++ = 'r'; break; + case '\f': n++; if ( p ) *p++ = 'f'; break; + case '\v': n++; if ( p ) *p++ = 'v'; break; + case '\b': n++; if ( p ) *p++ = 'b'; break; + case 0: n++; if ( p ) *p++ = '0'; break; + default: + n += 3; + if (p) + { + sprintf (p, "x%02x", *s); + p += 3; + } + break; + } + } + else + { + if (p) + *p++ = *s; + n++; + } + } + else if ((*s & 0xe0) == 0xc0) /* 110x xxxx */ + { + val = *s & 0x1f; + nleft = 1; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xf0) == 0xe0) /* 1110 xxxx */ + { + val = *s & 0x0f; + nleft = 2; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xf8) == 0xf0) /* 1111 0xxx */ + { + val = *s & 0x07; + nleft = 3; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xfc) == 0xf8) /* 1111 10xx */ + { + val = *s & 0x03; + nleft = 4; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xfe) == 0xfc) /* 1111 110x */ + { + val = *s & 0x01; + nleft = 5; + encidx = 0; + encbuf[encidx++] = *s; + } + else /* Invalid encoding: print as \xNN. */ + { + if (p) + { + sprintf (p, "\\x%02x", *s); + p += 4; + } + n += 4; + resync = 1; + } + } + else if (*s < 0x80 || *s >= 0xc0) /* Invalid utf-8 */ + { + if (p) + { + for (i = 0; i < encidx; i++) + { + sprintf (p, "\\x%02x", encbuf[i]); + p += 4; + } + sprintf (p, "\\x%02x", *s); + p += 4; + } + n += 4 + 4 * encidx; + nleft = 0; + encidx = 0; + resync = 1; + } + else + { + encbuf[encidx++] = *s; + val <<= 6; + val |= *s & 0x3f; + if (!--nleft) /* Ready. */ + { + if (no_translation) + { + if (p) + { + for (i = 0; i < encidx; i++) + *p++ = encbuf[i]; + } + n += encidx; + encidx = 0; + } + else if (with_iconv) + { + /* Our strategy for using iconv is a bit strange + but it better keeps compatibility with + previous versions in regard to how invalid + encodings are displayed. What we do is to + keep the utf-8 as is and have the real + translation step then at the end. Yes, I + know that this is ugly. However we are short + of the 1.4 release and for this branch we + should not mess too much around with iconv + things. One reason for this is that we don't + know enough about non-GNU iconv + implementation and want to minimize the risk + of breaking the code on too many platforms. */ + if ( p ) + { + for (i=0; i < encidx; i++ ) + *p++ = encbuf[i]; + } + n += encidx; + encidx = 0; + } + else /* Latin-1 case. */ + { + if (val >= 0x80 && val < 256) + { + /* We can simply print this character */ + n++; + if (p) + *p++ = val; + } + else + { + /* We do not have a translation: print utf8. */ + if (p) + { + for (i = 0; i < encidx; i++) + { + sprintf (p, "\\x%02x", encbuf[i]); + p += 4; + } + } + n += encidx * 4; + encidx = 0; + } + } + } + + } + } + if (!buffer) + { + /* Allocate the buffer after the first pass. */ + buffer = p = xmalloc (n + 1); + } + else if (with_iconv) + { + /* Note: See above for comments. */ + iconv_t cd; + const char *inptr; + char *outbuf, *outptr; + size_t inbytes, outbytes; + + *p = 0; /* Terminate the buffer. */ + + cd = iconv_open (active_charset_name, "utf-8"); + if (cd == (iconv_t)-1) + { + handle_iconv_error (active_charset_name, "utf-8", 1); + xfree (buffer); + return utf8_to_native (string, length, delim); + } + + /* Allocate a new buffer large enough to hold all possible + encodings. */ + n = p - buffer + 1; + inbytes = n - 1;; + inptr = buffer; + outbytes = n * MB_LEN_MAX; + if (outbytes / MB_LEN_MAX != n) + BUG (); /* Actually an overflow. */ + outbuf = outptr = xmalloc (outbytes); + if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes, + &outptr, &outbytes) == (size_t)-1) + { + static int shown; + + if (!shown) + log_info (_("conversion from '%s' to '%s' failed: %s\n"), + "utf-8", active_charset_name, strerror (errno)); + shown = 1; + /* Didn't worked out. Try again but without iconv. */ + xfree (buffer); + buffer = NULL; + xfree (outbuf); + outbuf = do_utf8_to_native (string, length, delim, 0); + } + else /* Success. */ + { + *outptr = 0; /* Make sure it is a string. */ + /* We could realloc the buffer now but I doubt that it + makes much sense given that it will get freed + anyway soon after. */ + xfree (buffer); + } + iconv_close (cd); + return outbuf; + } + else /* Not using iconv. */ + { + *p = 0; /* Make sure it is a string. */ + return buffer; + } + } +} + +/* Convert string, which is in UTF-8 to native encoding. Replace + illegal encodings by some "\xnn" and quote all control + characters. A character with value DELIM will always be quoted, it + must be a vanilla ASCII character. A DELIM value of -1 is special: + it disables all quoting of control characters. This function + terminates the process on memory shortage. */ +char * +utf8_to_native (const char *string, size_t length, int delim) +{ + return do_utf8_to_native (string, length, delim, use_iconv); +} + + + + +/* Wrapper function for iconv_open, required for W32 as we dlopen that + library on that system. */ +jnlib_iconv_t +jnlib_iconv_open (const char *tocode, const char *fromcode) +{ + return (jnlib_iconv_t)iconv_open (tocode, fromcode); +} + + +/* Wrapper function for iconv, required for W32 as we dlopen that + library on that system. */ +size_t +jnlib_iconv (jnlib_iconv_t cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + return iconv ((iconv_t)cd, (ICONV_CONST char**)inbuf, inbytesleft, + outbuf, outbytesleft); +} + +/* Wrapper function for iconv_close, required for W32 as we dlopen that + library on that system. */ +int +jnlib_iconv_close (jnlib_iconv_t cd) +{ + return iconv_close ((iconv_t)cd); +} + + +#ifdef HAVE_W32_SYSTEM +/* Return a malloced string encoded for CODEPAGE from the wide char input + string STRING. Caller must free this value. Returns NULL and sets + ERRNO on failure. Calling this function with STRING set to NULL is + not defined. */ +static char * +wchar_to_cp (const wchar_t *string, unsigned int codepage) +{ + int n; + char *result; + + n = WideCharToMultiByte (codepage, 0, string, -1, NULL, 0, NULL, NULL); + if (n < 0) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + result = xtrymalloc (n+1); + if (!result) + return NULL; + + n = WideCharToMultiByte (codepage, 0, string, -1, result, n, NULL, NULL); + if (n < 0) + { + xfree (result); + gpg_err_set_errno (EINVAL); + result = NULL; + } + return result; +} + + +/* Return a malloced wide char string from a CODEPAGE encoded input + string STRING. Caller must free this value. Returns NULL and sets + ERRNO on failure. Calling this function with STRING set to NULL is + not defined. */ +static wchar_t * +cp_to_wchar (const char *string, unsigned int codepage) +{ + int n; + size_t nbytes; + wchar_t *result; + + n = MultiByteToWideChar (codepage, 0, string, -1, NULL, 0); + if (n < 0) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + nbytes = (size_t)(n+1) * sizeof(*result); + if (nbytes / sizeof(*result) != (n+1)) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + result = xtrymalloc (nbytes); + if (!result) + return NULL; + + n = MultiByteToWideChar (codepage, 0, string, -1, result, n); + if (n < 0) + { + xfree (result); + gpg_err_set_errno (EINVAL); + result = NULL; + } + return result; +} + + +/* Return a malloced string encoded in the active code page from the + * wide char input string STRING. Caller must free this value. + * Returns NULL and sets ERRNO on failure. Calling this function with + * STRING set to NULL is not defined. */ +char * +wchar_to_native (const wchar_t *string) +{ + return wchar_to_cp (string, CP_ACP); +} + + +/* Return a malloced wide char string from an UTF-8 encoded input + * string STRING. Caller must free this value. Returns NULL and sets + * ERRNO on failure. Calling this function with STRING set to NULL is + * not defined. */ +wchar_t * +native_to_wchar (const char *string) +{ + return cp_to_wchar (string, CP_ACP); +} + + +/* Return a malloced string encoded in UTF-8 from the wide char input + * string STRING. Caller must free this value. Returns NULL and sets + * ERRNO on failure. Calling this function with STRING set to NULL is + * not defined. */ +char * +wchar_to_utf8 (const wchar_t *string) +{ + return wchar_to_cp (string, CP_UTF8); +} + + +/* Return a malloced wide char string from an UTF-8 encoded input + * string STRING. Caller must free this value. Returns NULL and sets + * ERRNO on failure. Calling this function with STRING set to NULL is + * not defined. */ +wchar_t * +utf8_to_wchar (const char *string) +{ + return cp_to_wchar (string, CP_UTF8); +} + +#endif /*HAVE_W32_SYSTEM*/ diff --git a/common/utf8conv.h b/common/utf8conv.h new file mode 100644 index 0000000..1c6c584 --- /dev/null +++ b/common/utf8conv.h @@ -0,0 +1,58 @@ +/* utf8conf.h + * Copyright (C) 2003, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_UTF8CONF_H +#define GNUPG_COMMON_UTF8CONF_H + +int set_native_charset (const char *newset); +const char *get_native_charset (void); +int is_native_utf8 (void); + +char *native_to_utf8 (const char *string); +char *utf8_to_native (const char *string, size_t length, int delim); + + +/* Silly wrappers, required for W32 portability. */ +typedef void *jnlib_iconv_t; + +jnlib_iconv_t jnlib_iconv_open (const char *tocode, const char *fromcode); +size_t jnlib_iconv (jnlib_iconv_t cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); +int jnlib_iconv_close (jnlib_iconv_t cd); + +#ifdef HAVE_W32_SYSTEM +char *wchar_to_native (const wchar_t *string); +wchar_t *native_to_wchar (const char *string); +char *wchar_to_utf8 (const wchar_t *string); +wchar_t *utf8_to_wchar (const char *string); +#endif /*HAVE_W32_SYSTEM*/ + + +#endif /*GNUPG_COMMON_UTF8CONF_H*/ diff --git a/common/util.h b/common/util.h new file mode 100644 index 0000000..f7a53e1 --- /dev/null +++ b/common/util.h @@ -0,0 +1,378 @@ +/* util.h - Utility functions for GnuPG + * Copyright (C) 2001, 2002, 2003, 2004, 2009 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_UTIL_H +#define GNUPG_COMMON_UTIL_H + +#include /* We need this for the memory function protos. */ +#include /* We need errno. */ +#include /* We need gpg_error_t and estream. */ + +/* These error codes are used but not defined in the required + * libgpg-error version. Define them here. + * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) + */ +#if GPG_ERROR_VERSION_NUMBER < 0x011a00 /* 1.26 */ +# define GPG_ERR_UNKNOWN_FLAG 309 +# define GPG_ERR_INV_ORDER 310 +# define GPG_ERR_ALREADY_FETCHED 311 +# define GPG_ERR_TRY_LATER 312 +# define GPG_ERR_SYSTEM_BUG 666 +# define GPG_ERR_DNS_UNKNOWN 711 +# define GPG_ERR_DNS_SECTION 712 +# define GPG_ERR_DNS_ADDRESS 713 +# define GPG_ERR_DNS_NO_QUERY 714 +# define GPG_ERR_DNS_NO_ANSWER 715 +# define GPG_ERR_DNS_CLOSED 716 +# define GPG_ERR_DNS_VERIFY 717 +# define GPG_ERR_DNS_TIMEOUT 718 +#endif + + +/* Hash function used with libksba. */ +#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) + +/* Get all the stuff from jnlib. */ +#include "../common/logging.h" +#include "../common/argparse.h" +#include "../common/stringhelp.h" +#include "../common/mischelp.h" +#include "../common/strlist.h" +#include "../common/dotlock.h" +#include "../common/utf8conv.h" +#include "../common/dynload.h" +#include "../common/fwddecl.h" +#include "../common/utilproto.h" + +#include "gettime.h" + +/* Redefine asprintf by our estream version which uses our own memory + allocator.. */ +#define asprintf gpgrt_asprintf +#define vasprintf gpgrt_vasprintf + +/* Due to a bug in mingw32's snprintf related to the 'l' modifier and + for increased portability we use our snprintf on all systems. */ +#undef snprintf +#define snprintf gpgrt_snprintf + + +/* Replacements for macros not available with libgpg-error < 1.20. */ + +/* We need this type even if we are not using libreadline and or we + did not include libreadline in the current file. */ +#ifndef GNUPG_LIBREADLINE_H_INCLUDED +typedef char **rl_completion_func_t (const char *, int, int); +#endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/ + + +/* Handy malloc macros - please use only them. */ +#define xtrymalloc(a) gcry_malloc ((a)) +#define xtrymalloc_secure(a) gcry_malloc_secure ((a)) +#define xtrycalloc(a,b) gcry_calloc ((a),(b)) +#define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b)) +#define xtryrealloc(a,b) gcry_realloc ((a),(b)) +#define xtrystrdup(a) gcry_strdup ((a)) +#define xfree(a) gcry_free ((a)) +#define xfree_fnc gcry_free + +#define xmalloc(a) gcry_xmalloc ((a)) +#define xmalloc_secure(a) gcry_xmalloc_secure ((a)) +#define xcalloc(a,b) gcry_xcalloc ((a),(b)) +#define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) +#define xrealloc(a,b) gcry_xrealloc ((a),(b)) +#define xstrdup(a) gcry_xstrdup ((a)) + +/* For compatibility with gpg 1.4 we also define these: */ +#define xmalloc_clear(a) gcry_xcalloc (1, (a)) +#define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) + +/* The default error source of the application. This is different + from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the + source file and thus is usable in code shared by applications. + Defined by init.c. */ +extern gpg_err_source_t default_errsource; + +/* Convenience function to return a gpg-error code for memory + allocation failures. This function makes sure that an error will + be returned even if accidentally ERRNO is not set. */ +static inline gpg_error_t +out_of_core (void) +{ + return gpg_error_from_syserror (); +} + + +/*-- yesno.c --*/ +int answer_is_yes (const char *s); +int answer_is_yes_no_default (const char *s, int def_answer); +int answer_is_yes_no_quit (const char *s); +int answer_is_okay_cancel (const char *s, int def_answer); + +/*-- xreadline.c --*/ +ssize_t read_line (FILE *fp, + char **addr_of_buffer, size_t *length_of_buffer, + size_t *max_length); + + +/*-- b64enc.c and b64dec.c --*/ +struct b64state +{ + unsigned int flags; + int idx; + int quad_count; + FILE *fp; + estream_t stream; + char *title; + unsigned char radbuf[4]; + u32 crc; + int stop_seen:1; + int invalid_encoding:1; + gpg_error_t lasterr; +}; + +gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); +gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, + const char *title); +gpg_error_t b64enc_write (struct b64state *state, + const void *buffer, size_t nbytes); +gpg_error_t b64enc_finish (struct b64state *state); + +gpg_error_t b64dec_start (struct b64state *state, const char *title); +gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, + size_t *r_nbytes); +gpg_error_t b64dec_finish (struct b64state *state); + +/*-- sexputil.c */ +char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); +void log_printcanon (const char *text, + const unsigned char *sexp, size_t sexplen); +void log_printsexp (const char *text, gcry_sexp_t sexp); + +gpg_error_t make_canon_sexp (gcry_sexp_t sexp, + unsigned char **r_buffer, size_t *r_buflen); +gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, + unsigned char **r_buffer, size_t *r_buflen); +gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, + unsigned char *grip); +int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); +unsigned char *make_simple_sexp_from_hexstr (const char *line, + size_t *nscanned); +int hash_algo_from_sigval (const unsigned char *sigval); +unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, + const void *e, size_t elen, + size_t *r_len); +gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + unsigned char const **r_n, + size_t *r_nlen, + unsigned char const **r_e, + size_t *r_elen); +gpg_error_t get_pk_algo_from_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + const char **r_algo); +int get_pk_algo_from_key (gcry_sexp_t key); + +/*-- convert.c --*/ +int hex2bin (const char *string, void *buffer, size_t length); +int hexcolon2bin (const char *string, void *buffer, size_t length); +char *bin2hex (const void *buffer, size_t length, char *stringbuf); +char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); +const char *hex2str (const char *hexstring, + char *buffer, size_t bufsize, size_t *buflen); +char *hex2str_alloc (const char *hexstring, size_t *r_count); + +/*-- percent.c --*/ +char *percent_plus_escape (const char *string); +char *percent_plus_unescape (const char *string, int nulrepl); +char *percent_unescape (const char *string, int nulrepl); + +size_t percent_plus_unescape_inplace (char *string, int nulrepl); +size_t percent_unescape_inplace (char *string, int nulrepl); + +/*-- openpgp-oid.c --*/ +gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); +char *openpgp_oid_to_str (gcry_mpi_t a); +int openpgp_oid_is_ed25519 (gcry_mpi_t a); +int openpgp_oid_is_cv25519 (gcry_mpi_t a); +const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits); +const char *openpgp_oid_to_curve (const char *oid, int canon); +const char *openpgp_enum_curves (int *idxp); +const char *openpgp_is_curve_supported (const char *name, + int *r_algo, unsigned int *r_nbits); + + +/*-- homedir.c --*/ +const char *standard_homedir (void); +const char *default_homedir (void); +void gnupg_set_homedir (const char *newdir); +const char *gnupg_homedir (void); +int gnupg_default_homedir_p (void); +const char *gnupg_socketdir (void); +const char *gnupg_sysconfdir (void); +const char *gnupg_bindir (void); +const char *gnupg_libexecdir (void); +const char *gnupg_libdir (void); +const char *gnupg_datadir (void); +const char *gnupg_localedir (void); +const char *gnupg_cachedir (void); +const char *dirmngr_socket_name (void); + +char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info); + +/* All module names. We also include gpg and gpgsm for the sake for + gpgconf. */ +#define GNUPG_MODULE_NAME_AGENT 1 +#define GNUPG_MODULE_NAME_PINENTRY 2 +#define GNUPG_MODULE_NAME_SCDAEMON 3 +#define GNUPG_MODULE_NAME_DIRMNGR 4 +#define GNUPG_MODULE_NAME_PROTECT_TOOL 5 +#define GNUPG_MODULE_NAME_CHECK_PATTERN 6 +#define GNUPG_MODULE_NAME_GPGSM 7 +#define GNUPG_MODULE_NAME_GPG 8 +#define GNUPG_MODULE_NAME_CONNECT_AGENT 9 +#define GNUPG_MODULE_NAME_GPGCONF 10 +#define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11 +#define GNUPG_MODULE_NAME_GPGV 12 +const char *gnupg_module_name (int which); +void gnupg_module_name_flush_some (void); +void gnupg_set_builddir (const char *newdir); + + + +/*-- gpgrlhelp.c --*/ +void gnupg_rl_initialize (void); + +/*-- helpfile.c --*/ +char *gnupg_get_help_string (const char *key, int only_current_locale); + +/*-- localename.c --*/ +const char *gnupg_messages_locale_name (void); + +/*-- miscellaneous.c --*/ + +/* This function is called at startup to tell libgcrypt to use our own + logging subsystem. */ +void setup_libgcrypt_logging (void); + +/* Print an out of core message and die. */ +void xoutofcore (void); + +/* Same as estream_asprintf but die on memory failure. */ +char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +/* This is now an alias to estream_asprintf. */ +char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); + +/* Replacement for gcry_cipher_algo_name. */ +const char *gnupg_cipher_algo_name (int algo); + +void obsolete_option (const char *configname, unsigned int configlineno, + const char *name); + +const char *print_fname_stdout (const char *s); +const char *print_fname_stdin (const char *s); +void print_utf8_buffer3 (estream_t fp, const void *p, size_t n, + const char *delim); +void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim); +void print_utf8_buffer (estream_t fp, const void *p, size_t n); +void print_hexstring (FILE *fp, const void *buffer, size_t length, + int reserved); +char *try_make_printable_string (const void *p, size_t n, int delim); +char *make_printable_string (const void *p, size_t n, int delim); + +int is_file_compressed (const char *s, int *ret_rc); + +int match_multistr (const char *multistr,const char *match); + +int gnupg_compare_version (const char *a, const char *b); + +struct debug_flags_s +{ + unsigned int flag; + const char *name; +}; +int parse_debug_flag (const char *string, unsigned int *debugvar, + const struct debug_flags_s *flags); + + +/*-- Simple replacement functions. */ + +/* We use the gnupg_ttyname macro to be safe not to run into conflicts + which an extisting but broken ttyname. */ +#if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME) +# define gnupg_ttyname(n) _gnupg_ttyname ((n)) +/* Systems without ttyname (W32) will merely return NULL. */ +static inline char * +_gnupg_ttyname (int fd) +{ + (void)fd; + return NULL; +} +#else /*HAVE_TTYNAME*/ +# define gnupg_ttyname(n) ttyname ((n)) +#endif /*HAVE_TTYNAME */ + +#ifdef HAVE_W32CE_SYSTEM +#define getpid() GetCurrentProcessId () +char *_gnupg_getenv (const char *name); /* See sysutils.c */ +#define getenv(a) _gnupg_getenv ((a)) +char *_gnupg_setenv (const char *name); /* See sysutils.c */ +#define setenv(a,b,c) _gnupg_setenv ((a),(b),(c)) +int _gnupg_isatty (int fd); +#define gnupg_isatty(a) _gnupg_isatty ((a)) +#else +#define gnupg_isatty(a) isatty ((a)) +#endif + + + +/*-- Macros to replace ctype ones to avoid locale problems. --*/ +#define spacep(p) (*(p) == ' ' || *(p) == '\t') +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \ + || (*(p) >= 'a' && *(p) <= 'z')) +#define alnump(p) (alphap (p) || digitp (p)) +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) + /* Note this isn't identical to a C locale isspace() without \f and + \v, but works for the purposes used here. */ +#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') + +/* The atoi macros assume that the buffer has only valid digits. */ +#define atoi_1(p) (*(p) - '0' ) +#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) +#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) + +#endif /*GNUPG_COMMON_UTIL_H*/ diff --git a/common/utilproto.h b/common/utilproto.h new file mode 100644 index 0000000..7467f6b --- /dev/null +++ b/common/utilproto.h @@ -0,0 +1,44 @@ +/* utilproto.h - Some prototypes for inclusion by util.h + * Copyright (C) 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +/* This file is in general included via util.h but sometimes we do not + * want all stuff from util.h and instead use this file with its + * simple prototypes. */ + +#ifndef GNUPG_COMMON_UTILPROTO_H +#define GNUPG_COMMON_UTILPROTO_H + +/*-- signal.c --*/ +void gnupg_init_signals (int mode, void (*fast_cleanup)(void)); +void gnupg_block_all_signals (void); +void gnupg_unblock_all_signals (void); + + + +#endif /*GNUPG_COMMON_UTILPROTO_H*/ diff --git a/common/w32-reg.c b/common/w32-reg.c new file mode 100644 index 0000000..2d64215 --- /dev/null +++ b/common/w32-reg.c @@ -0,0 +1,230 @@ +/* w32-reg.c - MS-Windows Registry access + * Copyright (C) 1999, 2002, 2007 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#ifdef HAVE_W32_SYSTEM + /* This module is only used in this environment */ + +#include +#include +#include +#include +#ifdef HAVE_WINSOCK2_H +# include +#endif +#include + +#include "util.h" +#include "common-defs.h" +#include "utf8conv.h" +#include "w32help.h" + + +static HKEY +get_root_key(const char *root) +{ + HKEY root_key; + + if (!root) + root_key = HKEY_CURRENT_USER; + else if (!strcmp( root, "HKEY_CLASSES_ROOT" ) ) + root_key = HKEY_CLASSES_ROOT; + else if (!strcmp( root, "HKEY_CURRENT_USER" ) ) + root_key = HKEY_CURRENT_USER; + else if (!strcmp( root, "HKEY_LOCAL_MACHINE" ) ) + root_key = HKEY_LOCAL_MACHINE; + else if (!strcmp( root, "HKEY_USERS" ) ) + root_key = HKEY_USERS; + else if (!strcmp( root, "HKEY_PERFORMANCE_DATA" ) ) + root_key = HKEY_PERFORMANCE_DATA; + else if (!strcmp( root, "HKEY_CURRENT_CONFIG" ) ) + root_key = HKEY_CURRENT_CONFIG; + else + return NULL; + + return root_key; +} + + +/* Return a string from the Win32 Registry or NULL in case of error. + Caller must release the return value. A NULL for root is an alias + for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ +char * +read_w32_registry_string (const char *root, const char *dir, const char *name) +{ +#ifdef HAVE_W32CE_SYSTEM + HKEY root_key, key_handle; + DWORD n1, nbytes, type; + char *result = NULL; + wchar_t *wdir, *wname; + + if ( !(root_key = get_root_key(root) ) ) + return NULL; + + wdir = utf8_to_wchar (dir); + if (!wdir) + return NULL; + + if (RegOpenKeyEx (root_key, wdir, 0, KEY_READ, &key_handle) ) + { + if (root) + { + xfree (wdir); + return NULL; /* No need for a RegClose, so return immediately. */ + } + /* It seems to be common practise to fall back to HKLM. */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, wdir, 0, KEY_READ, &key_handle) ) + { + xfree (wdir); + return NULL; /* Still no need for a RegClose. */ + } + } + xfree (wdir); + + if (name) + { + wname = utf8_to_wchar (name); + if (!wname) + goto leave; + } + else + wname = NULL; + + nbytes = 2; + if (RegQueryValueEx (key_handle, wname, 0, NULL, NULL, &nbytes)) + goto leave; + result = xtrymalloc ((n1=nbytes+2)); + if (!result) + goto leave; + if (RegQueryValueEx (key_handle, wname, 0, &type, result, &n1)) + { + xfree (result); + result = NULL; + goto leave; + } + result[nbytes] = 0; /* Make sure it is a string. */ + result[nbytes+1] = 0; + if (type == REG_SZ || type == REG_EXPAND_SZ) + { + wchar_t *tmp = (void*)result; + result = wchar_to_utf8 (tmp); + xfree (tmp); + } + + leave: + xfree (wname); + RegCloseKey (key_handle); + return result; +#else /*!HAVE_W32CE_SYSTEM*/ + HKEY root_key, key_handle; + DWORD n1, nbytes, type; + char *result = NULL; + + if ( !(root_key = get_root_key(root) ) ) + return NULL; + + if (RegOpenKeyEx (root_key, dir, 0, KEY_READ, &key_handle) ) + { + if (root) + return NULL; /* No need for a RegClose, so return immediately. */ + /* It seems to be common practise to fall back to HKLM. */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) ) + return NULL; /* Still no need for a RegClose. */ + } + + nbytes = 1; + if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) + goto leave; + result = xtrymalloc ((n1=nbytes+1)); + if (!result) + goto leave; + if (RegQueryValueEx( key_handle, name, 0, &type, result, &n1 )) + { + xfree (result); + result = NULL; + goto leave; + } + result[nbytes] = 0; /* Make sure it is a string. */ + if (type == REG_EXPAND_SZ && strchr (result, '%')) + { + char *tmp; + + n1 += 1000; + tmp = xtrymalloc (n1+1); + if (!tmp) + goto leave; + nbytes = ExpandEnvironmentStrings (result, tmp, n1); + if (nbytes && nbytes > n1) + { + xfree (tmp); + n1 = nbytes; + tmp = xtrymalloc (n1 + 1); + if (!tmp) + goto leave; + nbytes = ExpandEnvironmentStrings (result, tmp, n1); + if (nbytes && nbytes > n1) + { + /* Oops - truncated, better don't expand at all. */ + xfree (tmp); + goto leave; + } + tmp[nbytes] = 0; + xfree (result); + result = tmp; + } + else if (nbytes) + { + /* Okay, reduce the length. */ + tmp[nbytes] = 0; + xfree (result); + result = xtrymalloc (strlen (tmp)+1); + if (!result) + result = tmp; + else + { + strcpy (result, tmp); + xfree (tmp); + } + } + else + { + /* Error - don't expand. */ + xfree (tmp); + } + } + + leave: + RegCloseKey (key_handle); + return result; +#endif /*!HAVE_W32CE_SYSTEM*/ +} + + +#endif /*HAVE_W32_SYSTEM*/ diff --git a/common/w32help.h b/common/w32help.h new file mode 100644 index 0000000..e495e34 --- /dev/null +++ b/common/w32help.h @@ -0,0 +1,56 @@ +/* w32help.h - W32 speicif functions + * Copyright (C) 2007 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 either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef GNUPG_COMMON_W32HELP_H +#define GNUPG_COMMON_W32HELP_H +#ifdef HAVE_W32_SYSTEM + +/*-- w32-reg.c --*/ +char *read_w32_registry_string (const char *root, + const char *dir, const char *name ); + +/* Other stuff. */ +#ifdef HAVE_W32CE_SYSTEM +/* Setmode is missing in cegcc but available since CE 5.0. */ +int _setmode (int handle, int mode); +# define setmode(a,b) _setmode ((a),(b)) + +static inline int +umask (int a) +{ + (void)a; + return 0; +} + + +#endif /*HAVE_W32CE_SYSTEM*/ + +#endif /*HAVE_W32_SYSTEM*/ +#endif /*GNUPG_COMMON_MISCHELP_H*/ diff --git a/common/w32info-rc.h.in b/common/w32info-rc.h.in new file mode 100644 index 0000000..d7909dd --- /dev/null +++ b/common/w32info-rc.h.in @@ -0,0 +1,32 @@ +/* w32info-rc.h.in - Common defs for VERSIONINFO resources. + * Copyright (C) 2013 g10 Code GmbH + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This 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. + */ + +/* This file is processed by configure to create w32info-rc.h . */ + +#define W32INFO_COMMENTS "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 3 of the License, or (at your option) any later version.\0" + +#define W32INFO_COMPANYNAME "The GnuPG Project\0" + +#define W32INFO_VI_FILEVERSION @BUILD_FILEVERSION@ +#define W32INFO_VI_PRODUCTVERSION @BUILD_FILEVERSION@ + +#define W32INFO_FILEVERSION "@VERSION@ (@BUILD_REVISION@) \ +built on @BUILD_HOSTNAME@ at @BUILD_TIMESTAMP@\0" + +#define W32INFO_PRODUCTNAME "GNU Privacy Guard (GnuPG)\0" +#define W32INFO_PRODUCTVERSION "@VERSION@\0" + +#define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \ +2015 Free Software Foundation, Inc.\0" diff --git a/common/xasprintf.c b/common/xasprintf.c new file mode 100644 index 0000000..00ff66a --- /dev/null +++ b/common/xasprintf.c @@ -0,0 +1,70 @@ +/* xasprintf.c + * Copyright (C) 2003, 2005 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include + +#include "util.h" + +/* Same as asprintf but return an allocated buffer suitable to be + freed using xfree. This function simply dies on memory failure, + thus no extra check is required. + + FIXME: We should remove these functions in favor of gpgrt_bsprintf + and a xgpgrt_bsprintf or rename them to xbsprintf and + xtrybsprintf. */ +char * +xasprintf (const char *fmt, ...) +{ + va_list ap; + char *buf; + + va_start (ap, fmt); + if (gpgrt_vasprintf (&buf, fmt, ap) < 0) + log_fatal ("estream_asprintf failed: %s\n", strerror (errno)); + va_end (ap); + return buf; +} + +/* Same as above but return NULL on memory failure. */ +char * +xtryasprintf (const char *fmt, ...) +{ + int rc; + va_list ap; + char *buf; + + va_start (ap, fmt); + rc = gpgrt_vasprintf (&buf, fmt, ap); + va_end (ap); + if (rc < 0) + return NULL; + return buf; +} diff --git a/common/xreadline.c b/common/xreadline.c new file mode 100644 index 0000000..b17579f --- /dev/null +++ b/common/xreadline.c @@ -0,0 +1,127 @@ +/* xreadline.c - fgets replacement function + * Copyright (C) 1999, 2004 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include + +#include "util.h" + + +/* Same as fgets() but if the provided buffer is too short a larger + one will be allocated. This is similar to getline. A line is + considered a byte stream ending in a LF. + + If MAX_LENGTH is not NULL, it shall point to a value with the + maximum allowed allocation. + + Returns the length of the line. EOF is indicated by a line of + length zero. A truncated line is indicated by setting the value at + MAX_LENGTH to 0. If the returned value is less then 0 not enough + memory was enable and ERRNO is set accordingly. + + If a line has been truncated, the file pointer is moved forward to + the end of the line so that the next read starts with the next + line. Note that MAX_LENGTH must be re-initialzied in this case. + + Note: The returned buffer is allocated with enough extra space to + append a CR,LF,Nul + */ +ssize_t +read_line (FILE *fp, + char **addr_of_buffer, size_t *length_of_buffer, + size_t *max_length) +{ + int c; + char *buffer = *addr_of_buffer; + size_t length = *length_of_buffer; + size_t nbytes = 0; + size_t maxlen = max_length? *max_length : 0; + char *p; + + if (!buffer) + { /* No buffer given - allocate a new one. */ + length = 256; + buffer = xtrymalloc (length); + *addr_of_buffer = buffer; + if (!buffer) + { + *length_of_buffer = 0; + if (max_length) + *max_length = 0; + return -1; + } + *length_of_buffer = length; + } + + length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */ + p = buffer; + while ((c = getc (fp)) != EOF) + { + if (nbytes == length) + { /* Enlarge the buffer. */ + if (maxlen && length > maxlen) /* But not beyond our limit. */ + { + /* Skip the rest of the line. */ + while (c != '\n' && (c=getc (fp)) != EOF) + ; + *p++ = '\n'; /* Always append a LF (we reserved some space). */ + nbytes++; + if (max_length) + *max_length = 0; /* Indicate truncation. */ + break; /* the while loop. */ + } + length += 3; /* Adjust for the reserved bytes. */ + length += length < 1024? 256 : 1024; + *addr_of_buffer = xtryrealloc (buffer, length); + if (!*addr_of_buffer) + { + int save_errno = errno; + xfree (buffer); + *length_of_buffer = 0; + if (max_length) + *max_length = 0; + gpg_err_set_errno (save_errno); + return -1; + } + buffer = *addr_of_buffer; + *length_of_buffer = length; + length -= 3; + p = buffer + nbytes; + } + *p++ = c; + nbytes++; + if (c == '\n') + break; + } + *p = 0; /* Make sure the line is a string. */ + + return nbytes; +} diff --git a/common/yesno.c b/common/yesno.c new file mode 100644 index 0000000..58de63d --- /dev/null +++ b/common/yesno.c @@ -0,0 +1,150 @@ +/* yesno.c - Yes/No questions + * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include + +#include "i18n.h" +#include "util.h" + + +/* Check the string S for a YES or NO answer and take care of + localization. If no valid string is given the value of DEF_ANSWER + is returned. Returns 1 for yes and 0 for no. */ +int +answer_is_yes_no_default (const char *s, int def_answer) +{ + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_yes = _("yes"); + const char *short_yes = _("yY"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_no = _("no"); + const char *short_no = _("nN"); + + /* Note: we have to use the local dependent compare here. */ + if ( match_multistr(long_yes,s) ) + return 1; + if ( *s && strchr( short_yes, *s ) && !s[1] ) + return 1; + /* Test for "no" strings to catch ambiguities for the next test. */ + if ( match_multistr(long_no,s) ) + return 0; + if ( *s && strchr( short_no, *s ) && !s[1] ) + return 0; + /* Test for the english version (for those who are used to type yes). */ + if ( !ascii_strcasecmp(s, "yes" ) ) + return 1; + if ( *s && strchr( "yY", *s ) && !s[1] ) + return 1; + return def_answer; +} + +int +answer_is_yes ( const char *s ) +{ + return answer_is_yes_no_default(s,0); +} + +/**************** + * Return 1 for yes, -1 for quit, or 0 for no + */ +int +answer_is_yes_no_quit ( const char *s ) +{ + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_yes = _("yes"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_no = _("no"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_quit = _("quit"); + const char *short_yes = _("yY"); + const char *short_no = _("nN"); + const char *short_quit = _("qQ"); + + /* Note: we have to use a local dependent compare here. */ + if ( match_multistr(long_no,s) ) + return 0; + if ( match_multistr(long_yes,s) ) + return 1; + if ( match_multistr(long_quit,s) ) + return -1; + if ( *s && strchr( short_no, *s ) && !s[1] ) + return 0; + if ( *s && strchr( short_yes, *s ) && !s[1] ) + return 1; + if ( *s && strchr( short_quit, *s ) && !s[1] ) + return -1; + /* but not here. */ + if ( !ascii_strcasecmp(s, "yes" ) ) + return 1; + if ( !ascii_strcasecmp(s, "quit" ) ) + return -1; + if ( *s && strchr( "yY", *s ) && !s[1] ) + return 1; + if ( *s && strchr( "qQ", *s ) && !s[1] ) + return -1; + return 0; +} + +/* + Return 1 for okay, 0 for for cancel or DEF_ANSWER for default. + */ +int +answer_is_okay_cancel (const char *s, int def_answer) +{ + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_okay = _("okay|okay"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_cancel = _("cancel|cancel"); + const char *short_okay = _("oO"); + const char *short_cancel = _("cC"); + + /* Note: We have to use the locale dependent compare. */ + if ( match_multistr(long_okay,s) ) + return 1; + if ( match_multistr(long_cancel,s) ) + return 0; + if ( *s && strchr( short_okay, *s ) && !s[1] ) + return 1; + if ( *s && strchr( short_cancel, *s ) && !s[1] ) + return 0; + /* Always test for the English values (not locale here). */ + if ( !ascii_strcasecmp(s, "okay" ) ) + return 1; + if ( !ascii_strcasecmp(s, "ok" ) ) + return 1; + if ( !ascii_strcasecmp(s, "cancel" ) ) + return 0; + if ( *s && strchr( "oO", *s ) && !s[1] ) + return 1; + if ( *s && strchr( "cC", *s ) && !s[1] ) + return 0; + return def_answer; +} diff --git a/common/zb32.c b/common/zb32.c new file mode 100644 index 0000000..517321e --- /dev/null +++ b/common/zb32.c @@ -0,0 +1,120 @@ +/* zb32.c - z-base-32 functions + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "zb32.h" + +/* Zooko's base32 variant. See RFC-6189 and + http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt + Caller must xfree the returned string. Returns NULL and sets ERRNO + on error. To avoid integer overflow DATALEN is limited to 2^16 + bytes. Note, that DATABITS is measured in bits!. */ +char * +zb32_encode (const void *data, unsigned int databits) +{ + static char const zb32asc[32] = {'y','b','n','d','r','f','g','8', + 'e','j','k','m','c','p','q','x', + 'o','t','1','u','w','i','s','z', + 'a','3','4','5','h','7','6','9' }; + const unsigned char *s; + char *output, *d; + size_t datalen; + + datalen = (databits + 7) / 8; + if (datalen > (1 << 16)) + { + errno = EINVAL; + return NULL; + } + + d = output = xtrymalloc (8 * (datalen / 5) + + 2 * (datalen % 5) + - ((datalen%5)>2) + + 1); + if (!output) + return NULL; + + /* I use straightforward code. The compiler should be able to do a + better job on optimization than me and it is easier to read. */ + for (s = data; datalen >= 5; s += 5, datalen -= 5) + { + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) | (s[2] >> 4) ]; + *d++ = zb32asc[((s[2] & 15) << 1) | (s[3] >> 7) ]; + *d++ = zb32asc[((s[3] & 127) >> 2) ]; + *d++ = zb32asc[((s[3] & 3) << 3) | (s[4] >> 5) ]; + *d++ = zb32asc[((s[4] & 31) ) ]; + } + + switch (datalen) + { + case 4: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) | (s[2] >> 4) ]; + *d++ = zb32asc[((s[2] & 15) << 1) | (s[3] >> 7) ]; + *d++ = zb32asc[((s[3] & 127) >> 2) ]; + *d++ = zb32asc[((s[3] & 3) << 3) ]; + break; + case 3: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) | (s[2] >> 4) ]; + *d++ = zb32asc[((s[2] & 15) << 1) ]; + break; + case 2: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) ]; + break; + case 1: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) ]; + break; + default: + break; + } + *d = 0; + + /* Need to strip some bytes if not a multiple of 40. */ + output[(databits + 5 - 1) / 5] = 0; + return output; +} diff --git a/common/zb32.h b/common/zb32.h new file mode 100644 index 0000000..47bb1f8 --- /dev/null +++ b/common/zb32.h @@ -0,0 +1,38 @@ +/* zb32.h - z-base-32 functions + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#ifndef GNUPG_COMMON_ZB32_H +#define GNUPG_COMMON_ZB32_H + +/* Encode DATA which has a length of DATABITS (bits!) using the + zbase32 encoder and return a malloced string. Returns NULL on + error and sets ERRNO. */ +char *zb32_encode (const void *data, unsigned int databits); + +#endif /*GNUPG_COMMON_ZB32_H*/ diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..0da0ffb --- /dev/null +++ b/config.h.in @@ -0,0 +1,911 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + + +#ifndef GNUPG_CONFIG_H_INCLUDED +#define GNUPG_CONFIG_H_INCLUDED + + +/* Defined if the host has big endian byte ordering */ +#undef BIG_ENDIAN_HOST + +/* an Apple OSXism */ +#undef BIND_8_COMPAT + +/* GIT commit id revision used to build this package */ +#undef BUILD_REVISION + +/* The time this package was configured for a build */ +#undef BUILD_TIMESTAMP + +/* Defined if GPG-AGENT is to be build */ +#undef BUILD_WITH_AGENT + +/* Defined if SCDAEMON is to be build */ +#undef BUILD_WITH_DIRMNGR + +/* Defined if G13 is to be build */ +#undef BUILD_WITH_G13 + +/* Defined if GPG is to be build */ +#undef BUILD_WITH_GPG + +/* Defined if GPGSM is to be build */ +#undef BUILD_WITH_GPGSM + +/* Defined if SCDAEMON is to be build */ +#undef BUILD_WITH_SCDAEMON + +/* The default keyserver for dirmngr to use, if none is explicitly given */ +#undef DIRMNGR_DEFAULT_KEYSERVER + +/* The displayed name of dirmngr */ +#undef DIRMNGR_DISP_NAME + +/* The name of the dirmngr info envvar */ +#undef DIRMNGR_INFO_NAME + +/* The name of the dirmngr */ +#undef DIRMNGR_NAME + +/* The name of the dirmngr socket */ +#undef DIRMNGR_SOCK_NAME + +/* define to disable photo viewing */ +#undef DISABLE_PHOTO_VIEWER + +/* Define to disable regular expression support */ +#undef DISABLE_REGEX + +/* Define to include smartcard support */ +#undef ENABLE_CARD_SUPPORT + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#undef ENABLE_NLS + +/* Define to enable SELinux support */ +#undef ENABLE_SELINUX_HACKS + +/* defines the filename of the encfs program */ +#undef ENCFS + +/* The executable file extension, if any */ +#undef EXEEXT + +/* if set, restrict photo-viewer to this */ +#undef FIXED_PHOTO_VIEWER + +/* defines the filename of the fusermount program */ +#undef FUSERMOUNT + +/* The displayed name of g13 */ +#undef G13_DISP_NAME + +/* The name of the g13 tool */ +#undef G13_NAME + +/* version of the libassuan library */ +#undef GNUPG_LIBASSUAN_VERSION + +/* The name of the project */ +#undef GNUPG_NAME + +/* The directory part of the W32 registry keys */ +#undef GNUPG_REGISTRY_DIR + +/* The displayed name of gpgconf */ +#undef GPGCONF_DISP_NAME + +/* The name of the gpgconf tool */ +#undef GPGCONF_NAME + +/* The standard binary file suffix */ +#undef GPGEXT_GPG + +/* The displayed name of gpgsm */ +#undef GPGSM_DISP_NAME + +/* The name of the S/MIME tool */ +#undef GPGSM_NAME + +/* The name of the gpgtar tool */ +#undef GPGTAR_NAME + +/* The name of the agent socket for browsers */ +#undef GPG_AGENT_BROWSER_SOCK_NAME + +/* The displayed name of gpg-agent */ +#undef GPG_AGENT_DISP_NAME + +/* The name of the agent socket for remote access */ +#undef GPG_AGENT_EXTRA_SOCK_NAME + +/* The name of the agent */ +#undef GPG_AGENT_NAME + +/* The name of the agent socket */ +#undef GPG_AGENT_SOCK_NAME + +/* The name of the agent socket for ssh */ +#undef GPG_AGENT_SSH_SOCK_NAME + +/* The displayed name of gpg */ +#undef GPG_DISP_NAME + +/* The name of the OpenPGP tool */ +#undef GPG_NAME + +/* Define to support the AES128 cipher */ +#undef GPG_USE_AES128 + +/* Define to support the AES192 cipher */ +#undef GPG_USE_AES192 + +/* Define to support the AES256 cipher */ +#undef GPG_USE_AES256 + +/* Define to support the BLOWFISH cipher */ +#undef GPG_USE_BLOWFISH + +/* Define to support the CAMELLIA128 cipher */ +#undef GPG_USE_CAMELLIA128 + +/* Define to support the CAMELLIA192 cipher */ +#undef GPG_USE_CAMELLIA192 + +/* Define to support the CAMELLIA256 cipher */ +#undef GPG_USE_CAMELLIA256 + +/* Define to support the CAST5 cipher */ +#undef GPG_USE_CAST5 + +/* Define to support the ECDH public key */ +#undef GPG_USE_ECDH + +/* Define to support the ECDSA public key */ +#undef GPG_USE_ECDSA + +/* Define to support the EdDSA public key */ +#undef GPG_USE_EDDSA + +/* Define to support the IDEA cipher */ +#undef GPG_USE_IDEA + +/* Define to support the MD5 hash */ +#undef GPG_USE_MD5 + +/* Define to support the RIPE-MD160 hash */ +#undef GPG_USE_RMD160 + +/* Define to support the RSA public key */ +#undef GPG_USE_RSA + +/* Define to support the SHA-224 hash */ +#undef GPG_USE_SHA224 + +/* Define to support the SHA-384 hash */ +#undef GPG_USE_SHA384 + +/* Define to support the SHA-512 hash */ +#undef GPG_USE_SHA512 + +/* Define to support the TWOFISH cipher */ +#undef GPG_USE_TWOFISH + +/* Defined if we build for an Android system */ +#undef HAVE_ANDROID_SYSTEM + +/* Define to 1 if you have the `atexit' function. */ +#undef HAVE_ATEXIT + +/* Defined if ttyname does not work properly */ +#undef HAVE_BROKEN_TTYNAME + +/* Defined if a `byte' is typedef'd */ +#undef HAVE_BYTE_TYPEDEF + +/* Defined if the bz2 compression library is available */ +#undef HAVE_BZIP2 + +/* Define to 1 if you have the `canonicalize_file_name' function. */ +#undef HAVE_CANONICALIZE_FILE_NAME + +/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +#undef HAVE_CFLOCALECOPYCURRENT + +/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +#undef HAVE_CFPREFERENCESCOPYAPPVALUE + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the `ctermid' function. */ +#undef HAVE_CTERMID + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#undef HAVE_DCGETTEXT + +/* Define to 1 if you have the declaration of `getpagesize', and to 0 if you + don't. */ +#undef HAVE_DECL_GETPAGESIZE + +/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you + don't. */ +#undef HAVE_DECL_SYS_SIGLIST + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRECT_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) + with special properties like no file modes, case insensitive file names and + preferred use of backslashes as directory name separators. */ +#undef HAVE_DOSISH_SYSTEM + +/* Defined if the OS supports drive letters. */ +#undef HAVE_DRIVE_LETTERS + +/* Define to 1 if you have the `fcntl' function. */ +#undef HAVE_FCNTL + +/* Define to 1 if you have the `flockfile' function. */ +#undef HAVE_FLOCKFILE + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#undef HAVE_FSEEKO + +/* Define to 1 if you have the `fsync' function. */ +#undef HAVE_FSYNC + +/* Define to 1 if you have the `ftello' function. */ +#undef HAVE_FTELLO + +/* Define to 1 if you have the `ftruncate' function. */ +#undef HAVE_FTRUNCATE + +/* Define to 1 if you have the `funlockfile' function. */ +#undef HAVE_FUNLOCKFILE + +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `getenv' function. */ +#undef HAVE_GETENV + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the `getpwnam' function. */ +#undef HAVE_GETPWNAM + +/* Define to 1 if you have the `getpwuid' function. */ +#undef HAVE_GETPWUID + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#undef HAVE_GETTEXT + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `gmtime_r' function. */ +#undef HAVE_GMTIME_R + +/* Define if you have the iconv() function and it works. */ +#undef HAVE_ICONV + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the `inotify_init' function. */ +#undef HAVE_INOTIFY_INIT + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `isascii' function. */ +#undef HAVE_ISASCII + +/* Define if you have and nl_langinfo(CODESET). */ +#undef HAVE_LANGINFO_CODESET + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* defined if liblber is available */ +#undef HAVE_LBER + +/* Define if your file defines LC_MESSAGES. */ +#undef HAVE_LC_MESSAGES + +/* Define to 1 if you have the `ldap_get_option' function. */ +#undef HAVE_LDAP_GET_OPTION + +/* Define if the LDAP library supports ld_errno */ +#undef HAVE_LDAP_LD_ERRNO + +/* Define to 1 if you have the `ldap_set_option' function. */ +#undef HAVE_LDAP_SET_OPTION + +/* Define to 1 if you have the `ldap_start_tls_s' function. */ +#undef HAVE_LDAP_START_TLS_S + +/* Define to 1 if you have the `ldap_start_tls_sA' function. */ +#undef HAVE_LDAP_START_TLS_SA + +/* Define to 1 if you have a fully functional readline library. */ +#undef HAVE_LIBREADLINE + +/* defined if libusb is available */ +#undef HAVE_LIBUSB + +/* defined if libutil is available */ +#undef HAVE_LIBUTIL + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if you have the `memicmp' function. */ +#undef HAVE_MEMICMP + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memrchr' function. */ +#undef HAVE_MEMRCHR + +/* Define to 1 if you have the `mmap' function. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the `nanosleep' function in libc. */ +#undef HAVE_NANOSLEEP + +/* Define to 1 if you have the `nl_langinfo' function. */ +#undef HAVE_NL_LANGINFO + +/* Defined if the New Portable Thread Library is available */ +#undef HAVE_NPTH + +/* Define to 1 if you have the `pipe' function. */ +#undef HAVE_PIPE + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `raise' function. */ +#undef HAVE_RAISE + +/* Define to 1 if you have the `rand' function. */ +#undef HAVE_RAND + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `setrlimit' function. */ +#undef HAVE_SETRLIMIT + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the header file. */ +#undef HAVE_SIGNAL_H + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + +/* Define to 1 if the system has the type `sigset_t'. */ +#undef HAVE_SIGSET_T + +/* Define to 1 if you have the `stat' function. */ +#undef HAVE_STAT + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the `stricmp' function. */ +#undef HAVE_STRICMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlwr' function. */ +#undef HAVE_STRLWR + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `strpbrk' function. */ +#undef HAVE_STRPBRK + +/* Define to 1 if you have the `strsep' function. */ +#undef HAVE_STRSEP + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define to 1 if you have the `strtoull' function. */ +#undef HAVE_STRTOULL + +/* Define to 1 if the system has the type `struct sigaction'. */ +#undef HAVE_STRUCT_SIGACTION + +/* The system's resolver is usable. */ +#undef HAVE_SYSTEM_RESOLVER + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the `tcgetattr' function. */ +#undef HAVE_TCGETATTR + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIO_H + +/* Define to 1 if you have the `timegm' function. */ +#undef HAVE_TIMEGM + +/* Define to 1 if you have the `times' function. */ +#undef HAVE_TIMES + +/* Define to 1 if you have the `ttyname' function. */ +#undef HAVE_TTYNAME + +/* Defined if a `u16' is typedef'd */ +#undef HAVE_U16_TYPEDEF + +/* Defined if a `u32' is typedef'd */ +#undef HAVE_U32_TYPEDEF + +/* Defined if a `ulong' is typedef'd */ +#undef HAVE_ULONG_TYPEDEF + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Defined if time_t is an unsigned type */ +#undef HAVE_UNSIGNED_TIME_T + +/* Defined if a `ushort' is typedef'd */ +#undef HAVE_USHORT_TYPEDEF + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTMP_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Defined if we run on WindowsCE */ +#undef HAVE_W32CE_SYSTEM + +/* Defined if we run on a W32 API based system */ +#undef HAVE_W32_SYSTEM + +/* Define to 1 if you have the `wait4' function. */ +#undef HAVE_WAIT4 + +/* Define to 1 if you have the `waitpid' function. */ +#undef HAVE_WAITPID + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINSOCK2_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_WS2TCPIP_H + +/* Defined if ZIP and ZLIB are supported */ +#undef HAVE_ZIP + +/* Enable GNUTLS support in http.c */ +#undef HTTP_USE_GNUTLS + +/* Enable NTBTLS support in http.c */ +#undef HTTP_USE_NTBTLS + +/* Define as const if the declaration of iconv() needs const. */ +#undef ICONV_CONST + +/* Defined if this is not a regular release */ +#undef IS_DEVELOPMENT_VERSION + +/* Defined if the host has little endian byte ordering */ +#undef LITTLE_ENDIAN_HOST + +/* Defined if mkdir() does not take permission flags */ +#undef MKDIR_TAKES_ONE_ARG + +/* Required version of Libksba */ +#undef NEED_KSBA_VERSION + +/* Define if the LDAP library requires including lber.h before ldap.h */ +#undef NEED_LBER_H + +/* Required version of Libgcrypt */ +#undef NEED_LIBGCRYPT_VERSION + +/* Required version of NTBTLS */ +#undef NEED_NTBTLS_VERSION + +/* Define to disable all external program execution */ +#undef NO_EXEC + +/* Define to include only trust-model always */ +#undef NO_TRUST_MODELS + +/* Name of this package */ +#undef PACKAGE + +/* Bug report address */ +#undef PACKAGE_BUGREPORT + +/* Name of this package for gettext */ +#undef PACKAGE_GT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Size of the key and UID caches */ +#undef PK_UID_CACHE_SIZE + +/* A human readable text with the name of the OS */ +#undef PRINTABLE_OS_NAME + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* The displayed name of scdaemon */ +#undef SCDAEMON_DISP_NAME + +/* The name of the scdaemon */ +#undef SCDAEMON_NAME + +/* The name of the SCdaemon socket */ +#undef SCDAEMON_SOCK_NAME + +/* Size of secure memory buffer */ +#undef SECMEM_BUFFER_SIZE + +/* defines the filename of the shred program */ +#undef SHRED + +/* The size of `time_t', as computed by sizeof. */ +#undef SIZEOF_TIME_T + +/* The size of `unsigned int', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_INT + +/* The size of `unsigned long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG + +/* The size of `unsigned long long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG_LONG + +/* The size of `unsigned short', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_SHORT + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to enable auto starting of the dirmngr */ +#undef USE_DIRMNGR_AUTO_START + +/* Define to install gpg as gpg2 */ +#undef USE_GPG2_HACK + +/* Defined if LDAP is support */ +#undef USE_LDAP + +/* Build dirmngr with LDAP wrapper process */ +#undef USE_LDAPWRAPPER + +/* Build with integrated libdns support */ +#undef USE_LIBDNS + +/* Defined if support for nPth is requested and nPth is available */ +#undef USE_NPTH + +/* Set this to limit filenames to the 8.3 format */ +#undef USE_ONLY_8DOT3 + +/* Because the Unix gettext has too much overhead on MingW32 systems and these + systems lack Posix functions, we use a simplified version of gettext */ +#undef USE_SIMPLE_GETTEXT + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Enable to build the TOFU code */ +#undef USE_TOFU + +/* Version of this package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#undef _LARGEFILE_SOURCE + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `int' if does not define. */ +#undef mode_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* type to use in place of socklen_t if not defined */ +#undef socklen_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +#undef volatile + + +/* This is the major version number of GnuPG so that + source included files can test for this. Note, that + we use 2 here even for GnuPG 1.9.x. */ +#define GNUPG_MAJOR_VERSION 2 + +/* Now to separate file name parts. + Please note that the string version must not contain more + than one character because the code assumes strlen()==1 */ +#ifdef HAVE_DOSISH_SYSTEM +#define DIRSEP_C '\\' +#define DIRSEP_S "\\" +#define EXTSEP_C '.' +#define EXTSEP_S "." +#define PATHSEP_C ';' +#define PATHSEP_S ";" +#define EXEEXT_S ".exe" +#else +#define DIRSEP_C '/' +#define DIRSEP_S "/" +#define EXTSEP_C '.' +#define EXTSEP_S "." +#define PATHSEP_C ':' +#define PATHSEP_S ":" +#define EXEEXT_S "" +#endif + +/* This is the same as VERSION, but should be overridden if the + platform cannot handle things like dots '.' in filenames. Set + SAFE_VERSION_DOT and SAFE_VERSION_DASH to whatever SAFE_VERSION + uses for dots and dashes. */ +#define SAFE_VERSION VERSION +#define SAFE_VERSION_DOT '.' +#define SAFE_VERSION_DASH '-' + +/* Some global constants. */ +#ifdef HAVE_DOSISH_SYSTEM +# ifdef HAVE_DRIVE_LETTERS +# define GNUPG_DEFAULT_HOMEDIR "c:/gnupg" +# else +# define GNUPG_DEFAULT_HOMEDIR "/gnupg" +# endif +#elif defined(__VMS) +#define GNUPG_DEFAULT_HOMEDIR "/SYS$LOGIN/gnupg" +#else +#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg" +#endif +#define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d" +#define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d" + +/* For some systems (DOS currently), we hardcode the path here. For + POSIX systems the values are constructed by the Makefiles, so that + the values may be overridden by the make invocations; this is to + comply with the GNU coding standards. Note that these values are + only defaults. */ +#ifdef HAVE_DOSISH_SYSTEM +# ifdef HAVE_DRIVE_LETTERS +# define GNUPG_BINDIR "c:\\gnupg" +# define GNUPG_LIBEXECDIR "c:\\gnupg" +# define GNUPG_LIBDIR "c:\\gnupg" +# define GNUPG_DATADIR "c:\\gnupg" +# define GNUPG_SYSCONFDIR "c:\\gnupg" +# else +# define GNUPG_BINDIR "\\gnupg" +# define GNUPG_LIBEXECDIR "\\gnupg" +# define GNUPG_LIBDIR "\\gnupg" +# define GNUPG_DATADIR "\\gnupg" +# define GNUPG_SYSCONFDIR "\\gnupg" +# endif +#endif + +/* Derive some other constants. */ +#if !(defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID)) +#define EXEC_TEMPFILE_ONLY +#endif + + +/* We didn't define endianness above, so get it from OS macros. This + is intended for making fat binary builds on OS X. */ +#if !defined(BIG_ENDIAN_HOST) && !defined(LITTLE_ENDIAN_HOST) +#if defined(__BIG_ENDIAN__) +#define BIG_ENDIAN_HOST 1 +#elif defined(__LITTLE_ENDIAN__) +#define LITTLE_ENDIAN_HOST 1 +#else +#error "No endianness found" +#endif +#endif + + +/* Hack used for W32: ldap.m4 also tests for the ASCII version of + ldap_start_tls_s because that is the actual symbol used in the + library. winldap.h redefines it to our commonly used value, + thus we define our usual macro here. */ +#ifdef HAVE_LDAP_START_TLS_SA +# ifndef HAVE_LDAP_START_TLS_S +# define HAVE_LDAP_START_TLS_S 1 +# endif +#endif + +/* Provide the es_ macro for estream. */ +#define GPGRT_ENABLE_ES_MACROS 1 + +/* Tell libgcrypt not to use its own libgpg-error implementation. */ +#define USE_LIBGPG_ERROR 1 + +/* Tell Libgcrypt not to include deprecated definitions. */ +#define GCRYPT_NO_DEPRECATED 1 + +/* Our HTTP code is used in estream mode. */ +#define HTTP_USE_ESTREAM 1 + +/* Under W32 we do an explicit socket initialization, thus we need to + avoid the on-demand initialization which would also install an atexit + handler. */ +#define HTTP_NO_WSASTARTUP + +/* Under Windows we use the gettext code from libgpg-error. */ +#define GPG_ERR_ENABLE_GETTEXT_MACROS + +/* Under WindowsCE we use the strerror replacement from libgpg-error. */ +#define GPG_ERR_ENABLE_ERRNO_MACROS + +#endif /*GNUPG_CONFIG_H_INCLUDED*/ + diff --git a/configure b/configure new file mode 100755 index 0000000..096ffc6 --- /dev/null +++ b/configure @@ -0,0 +1,17777 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for gnupg 2.1.17. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: https://bugs.gnupg.org about your system, including any +$0: error possibly output before this message. Then install +$0: a modern shell, or manually run the script under such a +$0: shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='gnupg' +PACKAGE_TARNAME='gnupg' +PACKAGE_VERSION='2.1.17' +PACKAGE_STRING='gnupg 2.1.17' +PACKAGE_BUGREPORT='https://bugs.gnupg.org' +PACKAGE_URL='' + +ac_unique_file="sm/gpgsm.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +gt_needs= +ac_header_list= +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +BUILD_HOSTNAME +BUILD_TIMESTAMP +BUILD_FILEVERSION +BUILD_VERSION +BUILD_REVISION +USE_TOFU_FALSE +USE_TOFU_TRUE +NO_TRUST_MODELS_FALSE +NO_TRUST_MODELS_TRUE +ENABLE_CARD_SUPPORT_FALSE +ENABLE_CARD_SUPPORT_TRUE +BUILD_WKS_TOOLS_FALSE +BUILD_WKS_TOOLS_TRUE +BUILD_GPGTAR_FALSE +BUILD_GPGTAR_TRUE +BUILD_SYMCRYPTRUN_FALSE +BUILD_SYMCRYPTRUN_TRUE +BUILD_DOC_FALSE +BUILD_DOC_TRUE +BUILD_TOOLS_FALSE +BUILD_TOOLS_TRUE +BUILD_DIRMNGR_FALSE +BUILD_DIRMNGR_TRUE +BUILD_G13_FALSE +BUILD_G13_TRUE +BUILD_SCDAEMON_FALSE +BUILD_SCDAEMON_TRUE +BUILD_AGENT_FALSE +BUILD_AGENT_TRUE +BUILD_GPGSM_FALSE +BUILD_GPGSM_TRUE +BUILD_GPG_FALSE +BUILD_GPG_TRUE +USE_C99_CFLAGS +W32SOCKLIBS +NETLIBS +CROSS_COMPILING_FALSE +CROSS_COMPILING_TRUE +LIBREADLINE +ZLIBS +ENABLE_BZIP2_SUPPORT_FALSE +ENABLE_BZIP2_SUPPORT_TRUE +DISABLE_REGEX_FALSE +DISABLE_REGEX_TRUE +SYS_SOCKET_H +BUILD_INCLUDED_LIBINTL +USE_INCLUDED_LIBINTL +POSUB +LTLIBINTL +LIBINTL +INTLLIBS +INTL_MACOSX_LIBS +XGETTEXT_EXTRA_OPTIONS +MSGMERGE +XGETTEXT_015 +XGETTEXT +GMSGFMT_015 +MSGFMT_015 +GMSGFMT +MSGFMT +GETTEXT_MACRO_VERSION +USE_NLS +LTLIBICONV +LIBICONV +SENDMAIL +USE_LDAPWRAPPER_FALSE +USE_LDAPWRAPPER_TRUE +USE_LDAP_FALSE +USE_LDAP_TRUE +LBER_LIBS +LDAP_CPPFLAGS +LDAPLIBS +GPGKEYS_LDAP +DNSLIBS +LIBGNUTLS_LIBS +LIBGNUTLS_CFLAGS +NTBTLS_LIBS +NTBTLS_CFLAGS +NTBTLS_CONFIG +NPTH_LIBS +NPTH_CFLAGS +NPTH_CONFIG +SHRED +LIBUTIL_LIBS +FUSERMOUNT +ENCFS +SQLITE3_FALSE +SQLITE3_TRUE +SQLITE3_LIBS +SQLITE3_CFLAGS +DL_LIBS +LIBUSB_CPPFLAGS +LIBUSB_LIBS +KSBA_LIBS +KSBA_CFLAGS +KSBA_CONFIG +LIBASSUAN_LIBS +LIBASSUAN_CFLAGS +LIBASSUAN_CONFIG +LIBGCRYPT_LIBS +LIBGCRYPT_CFLAGS +LIBGCRYPT_CONFIG +GPG_ERROR_MT_LIBS +GPG_ERROR_MT_CFLAGS +GPG_ERROR_LIBS +GPG_ERROR_CFLAGS +GPG_ERROR_CONFIG +HAVE_ANDROID_SYSTEM_FALSE +HAVE_ANDROID_SYSTEM_TRUE +HAVE_W32CE_SYSTEM_FALSE +HAVE_W32CE_SYSTEM_TRUE +HAVE_W32_SYSTEM_FALSE +HAVE_W32_SYSTEM_TRUE +USE_SIMPLE_GETTEXT_FALSE +USE_SIMPLE_GETTEXT_TRUE +HAVE_DOSISH_SYSTEM_FALSE +HAVE_DOSISH_SYSTEM_TRUE +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +CC_FOR_BUILD +HAVE_USTAR_FALSE +HAVE_USTAR_TRUE +TAR +WINDRES +PERL +AR +RANLIB +LN_S +SYSROOT +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE +USE_LIBDNS_FALSE +USE_LIBDNS_TRUE +USE_GPG2_HACK_FALSE +USE_GPG2_HACK_TRUE +GNUPG_DIRMNGR_LDAP_PGM_FALSE +GNUPG_DIRMNGR_LDAP_PGM_TRUE +GNUPG_DIRMNGR_LDAP_PGM +GNUPG_PROTECT_TOOL_PGM_FALSE +GNUPG_PROTECT_TOOL_PGM_TRUE +GNUPG_PROTECT_TOOL_PGM +GNUPG_DIRMNGR_PGM_FALSE +GNUPG_DIRMNGR_PGM_TRUE +GNUPG_DIRMNGR_PGM +GNUPG_SCDAEMON_PGM_FALSE +GNUPG_SCDAEMON_PGM_TRUE +GNUPG_SCDAEMON_PGM +GNUPG_PINENTRY_PGM_FALSE +GNUPG_PINENTRY_PGM_TRUE +GNUPG_PINENTRY_PGM +GNUPG_AGENT_PGM_FALSE +GNUPG_AGENT_PGM_TRUE +GNUPG_AGENT_PGM +PACKAGE_GT +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_dependency_tracking +enable_gpg +enable_gpgsm +enable_scdaemon +enable_g13 +enable_dirmngr +enable_tools +enable_doc +enable_symcryptrun +enable_gpgtar +enable_wks_tools +with_agent_pgm +with_pinentry_pgm +with_scdaemon_pgm +with_dirmngr_pgm +with_protect_tool_pgm +with_dirmngr_ldap_pgm +enable_gpg2_is_gpg +enable_selinux_support +enable_large_secmem +enable_trust_models +enable_tofu +enable_libdns +enable_gpg_rsa +enable_gpg_ecdh +enable_gpg_ecdsa +enable_gpg_eddsa +enable_gpg_idea +enable_gpg_cast5 +enable_gpg_blowfish +enable_gpg_aes128 +enable_gpg_aes192 +enable_gpg_aes256 +enable_gpg_twofish +enable_gpg_camellia128 +enable_gpg_camellia192 +enable_gpg_camellia256 +enable_gpg_md5 +enable_gpg_rmd160 +enable_gpg_sha224 +enable_gpg_sha384 +enable_gpg_sha512 +enable_zip +enable_bzip2 +enable_exec +enable_photo_viewers +with_photo_viewer +enable_key_cache +with_capabilities +enable_card_support +enable_ccid_driver +enable_dirmngr_auto_start +enable_maintainer_mode +enable_largefile +with_tar +with_libgpg_error_prefix +with_gpg_error_prefix +with_libgcrypt_prefix +with_libassuan_prefix +with_ksba_prefix +enable_sqlite +with_npth_prefix +enable_ntbtls +with_ntbtls_prefix +enable_gnutls +enable_ldap +with_ldap +with_mailprog +with_gnu_ld +enable_rpath +with_libiconv_prefix +enable_nls +with_libintl_prefix +enable_endian_check +enable_regex +with_regex +with_zlib +with_bzip2 +with_readline +enable_optimization +enable_build_timestamp +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +SYSROOT +CC_FOR_BUILD +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +SQLITE3_CFLAGS +SQLITE3_LIBS +LIBGNUTLS_CFLAGS +LIBGNUTLS_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures gnupg 2.1.17 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/gnupg] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of gnupg 2.1.17:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --disable-gpg do not build the gpg program + --disable-gpgsm do not build the gpgsm program + --disable-scdaemon do not build the scdaemon program + --enable-g13 build the g13 program + --disable-dirmngr do not build the dirmngr program + --disable-tools do not build the tools program + --disable-doc do not build the doc program + --enable-symcryptrun build the symcryptrun program + --disable-gpgtar do not build the gpgtar program + --enable-wks-tools build the wks-tools program + --enable-gpg2-is-gpg Set installed name of gpg2 to gpg + --enable-selinux-support + enable SELinux support + --enable-large-secmem allocate extra secure memory + --disable-trust-models disable all trust models except "always" + --disable-tofu disable the TOFU trust model + --disable-libdns do not build with libdns support + --disable-gpg-rsa disable the RSA public key algorithm in gpg + --disable-gpg-ecdh disable the ECDH public key algorithm in gpg + --disable-gpg-ecdsa disable the ECDSA public key algorithm in gpg + --disable-gpg-eddsa disable the EdDSA public key algorithm in gpg + --disable-gpg-idea disable the IDEA cipher algorithm in gpg + --disable-gpg-cast5 disable the CAST5 cipher algorithm in gpg + --disable-gpg-blowfish disable the BLOWFISH cipher algorithm in gpg + --disable-gpg-aes128 disable the AES128 cipher algorithm in gpg + --disable-gpg-aes192 disable the AES192 cipher algorithm in gpg + --disable-gpg-aes256 disable the AES256 cipher algorithm in gpg + --disable-gpg-twofish disable the TWOFISH cipher algorithm in gpg + --disable-gpg-camellia128 + disable the CAMELLIA128 cipher algorithm in gpg + --disable-gpg-camellia192 + disable the CAMELLIA192 cipher algorithm in gpg + --disable-gpg-camellia256 + disable the CAMELLIA256 cipher algorithm in gpg + --disable-gpg-md5 disable the MD5 hash algorithm in gpg + --disable-gpg-rmd160 disable the RIPE-MD160 hash algorithm in gpg + --disable-gpg-sha224 disable the SHA-224 hash algorithm in gpg + --disable-gpg-sha384 disable the SHA-384 hash algorithm in gpg + --disable-gpg-sha512 disable the SHA-512 hash algorithm in gpg + --disable-zip disable the ZIP and ZLIB compression algorithm + --disable-bzip2 disable the BZIP2 compression algorithm + --disable-exec disable all external program execution + --disable-photo-viewers disable photo ID viewers + --enable-key-cache=SIZE Set key cache to SIZE (default 4096) + --disable-card-support disable smartcard support + --disable-ccid-driver disable the internal CCID driver + --disable-dirmngr-auto-start + disable auto starting of the dirmngr + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + --disable-largefile omit support for large files + --disable-sqlite disable the use of SQLITE + --disable-ntbtls disable the use of NTBTLS as TLS library + --disable-gnutls disable GNUTLS as fallback TLS library + --disable-ldap disable LDAP support + --disable-rpath do not hardcode runtime library paths + --disable-nls do not use Native Language Support + --disable-endian-check disable the endian check and trust the OS provided + macros + --disable-regex do not handle regular expressions in trust + signatures + --disable-optimization disable compiler optimization + --enable-build-timestamp + set an explicit build timestamp for reproducibility. + (default is the current time in ISO-8601 format) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-agent-pgm=PATH Use PATH as the default for the agent) + --with-pinentry-pgm=PATH Use PATH as the default for the pinentry) + --with-scdaemon-pgm=PATH Use PATH as the default for the scdaemon) + --with-dirmngr-pgm=PATH Use PATH as the default for the dirmngr) + --with-protect-tool-pgm=PATH Use PATH as the default for the protect-tool) + --with-dirmngr-ldap-pgm=PATH Use PATH as the default for the dirmngr ldap wrapper) + --with-photo-viewer=FIXED_VIEWER set a fixed photo ID viewer + --with-capabilities use linux capabilities default=no + --with-tar=PATH look for a tar program in PATH + --with-libgpg-error-prefix=PFX + prefix where GPG Error is installed (optional) + + --with-libgcrypt-prefix=PFX + prefix where LIBGCRYPT is installed (optional) + --with-libassuan-prefix=PFX + prefix where LIBASSUAN is installed (optional) + --with-ksba-prefix=PFX prefix where KSBA is installed (optional) + --with-npth-prefix=PFX prefix where NPTH is installed (optional) + --with-ntbtls-prefix=PFX + prefix where NTBTLS is installed (optional) + --with-ldap=DIR look for the LDAP library in DIR + --with-mailprog=NAME use "NAME -t" for mail transport + --with-gnu-ld assume the C compiler uses GNU ld default=no + --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib + --without-libiconv-prefix don't search for libiconv in includedir and libdir + --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib + --without-libintl-prefix don't search for libintl in includedir and libdir + --with-regex=DIR look for regex in DIR + --with-zlib=DIR use libz in DIR + --with-bzip2=DIR look for bzip2 in DIR + --with-readline=DIR look for the readline library in DIR + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + SYSROOT locate config scripts also below that directory + CC_FOR_BUILD + build system C compiler + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + SQLITE3_CFLAGS + C compiler flags for SQLITE3, overriding pkg-config + SQLITE3_LIBS + linker flags for SQLITE3, overriding pkg-config + LIBGNUTLS_CFLAGS + C compiler flags for LIBGNUTLS, overriding pkg-config + LIBGNUTLS_LIBS + linker flags for LIBGNUTLS, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +gnupg configure 2.1.17 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ------------------------------------- ## +## Report this to https://bugs.gnupg.org ## +## ------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by gnupg $as_me 2.1.17, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +gt_needs="$gt_needs need-ngettext" +as_fn_append ac_header_list " sys/socket.h" +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +NEED_GPG_ERROR_VERSION=1.24 + +NEED_LIBGCRYPT_API=1 +NEED_LIBGCRYPT_VERSION=1.7.0 + +NEED_LIBASSUAN_API=2 +NEED_LIBASSUAN_VERSION=2.4.3 + +NEED_KSBA_API=1 +NEED_KSBA_VERSION=1.3.4 + +NEED_NTBTLS_API=1 +NEED_NTBTLS_VERSION=0.1.0 + +NEED_NPTH_API=1 +NEED_NPTH_VERSION=1.2 + + +NEED_GNUTLS_VERSION=3.0 + +NEED_SQLITE_VERSION=3.7 + +development_version=no +PACKAGE=$PACKAGE_NAME +PACKAGE_GT=${PACKAGE_NAME}2 +VERSION=$PACKAGE_VERSION + +ac_aux_dir= +for ac_dir in build-aux "$srcdir"/build-aux; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +ac_config_headers="$ac_config_headers config.h" + +am__api_version='1.14' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='gnupg' + VERSION='2.1.17' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: autobuild project... ${PACKAGE_NAME:-$PACKAGE}" >&5 +$as_echo "$as_me: autobuild project... ${PACKAGE_NAME:-$PACKAGE}" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: autobuild revision... ${PACKAGE_VERSION:-$VERSION}" >&5 +$as_echo "$as_me: autobuild revision... ${PACKAGE_VERSION:-$VERSION}" >&6;} + hostname=`hostname` + if test "$hostname"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: autobuild hostname... $hostname" >&5 +$as_echo "$as_me: autobuild hostname... $hostname" >&6;} + fi + + date=`date +%Y%m%d-%H%M%S` + if test "$?" != 0; then + date=`date` + fi + if test "$date"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: autobuild timestamp... $date" >&5 +$as_echo "$as_me: autobuild timestamp... $date" >&6;} + fi + + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + + +# Before we do anything with the C compiler, we first save the user's +# CFLAGS (they are restored at the end of the configure script). This +# is because some configure checks don't work with -Werror, but we'd +# like to use -Werror with our build. +CFLAGS_orig=$CFLAGS +CFLAGS= + +# Some status variables. +have_gpg_error=no +have_libgcrypt=no +have_libassuan=no +have_ksba=no +have_ntbtls=no +have_gnutls=no +have_sqlite=no +have_npth=no +have_libusb=no +have_system_resolver=no +gnupg_have_ldap="n/a" + +use_zip=yes +use_bzip2=yes +use_exec=yes +use_trust_models=yes +use_tofu=yes +use_libdns=yes +card_support=yes +use_ccid_driver=auto +dirmngr_auto_start=yes +use_tls_library=no +large_secmem=no +show_tor_support=no + + + + build_gpg=yes + + # Check whether --enable-gpg was given. +if test "${enable_gpg+set}" = set; then : + enableval=$enable_gpg; build_gpg=$enableval +else + build_gpg=yes +fi + + + case "$build_gpg" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-gpg" "$LINENO" 5 + ;; + esac + + + + build_gpgsm=yes + + # Check whether --enable-gpgsm was given. +if test "${enable_gpgsm+set}" = set; then : + enableval=$enable_gpgsm; build_gpgsm=$enableval +else + build_gpgsm=yes +fi + + + case "$build_gpgsm" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-gpgsm" "$LINENO" 5 + ;; + esac + + +# The agent is a required part and can't be disabled anymore. +build_agent=yes + + build_scdaemon=yes + + # Check whether --enable-scdaemon was given. +if test "${enable_scdaemon+set}" = set; then : + enableval=$enable_scdaemon; build_scdaemon=$enableval +else + build_scdaemon=yes +fi + + + case "$build_scdaemon" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-scdaemon" "$LINENO" 5 + ;; + esac + + + + build_g13=no + + # Check whether --enable-g13 was given. +if test "${enable_g13+set}" = set; then : + enableval=$enable_g13; build_g13=$enableval +else + build_g13=no +fi + + + case "$build_g13" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-g13" "$LINENO" 5 + ;; + esac + + + + build_dirmngr=yes + + # Check whether --enable-dirmngr was given. +if test "${enable_dirmngr+set}" = set; then : + enableval=$enable_dirmngr; build_dirmngr=$enableval +else + build_dirmngr=yes +fi + + + case "$build_dirmngr" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-dirmngr" "$LINENO" 5 + ;; + esac + + + + build_tools=yes + + # Check whether --enable-tools was given. +if test "${enable_tools+set}" = set; then : + enableval=$enable_tools; build_tools=$enableval +else + build_tools=yes +fi + + + case "$build_tools" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-tools" "$LINENO" 5 + ;; + esac + + + + build_doc=yes + + # Check whether --enable-doc was given. +if test "${enable_doc+set}" = set; then : + enableval=$enable_doc; build_doc=$enableval +else + build_doc=yes +fi + + + case "$build_doc" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-doc" "$LINENO" 5 + ;; + esac + + + + build_symcryptrun=no + + # Check whether --enable-symcryptrun was given. +if test "${enable_symcryptrun+set}" = set; then : + enableval=$enable_symcryptrun; build_symcryptrun=$enableval +else + build_symcryptrun=no +fi + + + case "$build_symcryptrun" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-symcryptrun" "$LINENO" 5 + ;; + esac + + +# We use gpgtar to unpack test data, hence we always build it. If the +# user opts out, we simply don't install it. + + build_gpgtar=yes + + # Check whether --enable-gpgtar was given. +if test "${enable_gpgtar+set}" = set; then : + enableval=$enable_gpgtar; build_gpgtar=$enableval +else + build_gpgtar=yes +fi + + + case "$build_gpgtar" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-gpgtar" "$LINENO" 5 + ;; + esac + + + + build_wks_tools=no + + # Check whether --enable-wks-tools was given. +if test "${enable_wks_tools+set}" = set; then : + enableval=$enable_wks_tools; build_wks_tools=$enableval +else + build_wks_tools=no +fi + + + case "$build_wks_tools" in + no|yes) + ;; + *) + as_fn_error $? "only yes or no allowed for feature --enable-wks-tools" "$LINENO" 5 + ;; + esac + + + + + + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_GT "$PACKAGE_GT" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NEED_LIBGCRYPT_VERSION "$NEED_LIBGCRYPT_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NEED_KSBA_VERSION "$NEED_KSBA_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NEED_NTBTLS_VERSION "$NEED_NTBTLS_VERSION" +_ACEOF + + + + +# The default is to use the modules from this package and the few +# other packages in a standard place; i.e where this package gets +# installed. With these options it is possible to override these +# ${prefix} depended values with fixed paths, which can't be replaced +# at make time. See also am/cmacros.am and the defaults in AH_BOTTOM. + +# Check whether --with-agent-pgm was given. +if test "${with_agent_pgm+set}" = set; then : + withval=$with_agent_pgm; GNUPG_AGENT_PGM="$withval" +else + GNUPG_AGENT_PGM="" +fi + + + if test -n "$GNUPG_AGENT_PGM"; then + GNUPG_AGENT_PGM_TRUE= + GNUPG_AGENT_PGM_FALSE='#' +else + GNUPG_AGENT_PGM_TRUE='#' + GNUPG_AGENT_PGM_FALSE= +fi + +show_gnupg_agent_pgm="(default)" +test -n "$GNUPG_AGENT_PGM" && show_gnupg_agent_pgm="$GNUPG_AGENT_PGM" + + +# Check whether --with-pinentry-pgm was given. +if test "${with_pinentry_pgm+set}" = set; then : + withval=$with_pinentry_pgm; GNUPG_PINENTRY_PGM="$withval" +else + GNUPG_PINENTRY_PGM="" +fi + + + if test -n "$GNUPG_PINENTRY_PGM"; then + GNUPG_PINENTRY_PGM_TRUE= + GNUPG_PINENTRY_PGM_FALSE='#' +else + GNUPG_PINENTRY_PGM_TRUE='#' + GNUPG_PINENTRY_PGM_FALSE= +fi + +show_gnupg_pinentry_pgm="(default)" +test -n "$GNUPG_PINENTRY_PGM" && show_gnupg_pinentry_pgm="$GNUPG_PINENTRY_PGM" + + + +# Check whether --with-scdaemon-pgm was given. +if test "${with_scdaemon_pgm+set}" = set; then : + withval=$with_scdaemon_pgm; GNUPG_SCDAEMON_PGM="$withval" +else + GNUPG_SCDAEMON_PGM="" +fi + + + if test -n "$GNUPG_SCDAEMON_PGM"; then + GNUPG_SCDAEMON_PGM_TRUE= + GNUPG_SCDAEMON_PGM_FALSE='#' +else + GNUPG_SCDAEMON_PGM_TRUE='#' + GNUPG_SCDAEMON_PGM_FALSE= +fi + +show_gnupg_scdaemon_pgm="(default)" +test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM" + + + +# Check whether --with-dirmngr-pgm was given. +if test "${with_dirmngr_pgm+set}" = set; then : + withval=$with_dirmngr_pgm; GNUPG_DIRMNGR_PGM="$withval" +else + GNUPG_DIRMNGR_PGM="" +fi + + + if test -n "$GNUPG_DIRMNGR_PGM"; then + GNUPG_DIRMNGR_PGM_TRUE= + GNUPG_DIRMNGR_PGM_FALSE='#' +else + GNUPG_DIRMNGR_PGM_TRUE='#' + GNUPG_DIRMNGR_PGM_FALSE= +fi + +show_gnupg_dirmngr_pgm="(default)" +test -n "$GNUPG_DIRMNGR_PGM" && show_gnupg_dirmngr_pgm="$GNUPG_DIRMNGR_PGM" + + +# Check whether --with-protect-tool-pgm was given. +if test "${with_protect_tool_pgm+set}" = set; then : + withval=$with_protect_tool_pgm; GNUPG_PROTECT_TOOL_PGM="$withval" +else + GNUPG_PROTECT_TOOL_PGM="" +fi + + + if test -n "$GNUPG_PROTECT_TOOL_PGM"; then + GNUPG_PROTECT_TOOL_PGM_TRUE= + GNUPG_PROTECT_TOOL_PGM_FALSE='#' +else + GNUPG_PROTECT_TOOL_PGM_TRUE='#' + GNUPG_PROTECT_TOOL_PGM_FALSE= +fi + +show_gnupg_protect_tool_pgm="(default)" +test -n "$GNUPG_PROTECT_TOOL_PGM" \ + && show_gnupg_protect_tool_pgm="$GNUPG_PROTECT_TOOL_PGM" + + +# Check whether --with-dirmngr-ldap-pgm was given. +if test "${with_dirmngr_ldap_pgm+set}" = set; then : + withval=$with_dirmngr_ldap_pgm; GNUPG_DIRMNGR_LDAP_PGM="$withval" +else + GNUPG_DIRMNGR_LDAP_PGM="" +fi + + + if test -n "$GNUPG_DIRMNGR_LDAP_PGM"; then + GNUPG_DIRMNGR_LDAP_PGM_TRUE= + GNUPG_DIRMNGR_LDAP_PGM_FALSE='#' +else + GNUPG_DIRMNGR_LDAP_PGM_TRUE='#' + GNUPG_DIRMNGR_LDAP_PGM_FALSE= +fi + +show_gnupg_dirmngr_ldap_pgm="(default)" +test -n "$GNUPG_DIRMNGR_LDAP_PGM" \ + && show_gnupg_dirmngr_ldap_pgm="$GNUPG_DIRMNGR_LDAP_PGM" + +# +# On some platforms gpg2 is usually installed as gpg without using a +# symlink. For correct operation of gpgconf it needs to know the +# installed name of gpg. This option sets "gpg2"'s installed name to +# just "gpg". Note that it might be required to rename gpg2 to gpg +# manually after the build process. +# +# Check whether --enable-gpg2-is-gpg was given. +if test "${enable_gpg2_is_gpg+set}" = set; then : + enableval=$enable_gpg2_is_gpg; gpg2_is_gpg=$enableval +fi + +if test "$gpg2_is_gpg" != "yes"; then + +$as_echo "#define USE_GPG2_HACK 1" >>confdefs.h + +fi + if test "$gpg2_is_gpg" != "yes"; then + USE_GPG2_HACK_TRUE= + USE_GPG2_HACK_FALSE='#' +else + USE_GPG2_HACK_TRUE='#' + USE_GPG2_HACK_FALSE= +fi + + + +# SELinux support includes tracking of sensitive files to avoid +# leaking their contents through processing these files by gpg itself +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether SELinux support is requested" >&5 +$as_echo_n "checking whether SELinux support is requested... " >&6; } +# Check whether --enable-selinux-support was given. +if test "${enable_selinux_support+set}" = set; then : + enableval=$enable_selinux_support; selinux_support=$enableval +else + selinux_support=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $selinux_support" >&5 +$as_echo "$selinux_support" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to allocate extra secure memory" >&5 +$as_echo_n "checking whether to allocate extra secure memory... " >&6; } +# Check whether --enable-large-secmem was given. +if test "${enable_large_secmem+set}" = set; then : + enableval=$enable_large_secmem; large_secmem=$enableval +else + large_secmem=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $large_secmem" >&5 +$as_echo "$large_secmem" >&6; } +if test "$large_secmem" = yes ; then + SECMEM_BUFFER_SIZE=65536 +else + SECMEM_BUFFER_SIZE=32768 +fi + +cat >>confdefs.h <<_ACEOF +#define SECMEM_BUFFER_SIZE $SECMEM_BUFFER_SIZE +_ACEOF + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable trust models" >&5 +$as_echo_n "checking whether to enable trust models... " >&6; } +# Check whether --enable-trust-models was given. +if test "${enable_trust_models+set}" = set; then : + enableval=$enable_trust_models; use_trust_models=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_trust_models" >&5 +$as_echo "$use_trust_models" >&6; } +if test "$use_trust_models" = no ; then + +$as_echo "#define NO_TRUST_MODELS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable TOFU" >&5 +$as_echo_n "checking whether to enable TOFU... " >&6; } +# Check whether --enable-tofu was given. +if test "${enable_tofu+set}" = set; then : + enableval=$enable_tofu; use_tofu=$enableval +else + use_tofu=$use_trust_models +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_tofu" >&5 +$as_echo "$use_tofu" >&6; } +if test "$use_trust_models" = no && test "$use_tofu" = yes; then + as_fn_error $? "both --disable-trust-models and --enable-tofu given" "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable libdns" >&5 +$as_echo_n "checking whether to enable libdns... " >&6; } +# Check whether --enable-libdns was given. +if test "${enable_libdns+set}" = set; then : + enableval=$enable_libdns; use_libdns=$enableval +else + use_libdns=yes +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_libdns" >&5 +$as_echo "$use_libdns" >&6; } +if test x"$use_libdns" = xyes ; then + +$as_echo "#define USE_LIBDNS 1" >>confdefs.h + +fi + if test "$use_libdns" = yes; then + USE_LIBDNS_TRUE= + USE_LIBDNS_FALSE='#' +else + USE_LIBDNS_TRUE='#' + USE_LIBDNS_FALSE= +fi + + + +# +# Options to disable algorithm +# + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the RSA public key for gpg" >&5 +$as_echo_n "checking whether to enable the RSA public key for gpg... " >&6; } + # Check whether --enable-gpg-rsa was given. +if test "${enable_gpg_rsa+set}" = set; then : + enableval=$enable_gpg_rsa; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_RSA 1" >>confdefs.h + + fi + +# Elgamal is a MUST algorithm +# DSA is a MUST algorithm +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the ECDH public key for gpg" >&5 +$as_echo_n "checking whether to enable the ECDH public key for gpg... " >&6; } + # Check whether --enable-gpg-ecdh was given. +if test "${enable_gpg_ecdh+set}" = set; then : + enableval=$enable_gpg_ecdh; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_ECDH 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the ECDSA public key for gpg" >&5 +$as_echo_n "checking whether to enable the ECDSA public key for gpg... " >&6; } + # Check whether --enable-gpg-ecdsa was given. +if test "${enable_gpg_ecdsa+set}" = set; then : + enableval=$enable_gpg_ecdsa; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_ECDSA 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the EdDSA public key for gpg" >&5 +$as_echo_n "checking whether to enable the EdDSA public key for gpg... " >&6; } + # Check whether --enable-gpg-eddsa was given. +if test "${enable_gpg_eddsa+set}" = set; then : + enableval=$enable_gpg_eddsa; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_EDDSA 1" >>confdefs.h + + fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the IDEA cipher for gpg" >&5 +$as_echo_n "checking whether to enable the IDEA cipher for gpg... " >&6; } + # Check whether --enable-gpg-idea was given. +if test "${enable_gpg_idea+set}" = set; then : + enableval=$enable_gpg_idea; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_IDEA 1" >>confdefs.h + + fi + +# 3DES is a MUST algorithm +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the CAST5 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the CAST5 cipher for gpg... " >&6; } + # Check whether --enable-gpg-cast5 was given. +if test "${enable_gpg_cast5+set}" = set; then : + enableval=$enable_gpg_cast5; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_CAST5 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the BLOWFISH cipher for gpg" >&5 +$as_echo_n "checking whether to enable the BLOWFISH cipher for gpg... " >&6; } + # Check whether --enable-gpg-blowfish was given. +if test "${enable_gpg_blowfish+set}" = set; then : + enableval=$enable_gpg_blowfish; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_BLOWFISH 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the AES128 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the AES128 cipher for gpg... " >&6; } + # Check whether --enable-gpg-aes128 was given. +if test "${enable_gpg_aes128+set}" = set; then : + enableval=$enable_gpg_aes128; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_AES128 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the AES192 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the AES192 cipher for gpg... " >&6; } + # Check whether --enable-gpg-aes192 was given. +if test "${enable_gpg_aes192+set}" = set; then : + enableval=$enable_gpg_aes192; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_AES192 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the AES256 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the AES256 cipher for gpg... " >&6; } + # Check whether --enable-gpg-aes256 was given. +if test "${enable_gpg_aes256+set}" = set; then : + enableval=$enable_gpg_aes256; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_AES256 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the TWOFISH cipher for gpg" >&5 +$as_echo_n "checking whether to enable the TWOFISH cipher for gpg... " >&6; } + # Check whether --enable-gpg-twofish was given. +if test "${enable_gpg_twofish+set}" = set; then : + enableval=$enable_gpg_twofish; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_TWOFISH 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the CAMELLIA128 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the CAMELLIA128 cipher for gpg... " >&6; } + # Check whether --enable-gpg-camellia128 was given. +if test "${enable_gpg_camellia128+set}" = set; then : + enableval=$enable_gpg_camellia128; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_CAMELLIA128 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the CAMELLIA192 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the CAMELLIA192 cipher for gpg... " >&6; } + # Check whether --enable-gpg-camellia192 was given. +if test "${enable_gpg_camellia192+set}" = set; then : + enableval=$enable_gpg_camellia192; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_CAMELLIA192 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the CAMELLIA256 cipher for gpg" >&5 +$as_echo_n "checking whether to enable the CAMELLIA256 cipher for gpg... " >&6; } + # Check whether --enable-gpg-camellia256 was given. +if test "${enable_gpg_camellia256+set}" = set; then : + enableval=$enable_gpg_camellia256; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_CAMELLIA256 1" >>confdefs.h + + fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the MD5 hash for gpg" >&5 +$as_echo_n "checking whether to enable the MD5 hash for gpg... " >&6; } + # Check whether --enable-gpg-md5 was given. +if test "${enable_gpg_md5+set}" = set; then : + enableval=$enable_gpg_md5; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_MD5 1" >>confdefs.h + + fi + +# SHA1 is a MUST algorithm +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the RIPE-MD160 hash for gpg" >&5 +$as_echo_n "checking whether to enable the RIPE-MD160 hash for gpg... " >&6; } + # Check whether --enable-gpg-rmd160 was given. +if test "${enable_gpg_rmd160+set}" = set; then : + enableval=$enable_gpg_rmd160; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_RMD160 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the SHA-224 hash for gpg" >&5 +$as_echo_n "checking whether to enable the SHA-224 hash for gpg... " >&6; } + # Check whether --enable-gpg-sha224 was given. +if test "${enable_gpg_sha224+set}" = set; then : + enableval=$enable_gpg_sha224; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_SHA224 1" >>confdefs.h + + fi + +# SHA256 is a MUST algorithm for GnuPG. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the SHA-384 hash for gpg" >&5 +$as_echo_n "checking whether to enable the SHA-384 hash for gpg... " >&6; } + # Check whether --enable-gpg-sha384 was given. +if test "${enable_gpg_sha384+set}" = set; then : + enableval=$enable_gpg_sha384; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_SHA384 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the SHA-512 hash for gpg" >&5 +$as_echo_n "checking whether to enable the SHA-512 hash for gpg... " >&6; } + # Check whether --enable-gpg-sha512 was given. +if test "${enable_gpg_sha512+set}" = set; then : + enableval=$enable_gpg_sha512; +else + enableval=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + if test x"$enableval" = xyes ; then + +$as_echo "#define GPG_USE_SHA512 1" >>confdefs.h + + fi + + + +# Allow disabling of zip support. +# This is in general not a good idea because according to rfc4880 OpenPGP +# implementations SHOULD support ZLIB. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the ZIP and ZLIB compression algorithm" >&5 +$as_echo_n "checking whether to enable the ZIP and ZLIB compression algorithm... " >&6; } +# Check whether --enable-zip was given. +if test "${enable_zip+set}" = set; then : + enableval=$enable_zip; use_zip=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_zip" >&5 +$as_echo "$use_zip" >&6; } + +# Allow disabling of bzib2 support. +# It is defined only after we confirm the library is available later +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the BZIP2 compression algorithm" >&5 +$as_echo_n "checking whether to enable the BZIP2 compression algorithm... " >&6; } +# Check whether --enable-bzip2 was given. +if test "${enable_bzip2+set}" = set; then : + enableval=$enable_bzip2; use_bzip2=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_bzip2" >&5 +$as_echo "$use_bzip2" >&6; } + +# Configure option to allow or disallow execution of external +# programs, like a photo viewer. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable external program execution" >&5 +$as_echo_n "checking whether to enable external program execution... " >&6; } +# Check whether --enable-exec was given. +if test "${enable_exec+set}" = set; then : + enableval=$enable_exec; use_exec=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_exec" >&5 +$as_echo "$use_exec" >&6; } +if test "$use_exec" = no ; then + +$as_echo "#define NO_EXEC 1" >>confdefs.h + +fi + +if test "$use_exec" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable photo ID viewing" >&5 +$as_echo_n "checking whether to enable photo ID viewing... " >&6; } + # Check whether --enable-photo-viewers was given. +if test "${enable_photo_viewers+set}" = set; then : + enableval=$enable_photo_viewers; if test "$enableval" = no ; then + +$as_echo "#define DISABLE_PHOTO_VIEWER 1" >>confdefs.h + + fi +else + enableval=yes +fi + + gnupg_cv_enable_photo_viewers=$enableval + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enableval" >&5 +$as_echo "$enableval" >&6; } + + if test "$gnupg_cv_enable_photo_viewers" = yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use a fixed photo ID viewer" >&5 +$as_echo_n "checking whether to use a fixed photo ID viewer... " >&6; } + +# Check whether --with-photo-viewer was given. +if test "${with_photo_viewer+set}" = set; then : + withval=$with_photo_viewer; if test "$withval" = yes ; then + withval=no + elif test "$withval" != no ; then + +cat >>confdefs.h <<_ACEOF +#define FIXED_PHOTO_VIEWER "$withval" +_ACEOF + + fi +else + withval=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 +$as_echo "$withval" >&6; } + fi +fi + + +# +# Check for the key/uid cache size. This can't be zero, but can be +# pretty small on embedded systems. This is used for the gpg part. +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for the size of the key and uid cache" >&5 +$as_echo_n "checking for the size of the key and uid cache... " >&6; } +# Check whether --enable-key-cache was given. +if test "${enable_key_cache+set}" = set; then : + enableval=$enable_key_cache; +else + enableval=4096 +fi + +if test "$enableval" = "no"; then + enableval=5 +elif test "$enableval" = "yes" || test "$enableval" = ""; then + enableval=4096 +fi +key_cache_size=`echo "$enableval" | sed 's/[A-Za-z]//g'` +if test "$enableval" != "$key_cache_size" || test "$key_cache_size" -lt 5; then + as_fn_error $? "invalid key-cache size" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $key_cache_size" >&5 +$as_echo "$key_cache_size" >&6; } + +cat >>confdefs.h <<_ACEOF +#define PK_UID_CACHE_SIZE $key_cache_size +_ACEOF + + + + +# +# Check whether we want to use Linux capabilities +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether use of capabilities is requested" >&5 +$as_echo_n "checking whether use of capabilities is requested... " >&6; } + +# Check whether --with-capabilities was given. +if test "${with_capabilities+set}" = set; then : + withval=$with_capabilities; use_capabilities="$withval" +else + use_capabilities=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_capabilities" >&5 +$as_echo "$use_capabilities" >&6; } + +# +# Check whether to disable the card support +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether smartcard support is requested" >&5 +$as_echo_n "checking whether smartcard support is requested... " >&6; } +# Check whether --enable-card-support was given. +if test "${enable_card_support+set}" = set; then : + enableval=$enable_card_support; card_support=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $card_support" >&5 +$as_echo "$card_support" >&6; } +if test "$card_support" = yes ; then + +$as_echo "#define ENABLE_CARD_SUPPORT 1" >>confdefs.h + +else + build_scdaemon=no +fi + +# +# Allow disabling of internal CCID support. +# It is defined only after we confirm the library is available later +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the internal CCID driver" >&5 +$as_echo_n "checking whether to enable the internal CCID driver... " >&6; } +# Check whether --enable-ccid-driver was given. +if test "${enable_ccid_driver+set}" = set; then : + enableval=$enable_ccid_driver; use_ccid_driver=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_ccid_driver" >&5 +$as_echo "$use_ccid_driver" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to auto start dirmngr" >&5 +$as_echo_n "checking whether to auto start dirmngr... " >&6; } +# Check whether --enable-dirmngr-auto-start was given. +if test "${enable_dirmngr_auto_start+set}" = set; then : + enableval=$enable_dirmngr_auto_start; dirmngr_auto_start=$enableval +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dirmngr_auto_start" >&5 +$as_echo "$dirmngr_auto_start" >&6; } +if test "$dirmngr_auto_start" = yes ; then + +$as_echo "#define USE_DIRMNGR_AUTO_START 1" >>confdefs.h + +fi + + +# +# To avoid double inclusion of config.h which might happen at some +# places, we add the usual double inclusion protection at the top of +# config.h. +# + + +# +# Stuff which goes at the bottom of config.h. +# + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + + +# Checks for programs. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for programs" >&5 +$as_echo "$as_me: checking for programs" >&6;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +missing_dir=`cd $ac_aux_dir && pwd` + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +if test "x$ac_cv_prog_cc_c89" = "xno" ; then + as_fn_error $? "No C-89 compiler found" "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + +# Extract the first word of ""perl"", so it can be a program name with args. +set dummy "perl"; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PERL+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PERL in + [\\/]* | ?:[\\/]*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PERL=$ac_cv_path_PERL +if test -n "$PERL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 +$as_echo "$PERL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. +set dummy ${ac_tool_prefix}windres; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_WINDRES+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$WINDRES"; then + ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_WINDRES="${ac_tool_prefix}windres" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +WINDRES=$ac_cv_prog_WINDRES +if test -n "$WINDRES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5 +$as_echo "$WINDRES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_WINDRES"; then + ac_ct_WINDRES=$WINDRES + # Extract the first word of "windres", so it can be a program name with args. +set dummy windres; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_WINDRES+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_WINDRES"; then + ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_WINDRES="windres" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES +if test -n "$ac_ct_WINDRES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5 +$as_echo "$ac_ct_WINDRES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_WINDRES" = x; then + WINDRES=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + WINDRES=$ac_ct_WINDRES + fi +else + WINDRES="$ac_cv_prog_WINDRES" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for strerror in -lcposix" >&5 +$as_echo_n "checking for strerror in -lcposix... " >&6; } +if ${ac_cv_lib_cposix_strerror+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcposix $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strerror (); +int +main () +{ +return strerror (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cposix_strerror=yes +else + ac_cv_lib_cposix_strerror=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cposix_strerror" >&5 +$as_echo "$ac_cv_lib_cposix_strerror" >&6; } +if test "x$ac_cv_lib_cposix_strerror" = xyes; then : + LIBS="$LIBS -lcposix" +fi + + + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + + +# Check whether --with-tar was given. +if test "${with_tar+set}" = set; then : + withval=$with_tar; _do_tar=$withval +fi + + + if test x$_do_tar != xno ; then + + if test x$_do_tar = x ; then + # Extract the first word of ""tar"", so it can be a program name with args. +set dummy "tar"; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_TAR+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $TAR in + [\\/]* | ?:[\\/]*) + ac_cv_path_TAR="$TAR" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_TAR="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +TAR=$ac_cv_path_TAR +if test -n "$TAR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TAR" >&5 +$as_echo "$TAR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + _mytar=$ac_cv_path_TAR + fi + + # Check if our tar is ustar format. If so, it's good. TODO: Add some + # code to check various options, etc, to try and create ustar + # format. + + if test x$_mytar != x ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $_mytar speaks USTAR" >&5 +$as_echo_n "checking whether $_mytar speaks USTAR... " >&6; } + echo hithere > conftest.txt + $_mytar -cf - conftest.txt | (dd skip=257 bs=1 count=5 2>/dev/null || cat) | grep ustar > /dev/null + _tar_bad=$? + rm conftest.txt + + if test x$_tar_bad = x0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + fi + fi + + if test x$_tar_bad = x0; then + HAVE_USTAR_TRUE= + HAVE_USTAR_FALSE='#' +else + HAVE_USTAR_TRUE='#' + HAVE_USTAR_FALSE= +fi + + + + +# We need to compile and run a program on the build machine. A +# comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in +# the AC archive is broken for autoconf 2.57. Given that there is no +# newer version of that macro, we assume that it is also broken for +# autoconf 2.61 and thus we use a simple but usually sufficient +# approach. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cc for build" >&5 +$as_echo_n "checking for cc for build... " >&6; } +if test "$cross_compiling" = "yes"; then + CC_FOR_BUILD="${CC_FOR_BUILD-cc}" +else + CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC_FOR_BUILD" >&5 +$as_echo "$CC_FOR_BUILD" >&6; } + + +# We need to call this macro because other pkg-config macros are +# not always used. + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + + +try_gettext=yes +require_iconv=yes +have_dosish_system=no +have_w32_system=no +have_w32ce_system=no +have_android_system=no +use_simple_gettext=no +use_ldapwrapper=yes +mmap_needed=yes +case "${host}" in + *-mingw32*) + # special stuff for Windoze NT + ac_cv_have_dev_random=no + +$as_echo "#define USE_ONLY_8DOT3 1" >>confdefs.h + + +$as_echo "#define USE_SIMPLE_GETTEXT 1" >>confdefs.h + + have_dosish_system=yes + have_w32_system=yes + require_iconv=no + use_ldapwrapper=no # Fixme: Do this only for CE. + case "${host}" in + *-mingw32ce*) + have_w32ce_system=yes + ;; + *) + +$as_echo "#define HAVE_DRIVE_LETTERS 1" >>confdefs.h + + ;; + esac + try_gettext="no" + use_simple_gettext=yes + mmap_needed=no + ;; + i?86-emx-os2 | i?86-*-os2*emx ) + # OS/2 with the EMX environment + ac_cv_have_dev_random=no + $as_echo "#define HAVE_DRIVE_LETTERS 1" >>confdefs.h + + have_dosish_system=yes + try_gettext="no" + ;; + + i?86-*-msdosdjgpp*) + # DOS with the DJGPP environment + ac_cv_have_dev_random=no + $as_echo "#define HAVE_DRIVE_LETTERS 1" >>confdefs.h + + have_dosish_system=yes + try_gettext="no" + ;; + + *-*-hpux*) + if test -z "$GCC" ; then + CFLAGS="-Ae -D_HPUX_SOURCE $CFLAGS" + fi + ;; + *-dec-osf4*) + if test -z "$GCC" ; then + # Suppress all warnings + # to get rid of the unsigned/signed char mismatch warnings. + CFLAGS="-w $CFLAGS" + fi + ;; + *-dec-osf5*) + if test -z "$GCC" ; then + # Use the newer compiler `-msg_disable ptrmismatch1' to + # get rid of the unsigned/signed char mismatch warnings. + # Using this may hide other pointer mismatch warnings, but + # it at least lets other warning classes through + CFLAGS="-msg_disable ptrmismatch1 $CFLAGS" + fi + ;; + m68k-atari-mint) + ;; + *-linux-android*) + have_android_system=yes + # Android is fully utf-8 and we do not want to use iconv to + # keeps things simple + require_iconv=no + ;; + *) + ;; +esac + +if test "$have_dosish_system" = yes; then + +$as_echo "#define HAVE_DOSISH_SYSTEM 1" >>confdefs.h + +fi + if test "$have_dosish_system" = yes; then + HAVE_DOSISH_SYSTEM_TRUE= + HAVE_DOSISH_SYSTEM_FALSE='#' +else + HAVE_DOSISH_SYSTEM_TRUE='#' + HAVE_DOSISH_SYSTEM_FALSE= +fi + + + if test x"$use_simple_gettext" = xyes; then + USE_SIMPLE_GETTEXT_TRUE= + USE_SIMPLE_GETTEXT_FALSE='#' +else + USE_SIMPLE_GETTEXT_TRUE='#' + USE_SIMPLE_GETTEXT_FALSE= +fi + + +if test "$have_w32_system" = yes; then + +$as_echo "#define HAVE_W32_SYSTEM 1" >>confdefs.h + + if test "$have_w32ce_system" = yes; then + +$as_echo "#define HAVE_W32CE_SYSTEM 1" >>confdefs.h + + fi +fi + if test "$have_w32_system" = yes; then + HAVE_W32_SYSTEM_TRUE= + HAVE_W32_SYSTEM_FALSE='#' +else + HAVE_W32_SYSTEM_TRUE='#' + HAVE_W32_SYSTEM_FALSE= +fi + + if test "$have_w32ce_system" = yes; then + HAVE_W32CE_SYSTEM_TRUE= + HAVE_W32CE_SYSTEM_FALSE='#' +else + HAVE_W32CE_SYSTEM_TRUE='#' + HAVE_W32CE_SYSTEM_FALSE= +fi + + +if test "$have_android_system" = yes; then + +$as_echo "#define HAVE_ANDROID_SYSTEM 1" >>confdefs.h + +fi + if test "$have_android_system" = yes; then + HAVE_ANDROID_SYSTEM_TRUE= + HAVE_ANDROID_SYSTEM_FALSE='#' +else + HAVE_ANDROID_SYSTEM_TRUE='#' + HAVE_ANDROID_SYSTEM_FALSE= +fi + + + +# (These need to go after AC_PROG_CC so that $EXEEXT is defined) + +cat >>confdefs.h <<_ACEOF +#define EXEEXT "$EXEEXT" +_ACEOF + + + +# +# Checks for libraries. +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libraries" >&5 +$as_echo "$as_me: checking for libraries" >&6;} + + +# +# libgpg-error is a library with error codes shared between GnuPG +# related projects. +# + + gpg_error_config_prefix="" + +# Check whether --with-libgpg-error-prefix was given. +if test "${with_libgpg_error_prefix+set}" = set; then : + withval=$with_libgpg_error_prefix; gpg_error_config_prefix="$withval" +fi + + + +# Check whether --with-gpg-error-prefix was given. +if test "${with_gpg_error_prefix+set}" = set; then : + withval=$with_gpg_error_prefix; gpg_error_config_prefix="$withval" +fi + + + if test x"${GPG_ERROR_CONFIG}" = x ; then + if test x"${gpg_error_config_prefix}" != x ; then + GPG_ERROR_CONFIG="${gpg_error_config_prefix}/bin/gpg-error-config" + else + case "${SYSROOT}" in + /*) + if test -x "${SYSROOT}/bin/gpg-error-config" ; then + GPG_ERROR_CONFIG="${SYSROOT}/bin/gpg-error-config" + fi + ;; + '') + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ignoring \$SYSROOT as it is not an absolute path." >&5 +$as_echo "$as_me: WARNING: Ignoring \$SYSROOT as it is not an absolute path." >&2;} + ;; + esac + fi + fi + + # Extract the first word of "gpg-error-config", so it can be a program name with args. +set dummy gpg-error-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_GPG_ERROR_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $GPG_ERROR_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_GPG_ERROR_CONFIG="$GPG_ERROR_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_GPG_ERROR_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_GPG_ERROR_CONFIG" && ac_cv_path_GPG_ERROR_CONFIG="no" + ;; +esac +fi +GPG_ERROR_CONFIG=$ac_cv_path_GPG_ERROR_CONFIG +if test -n "$GPG_ERROR_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GPG_ERROR_CONFIG" >&5 +$as_echo "$GPG_ERROR_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + min_gpg_error_version="$NEED_GPG_ERROR_VERSION" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GPG Error - version >= $min_gpg_error_version" >&5 +$as_echo_n "checking for GPG Error - version >= $min_gpg_error_version... " >&6; } + ok=no + if test "$GPG_ERROR_CONFIG" != "no" \ + && test -f "$GPG_ERROR_CONFIG" ; then + req_major=`echo $min_gpg_error_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_gpg_error_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)/\2/'` + gpg_error_config_version=`$GPG_ERROR_CONFIG $gpg_error_config_args --version` + major=`echo $gpg_error_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + minor=`echo $gpg_error_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -ge "$req_minor"; then + ok=yes + fi + fi + fi + fi + if test $ok = yes; then + GPG_ERROR_CFLAGS=`$GPG_ERROR_CONFIG $gpg_error_config_args --cflags` + GPG_ERROR_LIBS=`$GPG_ERROR_CONFIG $gpg_error_config_args --libs` + GPG_ERROR_MT_CFLAGS=`$GPG_ERROR_CONFIG $gpg_error_config_args --mt --cflags 2>/dev/null` + GPG_ERROR_MT_LIBS=`$GPG_ERROR_CONFIG $gpg_error_config_args --mt --libs 2>/dev/null` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($gpg_error_config_version)" >&5 +$as_echo "yes ($gpg_error_config_version)" >&6; } + have_gpg_error=yes + gpg_error_config_host=`$GPG_ERROR_CONFIG $gpg_error_config_args --host 2>/dev/null || echo none` + if test x"$gpg_error_config_host" != xnone ; then + if test x"$gpg_error_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $GPG_ERROR_CONFIG was +*** built for $gpg_error_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-gpg-error-prefix +*** to specify a matching config script or use \$SYSROOT. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $GPG_ERROR_CONFIG was +*** built for $gpg_error_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-gpg-error-prefix +*** to specify a matching config script or use \$SYSROOT. +***" >&2;} + gpg_config_script_warn="$gpg_config_script_warn libgpg-error" + fi + fi + else + GPG_ERROR_CFLAGS="" + GPG_ERROR_LIBS="" + GPG_ERROR_MT_CFLAGS="" + GPG_ERROR_MT_LIBS="" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_gpg_error=no + fi + + + + + + + +# +# Libgcrypt is our generic crypto library +# + + +# Check whether --with-libgcrypt-prefix was given. +if test "${with_libgcrypt_prefix+set}" = set; then : + withval=$with_libgcrypt_prefix; libgcrypt_config_prefix="$withval" +else + libgcrypt_config_prefix="" +fi + + if test x"${LIBGCRYPT_CONFIG}" = x ; then + if test x"${libgcrypt_config_prefix}" != x ; then + LIBGCRYPT_CONFIG="${libgcrypt_config_prefix}/bin/libgcrypt-config" + else + case "${SYSROOT}" in + /*) + if test -x "${SYSROOT}/bin/libgcrypt-config" ; then + LIBGCRYPT_CONFIG="${SYSROOT}/bin/libgcrypt-config" + fi + ;; + '') + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ignoring \$SYSROOT as it is not an absolute path." >&5 +$as_echo "$as_me: WARNING: Ignoring \$SYSROOT as it is not an absolute path." >&2;} + ;; + esac + fi + fi + + # Extract the first word of "libgcrypt-config", so it can be a program name with args. +set dummy libgcrypt-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LIBGCRYPT_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LIBGCRYPT_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_LIBGCRYPT_CONFIG="$LIBGCRYPT_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LIBGCRYPT_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_LIBGCRYPT_CONFIG" && ac_cv_path_LIBGCRYPT_CONFIG="no" + ;; +esac +fi +LIBGCRYPT_CONFIG=$ac_cv_path_LIBGCRYPT_CONFIG +if test -n "$LIBGCRYPT_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBGCRYPT_CONFIG" >&5 +$as_echo "$LIBGCRYPT_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + tmp="$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION" + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_libgcrypt_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_libgcrypt_api=0 + min_libgcrypt_version="$tmp" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBGCRYPT - version >= $min_libgcrypt_version" >&5 +$as_echo_n "checking for LIBGCRYPT - version >= $min_libgcrypt_version... " >&6; } + ok=no + if test "$LIBGCRYPT_CONFIG" != "no" ; then + req_major=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + req_micro=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version` + major=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1/'` + minor=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\2/'` + micro=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\3/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -gt "$req_minor"; then + ok=yes + else + if test "$minor" -eq "$req_minor"; then + if test "$micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + fi + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($libgcrypt_config_version)" >&5 +$as_echo "yes ($libgcrypt_config_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + if test $ok = yes; then + # If we have a recent libgcrypt, we should also check that the + # API is compatible + if test "$req_libgcrypt_api" -gt 0 ; then + tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking LIBGCRYPT API version" >&5 +$as_echo_n "checking LIBGCRYPT API version... " >&6; } + if test "$req_libgcrypt_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_libgcrypt_api got=$tmp" >&5 +$as_echo "does not match. want=$req_libgcrypt_api got=$tmp" >&6; } + fi + fi + fi + fi + if test $ok = yes; then + LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags` + LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs` + have_libgcrypt=yes + libgcrypt_config_host=`$LIBGCRYPT_CONFIG --host 2>/dev/null || echo none` + if test x"$libgcrypt_config_host" != xnone ; then + if test x"$libgcrypt_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script or use \$SYSROOT. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script or use \$SYSROOT. +***" >&2;} + gpg_config_script_warn="$gpg_config_script_warn libgcrypt" + fi + fi + else + LIBGCRYPT_CFLAGS="" + LIBGCRYPT_LIBS="" + have_libgcrypt=no + fi + + + + + +# +# libassuan is used for IPC +# + +# Check whether --with-libassuan-prefix was given. +if test "${with_libassuan_prefix+set}" = set; then : + withval=$with_libassuan_prefix; libassuan_config_prefix="$withval" +else + libassuan_config_prefix="" +fi + + if test x$libassuan_config_prefix != x ; then + libassuan_config_args="$libassuan_config_args --prefix=$libassuan_config_prefix" + if test x${LIBASSUAN_CONFIG+set} != xset ; then + LIBASSUAN_CONFIG=$libassuan_config_prefix/bin/libassuan-config + fi + fi + # Extract the first word of "libassuan-config", so it can be a program name with args. +set dummy libassuan-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LIBASSUAN_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LIBASSUAN_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_LIBASSUAN_CONFIG="$LIBASSUAN_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LIBASSUAN_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_LIBASSUAN_CONFIG" && ac_cv_path_LIBASSUAN_CONFIG="no" + ;; +esac +fi +LIBASSUAN_CONFIG=$ac_cv_path_LIBASSUAN_CONFIG +if test -n "$LIBASSUAN_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBASSUAN_CONFIG" >&5 +$as_echo "$LIBASSUAN_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + tmp="$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION" + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_libassuan_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_libassuan_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_libassuan_api=0 + min_libassuan_version="$tmp" + fi + + if test "$LIBASSUAN_CONFIG" != "no" ; then + libassuan_version=`$LIBASSUAN_CONFIG --version` + fi + libassuan_version_major=`echo $libassuan_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1/'` + libassuan_version_minor=`echo $libassuan_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\2/'` + libassuan_version_micro=`echo $libassuan_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\3/'` + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBASSUAN - version >= $min_libassuan_version" >&5 +$as_echo_n "checking for LIBASSUAN - version >= $min_libassuan_version... " >&6; } + ok=no + if test "$LIBASSUAN_CONFIG" != "no" ; then + + req_major=`echo $min_libassuan_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_libassuan_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + req_micro=`echo $min_libassuan_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + if test "$libassuan_version_major" -gt "$req_major"; then + ok=yes + else + if test "$libassuan_version_major" -eq "$req_major"; then + if test "$libassuan_version_minor" -gt "$req_minor"; then + ok=yes + else + if test "$libassuan_version_minor" -eq "$req_minor"; then + if test "$libassuan_version_micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + + fi + + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($libassuan_version)" >&5 +$as_echo "yes ($libassuan_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + if test $ok = yes; then + if test "$req_libassuan_api" -gt 0 ; then + tmp=`$LIBASSUAN_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking LIBASSUAN API version" >&5 +$as_echo_n "checking LIBASSUAN API version... " >&6; } + if test "$req_libassuan_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_libassuan_api got=$tmp." >&5 +$as_echo "does not match. want=$req_libassuan_api got=$tmp." >&6; } + fi + fi + fi + fi + + + if test $ok = yes; then + LIBASSUAN_CFLAGS=`$LIBASSUAN_CONFIG $libassuan_config_args --cflags` + LIBASSUAN_LIBS=`$LIBASSUAN_CONFIG $libassuan_config_args --libs` + have_libassuan=yes + else + LIBASSUAN_CFLAGS="" + LIBASSUAN_LIBS="" + have_libassuan=no + fi + + + +if test "$have_libassuan" = "yes"; then + +cat >>confdefs.h <<_ACEOF +#define GNUPG_LIBASSUAN_VERSION "$libassuan_version" +_ACEOF + + show_tor_support="only .onion" +fi + + +# +# libksba is our X.509 support library +# + + +# Check whether --with-ksba-prefix was given. +if test "${with_ksba_prefix+set}" = set; then : + withval=$with_ksba_prefix; ksba_config_prefix="$withval" +else + ksba_config_prefix="" +fi + + if test x$ksba_config_prefix != x ; then + ksba_config_args="$ksba_config_args --prefix=$ksba_config_prefix" + if test x${KSBA_CONFIG+set} != xset ; then + KSBA_CONFIG=$ksba_config_prefix/bin/ksba-config + fi + fi + + # Extract the first word of "ksba-config", so it can be a program name with args. +set dummy ksba-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_KSBA_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $KSBA_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_KSBA_CONFIG="$KSBA_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_KSBA_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_KSBA_CONFIG" && ac_cv_path_KSBA_CONFIG="no" + ;; +esac +fi +KSBA_CONFIG=$ac_cv_path_KSBA_CONFIG +if test -n "$KSBA_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $KSBA_CONFIG" >&5 +$as_echo "$KSBA_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + tmp="$NEED_KSBA_API:$NEED_KSBA_VERSION" + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_ksba_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_ksba_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_ksba_api=0 + min_ksba_version="$tmp" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for KSBA - version >= $min_ksba_version" >&5 +$as_echo_n "checking for KSBA - version >= $min_ksba_version... " >&6; } + ok=no + if test "$KSBA_CONFIG" != "no" ; then + req_major=`echo $min_ksba_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_ksba_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + req_micro=`echo $min_ksba_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + ksba_config_version=`$KSBA_CONFIG $ksba_config_args --version` + major=`echo $ksba_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1/'` + minor=`echo $ksba_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\2/'` + micro=`echo $ksba_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\3/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -gt "$req_minor"; then + ok=yes + else + if test "$minor" -eq "$req_minor"; then + if test "$micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + fi + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($ksba_config_version)" >&5 +$as_echo "yes ($ksba_config_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + if test $ok = yes; then + # Even if we have a recent libksba, we should check that the + # API is compatible. + if test "$req_ksba_api" -gt 0 ; then + tmp=`$KSBA_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking KSBA API version" >&5 +$as_echo_n "checking KSBA API version... " >&6; } + if test "$req_ksba_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_ksba_api got=$tmp." >&5 +$as_echo "does not match. want=$req_ksba_api got=$tmp." >&6; } + fi + fi + fi + fi + if test $ok = yes; then + KSBA_CFLAGS=`$KSBA_CONFIG $ksba_config_args --cflags` + KSBA_LIBS=`$KSBA_CONFIG $ksba_config_args --libs` + have_ksba=yes + libksba_config_host=`$LIBKSBA_CONFIG $ksba_config_args --host 2>/dev/null || echo none` + if test x"$libksba_config_host" != xnone ; then + if test x"$libksba_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $LIBKSBA_CONFIG was +*** built for $libksba_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libksba-prefix +*** to specify a matching config script. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $LIBKSBA_CONFIG was +*** built for $libksba_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libksba-prefix +*** to specify a matching config script. +***" >&2;} + fi + fi + else + KSBA_CFLAGS="" + KSBA_LIBS="" + have_ksba=no + fi + + + + + +# +# libusb allows us to use the integrated CCID smartcard reader driver. +# +# FiXME: Use GNUPG_CHECK_LIBUSB and modify to use separate AC_SUBSTs. +if test "$use_ccid_driver" = auto || test "$use_ccid_driver" = yes; then + case "${host}" in + *-mingw32*) + LIBUSB_NAME= + LIBUSB_LIBS= + LIBUSB_CPPFLAGS= + ;; + *-*-darwin*) + LIBUSB_NAME=usb-1.0 + LIBUSB_LIBS="-Wl,-framework,CoreFoundation -Wl,-framework,IOKit" + ;; + *-*-freebsd*) + # FreeBSD has a native 1.0 compatible library by -lusb. + LIBUSB_NAME=usb + LIBUSB_LIBS= + ;; + *) + LIBUSB_NAME=usb-1.0 + LIBUSB_LIBS= + ;; + esac +fi +if test x"$LIBUSB_NAME" != x ; then + as_ac_Lib=`$as_echo "ac_cv_lib_$LIBUSB_NAME''_libusb_init" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libusb_init in -l$LIBUSB_NAME" >&5 +$as_echo_n "checking for libusb_init in -l$LIBUSB_NAME... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$LIBUSB_NAME $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char libusb_init (); +int +main () +{ +return libusb_init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + LIBUSB_LIBS="-l$LIBUSB_NAME $LIBUSB_LIBS" + have_libusb=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking libusb include dir" >&5 +$as_echo_n "checking libusb include dir... " >&6; } + usb_incdir_found="no" + for _incdir in "" "/usr/include/libusb-1.0" "/usr/local/include/libusb-1.0"; do + _libusb_save_cppflags=$CPPFLAGS + if test -n "${_incdir}"; then + CPPFLAGS="-I${_incdir} ${CPPFLAGS}" + fi + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + usb_incdir=${_incdir}; usb_incdir_found="yes" +fi +rm -f conftest.err conftest.i conftest.$ac_ext + CPPFLAGS=${_libusb_save_cppflags} + if test "$usb_incdir_found" = "yes"; then + break + fi + done + if test "$usb_incdir_found" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${usb_incdir}" >&5 +$as_echo "${usb_incdir}" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + usb_incdir="" + have_libusb=no + if test "$use_ccid_driver" != yes; then + use_ccid_driver=no + fi + LIBUSB_LIBS="" + fi + + if test "$have_libusb" = yes; then + +$as_echo "#define HAVE_LIBUSB 1" >>confdefs.h + + fi + if test x"$usb_incdir" = x; then + LIBUSB_CPPFLAGS="" + else + LIBUSB_CPPFLAGS="-I${usb_incdir}" + fi +fi + + + +# +# Check wether it is necessary to link against libdl. +# (For example to load libpcsclite) +# +gnupg_dlopen_save_libs="$LIBS" +LIBS="" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 +$as_echo_n "checking for library containing dlopen... " >&6; } +if ${ac_cv_search_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +for ac_lib in '' c dl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dlopen=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_dlopen+:} false; then : + break +fi +done +if ${ac_cv_search_dlopen+:} false; then : + +else + ac_cv_search_dlopen=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 +$as_echo "$ac_cv_search_dlopen" >&6; } +ac_res=$ac_cv_search_dlopen +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +DL_LIBS=$LIBS + +LIBS="$gnupg_dlopen_save_libs" + + +# Checks for g10 + +# Check whether --enable-sqlite was given. +if test "${enable_sqlite+set}" = set; then : + enableval=$enable_sqlite; try_sqlite=$enableval +else + try_sqlite=yes +fi + + +if test x"$use_tofu" = xyes ; then + if test x"$try_sqlite" = xyes ; then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLITE3" >&5 +$as_echo_n "checking for SQLITE3... " >&6; } + +if test -n "$SQLITE3_CFLAGS"; then + pkg_cv_SQLITE3_CFLAGS="$SQLITE3_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sqlite3 >= \$NEED_SQLITE_VERSION\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sqlite3 >= $NEED_SQLITE_VERSION") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SQLITE3_CFLAGS=`$PKG_CONFIG --cflags "sqlite3 >= $NEED_SQLITE_VERSION" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$SQLITE3_LIBS"; then + pkg_cv_SQLITE3_LIBS="$SQLITE3_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sqlite3 >= \$NEED_SQLITE_VERSION\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sqlite3 >= $NEED_SQLITE_VERSION") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SQLITE3_LIBS=`$PKG_CONFIG --libs "sqlite3 >= $NEED_SQLITE_VERSION" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + SQLITE3_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sqlite3 >= $NEED_SQLITE_VERSION" 2>&1` + else + SQLITE3_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sqlite3 >= $NEED_SQLITE_VERSION" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$SQLITE3_PKG_ERRORS" >&5 + + have_sqlite=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_sqlite=no +else + SQLITE3_CFLAGS=$pkg_cv_SQLITE3_CFLAGS + SQLITE3_LIBS=$pkg_cv_SQLITE3_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_sqlite=yes +fi + fi + if test "$have_sqlite" = "yes"; then + : + + + else + use_tofu=no + tmp=$(echo "$SQLITE3_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g') + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** Building without SQLite support - TOFU disabled +*** +*** $tmp" >&5 +$as_echo "$as_me: WARNING: +*** +*** Building without SQLite support - TOFU disabled +*** +*** $tmp" >&2;} + fi +fi + + if test "$have_sqlite" = "yes"; then + SQLITE3_TRUE= + SQLITE3_FALSE='#' +else + SQLITE3_TRUE='#' + SQLITE3_FALSE= +fi + + +if test x"$use_tofu" = xyes ; then + +$as_echo "#define USE_TOFU 1" >>confdefs.h + +fi + + +# Checks for g13 + +# Extract the first word of "encfs", so it can be a program name with args. +set dummy encfs; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ENCFS+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ENCFS in + [\\/]* | ?:[\\/]*) + ac_cv_path_ENCFS="$ENCFS" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ENCFS="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_ENCFS" && ac_cv_path_ENCFS="/usr/bin/encfs" + ;; +esac +fi +ENCFS=$ac_cv_path_ENCFS +if test -n "$ENCFS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENCFS" >&5 +$as_echo "$ENCFS" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +cat >>confdefs.h <<_ACEOF +#define ENCFS "${ENCFS}" +_ACEOF + + +# Extract the first word of "fusermount", so it can be a program name with args. +set dummy fusermount; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_FUSERMOUNT+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $FUSERMOUNT in + [\\/]* | ?:[\\/]*) + ac_cv_path_FUSERMOUNT="$FUSERMOUNT" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_FUSERMOUNT="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_FUSERMOUNT" && ac_cv_path_FUSERMOUNT="/usr/bin/fusermount" + ;; +esac +fi +FUSERMOUNT=$ac_cv_path_FUSERMOUNT +if test -n "$FUSERMOUNT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FUSERMOUNT" >&5 +$as_echo "$FUSERMOUNT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +cat >>confdefs.h <<_ACEOF +#define FUSERMOUNT "${FUSERMOUNT}" +_ACEOF + + + +# Checks for dirmngr + + +# +# Checks for symcryptrun: +# + +# libutil has openpty() and login_tty(). +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for openpty in -lutil" >&5 +$as_echo_n "checking for openpty in -lutil... " >&6; } +if ${ac_cv_lib_util_openpty+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lutil $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char openpty (); +int +main () +{ +return openpty (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_util_openpty=yes +else + ac_cv_lib_util_openpty=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_openpty" >&5 +$as_echo "$ac_cv_lib_util_openpty" >&6; } +if test "x$ac_cv_lib_util_openpty" = xyes; then : + LIBUTIL_LIBS="$LIBUTIL_LIBS -lutil" + +$as_echo "#define HAVE_LIBUTIL 1" >>confdefs.h + + +fi + + + +# shred is used to clean temporary plain text files. +# Extract the first word of "shred", so it can be a program name with args. +set dummy shred; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_SHRED+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $SHRED in + [\\/]* | ?:[\\/]*) + ac_cv_path_SHRED="$SHRED" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SHRED="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_SHRED" && ac_cv_path_SHRED="/usr/bin/shred" + ;; +esac +fi +SHRED=$ac_cv_path_SHRED +if test -n "$SHRED"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SHRED" >&5 +$as_echo "$SHRED" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +cat >>confdefs.h <<_ACEOF +#define SHRED "${SHRED}" +_ACEOF + + + +# +# Check whether the nPth library is available +# + +# Check whether --with-npth-prefix was given. +if test "${with_npth_prefix+set}" = set; then : + withval=$with_npth_prefix; npth_config_prefix="$withval" +else + npth_config_prefix="" +fi + + if test "x$npth_config_prefix" != x ; then + NPTH_CONFIG="$npth_config_prefix/bin/npth-config" + fi + # Extract the first word of "npth-config", so it can be a program name with args. +set dummy npth-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_NPTH_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $NPTH_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_NPTH_CONFIG="$NPTH_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_NPTH_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_NPTH_CONFIG" && ac_cv_path_NPTH_CONFIG="no" + ;; +esac +fi +NPTH_CONFIG=$ac_cv_path_NPTH_CONFIG +if test -n "$NPTH_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPTH_CONFIG" >&5 +$as_echo "$NPTH_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + if test "$NPTH_CONFIG" != "no" ; then + npth_version=`$NPTH_CONFIG --version` + fi + npth_version_major=`echo $npth_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\1/'` + npth_version_minor=`echo $npth_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\).*/\2/'` + + tmp="$NEED_NPTH_API:$NEED_NPTH_VERSION" + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_npth_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_npth_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_npth_api=1 + min_npth_version="$tmp" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NPTH - version >= $min_npth_version" >&5 +$as_echo_n "checking for NPTH - version >= $min_npth_version... " >&6; } + ok=no + if test "$NPTH_CONFIG" != "no" ; then + req_major=`echo $min_npth_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_npth_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)/\2/'` + if test "$npth_version_major" -gt "$req_major"; then + ok=yes + else + if test "$npth_version_major" -eq "$req_major"; then + if test "$npth_version_minor" -gt "$req_minor"; then + ok=yes + else + if test "$npth_version_minor" -eq "$req_minor"; then + ok=yes + fi + fi + fi + fi + fi + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($npth_version)" >&5 +$as_echo "yes ($npth_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + if test $ok = yes; then + # If we have a recent NPTH, we should also check that the + # API is compatible. + if test "$req_npth_api" -gt 0 ; then + tmp=`$NPTH_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking NPTH API version" >&5 +$as_echo_n "checking NPTH API version... " >&6; } + if test "$req_npth_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_npth_api got=$tmp" >&5 +$as_echo "does not match. want=$req_npth_api got=$tmp" >&6; } + fi + fi + fi + fi + if test $ok = yes; then + NPTH_CFLAGS=`$NPTH_CONFIG --cflags` + NPTH_LIBS=`$NPTH_CONFIG --libs` + have_npth=yes + npth_config_host=`$NPTH_CONFIG --host 2>/dev/null || echo none` + if test x"$npth_config_host" != xnone ; then + if test x"$npth_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $NPTH_CONFIG was +*** built for $npth_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-npth-prefix +*** to specify a matching config script. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $NPTH_CONFIG was +*** built for $npth_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-npth-prefix +*** to specify a matching config script. +***" >&2;} + fi + fi + else + NPTH_CFLAGS="" + NPTH_LIBS="" + have_npth=no + fi + + + +if test "$have_npth" = "yes"; then + +$as_echo "#define HAVE_NPTH 1" >>confdefs.h + + +$as_echo "#define USE_NPTH 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** To support concurrent access for example in gpg-agent and the SCdaemon +*** we need the support of the New Portable Threads Library. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** To support concurrent access for example in gpg-agent and the SCdaemon +*** we need the support of the New Portable Threads Library. +***" >&2;} +fi + + +# +# NTBTLS is our TLS library. If it is not available fallback to +# GNUTLS. +# +# Check whether --enable-ntbtls was given. +if test "${enable_ntbtls+set}" = set; then : + enableval=$enable_ntbtls; try_ntbtls=$enableval +else + try_ntbtls=yes +fi + +if test x"$try_ntbtls" = xyes ; then + + +# Check whether --with-ntbtls-prefix was given. +if test "${with_ntbtls_prefix+set}" = set; then : + withval=$with_ntbtls_prefix; ntbtls_config_prefix="$withval" +else + ntbtls_config_prefix="" +fi + + if test x"${NTBTLS_CONFIG}" = x ; then + if test x"${ntbtls_config_prefix}" != x ; then + NTBTLS_CONFIG="${ntbtls_config_prefix}/bin/ntbtls-config" + else + case "${SYSROOT}" in + /*) + if test -x "${SYSROOT}/bin/ntbtls-config" ; then + NTBTLS_CONFIG="${SYSROOT}/bin/ntbtls-config" + fi + ;; + '') + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ignoring \$SYSROOT as it is not an absolute path." >&5 +$as_echo "$as_me: WARNING: Ignoring \$SYSROOT as it is not an absolute path." >&2;} + ;; + esac + fi + fi + + # Extract the first word of "ntbtls-config", so it can be a program name with args. +set dummy ntbtls-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_NTBTLS_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $NTBTLS_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_NTBTLS_CONFIG="$NTBTLS_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_NTBTLS_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_NTBTLS_CONFIG" && ac_cv_path_NTBTLS_CONFIG="no" + ;; +esac +fi +NTBTLS_CONFIG=$ac_cv_path_NTBTLS_CONFIG +if test -n "$NTBTLS_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NTBTLS_CONFIG" >&5 +$as_echo "$NTBTLS_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + tmp="$NEED_NTBTLS_API:$NEED_NTBTLS_VERSION" + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_ntbtls_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_ntbtls_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_ntbtls_api=0 + min_ntbtls_version="$tmp" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NTBTLS - version >= $min_ntbtls_version" >&5 +$as_echo_n "checking for NTBTLS - version >= $min_ntbtls_version... " >&6; } + ok=no + if test "$NTBTLS_CONFIG" != "no" ; then + req_major=`echo $min_ntbtls_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_ntbtls_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + req_micro=`echo $min_ntbtls_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + ntbtls_config_version=`$NTBTLS_CONFIG --version` + major=`echo $ntbtls_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1/'` + minor=`echo $ntbtls_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\2/'` + micro=`echo $ntbtls_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\3/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -gt "$req_minor"; then + ok=yes + else + if test "$minor" -eq "$req_minor"; then + if test "$micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + fi + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($ntbtls_config_version)" >&5 +$as_echo "yes ($ntbtls_config_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + if test $ok = yes; then + # If we have a recent ntbtls, we should also check that the + # API is compatible + if test "$req_ntbtls_api" -gt 0 ; then + tmp=`$NTBTLS_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking NTBTLS API version" >&5 +$as_echo_n "checking NTBTLS API version... " >&6; } + if test "$req_ntbtls_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_ntbtls_api got=$tmp" >&5 +$as_echo "does not match. want=$req_ntbtls_api got=$tmp" >&6; } + fi + fi + fi + fi + if test $ok = yes; then + NTBTLS_CFLAGS=`$NTBTLS_CONFIG --cflags` + NTBTLS_LIBS=`$NTBTLS_CONFIG --libs` + have_ntbtls=yes + ntbtls_config_host=`$NTBTLS_CONFIG --host 2>/dev/null || echo none` + if test x"$ntbtls_config_host" != xnone ; then + if test x"$ntbtls_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $NTBTLS_CONFIG was +*** built for $ntbtls_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-ntbtls-prefix +*** to specify a matching config script or use \$SYSROOT. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $NTBTLS_CONFIG was +*** built for $ntbtls_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-ntbtls-prefix +*** to specify a matching config script or use \$SYSROOT. +***" >&2;} + gpg_config_script_warn="$gpg_config_script_warn ntbtls" + fi + fi + else + NTBTLS_CFLAGS="" + NTBTLS_LIBS="" + have_ntbtls=no + fi + + + +fi +if test "$have_ntbtls" = yes ; then + use_tls_library=ntbtls + +$as_echo "#define HTTP_USE_NTBTLS 1" >>confdefs.h + +else + # Check whether --enable-gnutls was given. +if test "${enable_gnutls+set}" = set; then : + enableval=$enable_gnutls; try_gnutls=$enableval +else + try_gnutls=yes +fi + + if test x"$try_gnutls" = xyes ; then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBGNUTLS" >&5 +$as_echo_n "checking for LIBGNUTLS... " >&6; } + +if test -n "$LIBGNUTLS_CFLAGS"; then + pkg_cv_LIBGNUTLS_CFLAGS="$LIBGNUTLS_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gnutls >= \$NEED_GNUTLS_VERSION\""; } >&5 + ($PKG_CONFIG --exists --print-errors "gnutls >= $NEED_GNUTLS_VERSION") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBGNUTLS_CFLAGS=`$PKG_CONFIG --cflags "gnutls >= $NEED_GNUTLS_VERSION" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBGNUTLS_LIBS"; then + pkg_cv_LIBGNUTLS_LIBS="$LIBGNUTLS_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gnutls >= \$NEED_GNUTLS_VERSION\""; } >&5 + ($PKG_CONFIG --exists --print-errors "gnutls >= $NEED_GNUTLS_VERSION") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBGNUTLS_LIBS=`$PKG_CONFIG --libs "gnutls >= $NEED_GNUTLS_VERSION" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBGNUTLS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gnutls >= $NEED_GNUTLS_VERSION" 2>&1` + else + LIBGNUTLS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gnutls >= $NEED_GNUTLS_VERSION" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBGNUTLS_PKG_ERRORS" >&5 + + have_gnutls=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_gnutls=no +else + LIBGNUTLS_CFLAGS=$pkg_cv_LIBGNUTLS_CFLAGS + LIBGNUTLS_LIBS=$pkg_cv_LIBGNUTLS_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_gnutls=yes +fi + fi + if test "$have_gnutls" = "yes"; then + + + use_tls_library=gnutls + +$as_echo "#define HTTP_USE_GNUTLS 1" >>confdefs.h + + else + tmp=$(echo "$LIBGNUTLS_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g') + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** Building without NTBTLS and GNUTLS - no TLS access to keyservers. +*** +*** $tmp" >&5 +$as_echo "$as_me: WARNING: +*** +*** Building without NTBTLS and GNUTLS - no TLS access to keyservers. +*** +*** $tmp" >&2;} + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for networking options" >&5 +$as_echo "$as_me: checking for networking options" >&6;} + +# +# Must check for network library requirements before doing link tests +# for ldap, for example. If ldap libs are static (or dynamic and without +# ELF runtime link paths), then link will fail and LDAP support won't +# be detected. +# +ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" +if test "x$ac_cv_func_gethostbyname" = xyes; then : + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 +$as_echo_n "checking for gethostbyname in -lnsl... " >&6; } +if ${ac_cv_lib_nsl_gethostbyname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (); +int +main () +{ +return gethostbyname (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_nsl_gethostbyname=yes +else + ac_cv_lib_nsl_gethostbyname=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 +$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; } +if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then : + NETLIBS="-lnsl $NETLIBS" +fi + +fi + +ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt" +if test "x$ac_cv_func_setsockopt" = xyes; then : + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5 +$as_echo_n "checking for setsockopt in -lsocket... " >&6; } +if ${ac_cv_lib_socket_setsockopt+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char setsockopt (); +int +main () +{ +return setsockopt (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_socket_setsockopt=yes +else + ac_cv_lib_socket_setsockopt=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5 +$as_echo "$ac_cv_lib_socket_setsockopt" >&6; } +if test "x$ac_cv_lib_socket_setsockopt" = xyes; then : + NETLIBS="-lsocket $NETLIBS" +fi + +fi + + + +# +# Check standard resolver functions. +# +if test "$build_dirmngr" = "yes"; then + _dns_save_libs=$LIBS + LIBS="" + + # Find the system resolver which can always be enabled with + # the dirmngr option --standard-resolver. + + # the double underscore thing is a glibc-ism? + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing res_query" >&5 +$as_echo_n "checking for library containing res_query... " >&6; } +if ${ac_cv_search_res_query+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char res_query (); +int +main () +{ +return res_query (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_res_query=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_res_query+:} false; then : + break +fi +done +if ${ac_cv_search_res_query+:} false; then : + +else + ac_cv_search_res_query=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_res_query" >&5 +$as_echo "$ac_cv_search_res_query" >&6; } +ac_res=$ac_cv_search_res_query +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing __res_query" >&5 +$as_echo_n "checking for library containing __res_query... " >&6; } +if ${ac_cv_search___res_query+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char __res_query (); +int +main () +{ +return __res_query (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search___res_query=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search___res_query+:} false; then : + break +fi +done +if ${ac_cv_search___res_query+:} false; then : + +else + ac_cv_search___res_query=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search___res_query" >&5 +$as_echo "$ac_cv_search___res_query" >&6; } +ac_res=$ac_cv_search___res_query +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + have_resolver=no +fi + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dn_expand" >&5 +$as_echo_n "checking for library containing dn_expand... " >&6; } +if ${ac_cv_search_dn_expand+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dn_expand (); +int +main () +{ +return dn_expand (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dn_expand=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_dn_expand+:} false; then : + break +fi +done +if ${ac_cv_search_dn_expand+:} false; then : + +else + ac_cv_search_dn_expand=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dn_expand" >&5 +$as_echo "$ac_cv_search_dn_expand" >&6; } +ac_res=$ac_cv_search_dn_expand +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing __dn_expand" >&5 +$as_echo_n "checking for library containing __dn_expand... " >&6; } +if ${ac_cv_search___dn_expand+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char __dn_expand (); +int +main () +{ +return __dn_expand (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search___dn_expand=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search___dn_expand+:} false; then : + break +fi +done +if ${ac_cv_search___dn_expand+:} false; then : + +else + ac_cv_search___dn_expand=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search___dn_expand" >&5 +$as_echo "$ac_cv_search___dn_expand" >&6; } +ac_res=$ac_cv_search___dn_expand +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + have_resolver=no +fi + +fi + + + # macOS renames dn_skipname into res_9_dn_skipname in , + # and for some reason fools us into believing we don't need + # -lresolv even if we do. Since the test program checking for the + # symbol does not include , we need to check for the + # renamed symbol explicitly. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing res_9_dn_skipname" >&5 +$as_echo_n "checking for library containing res_9_dn_skipname... " >&6; } +if ${ac_cv_search_res_9_dn_skipname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char res_9_dn_skipname (); +int +main () +{ +return res_9_dn_skipname (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_res_9_dn_skipname=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_res_9_dn_skipname+:} false; then : + break +fi +done +if ${ac_cv_search_res_9_dn_skipname+:} false; then : + +else + ac_cv_search_res_9_dn_skipname=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_res_9_dn_skipname" >&5 +$as_echo "$ac_cv_search_res_9_dn_skipname" >&6; } +ac_res=$ac_cv_search_res_9_dn_skipname +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dn_skipname" >&5 +$as_echo_n "checking for library containing dn_skipname... " >&6; } +if ${ac_cv_search_dn_skipname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dn_skipname (); +int +main () +{ +return dn_skipname (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dn_skipname=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_dn_skipname+:} false; then : + break +fi +done +if ${ac_cv_search_dn_skipname+:} false; then : + +else + ac_cv_search_dn_skipname=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dn_skipname" >&5 +$as_echo "$ac_cv_search_dn_skipname" >&6; } +ac_res=$ac_cv_search_dn_skipname +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing __dn_skipname" >&5 +$as_echo_n "checking for library containing __dn_skipname... " >&6; } +if ${ac_cv_search___dn_skipname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char __dn_skipname (); +int +main () +{ +return __dn_skipname (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search___dn_skipname=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search___dn_skipname+:} false; then : + break +fi +done +if ${ac_cv_search___dn_skipname+:} false; then : + +else + ac_cv_search___dn_skipname=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search___dn_skipname" >&5 +$as_echo "$ac_cv_search___dn_skipname" >&6; } +ac_res=$ac_cv_search___dn_skipname +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + have_resolver=no +fi + +fi + +fi + + + if test x"$have_resolver" != xno ; then + + # Make sure that the BIND 4 resolver interface is workable before + # enabling any code that calls it. At some point I'll rewrite the + # code to use the BIND 8 resolver API. + # We might also want to use libdns instead. + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the resolver is usable" >&5 +$as_echo_n "checking whether the resolver is usable... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +int +main () +{ +unsigned char answer[PACKETSZ]; + res_query("foo.bar",C_IN,T_A,answer,PACKETSZ); + dn_skipname(0,0); + dn_expand(0,0,0,0,0); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_resolver=yes +else + have_resolver=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_resolver" >&5 +$as_echo "$have_resolver" >&6; } + + # This is Apple-specific and somewhat bizarre as they changed the + # define in bind 8 for some reason. + + if test x"$have_resolver" != xyes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether I can make the resolver usable with BIND_8_COMPAT" >&5 +$as_echo_n "checking whether I can make the resolver usable with BIND_8_COMPAT... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define BIND_8_COMPAT +#include +#include +#include +#include +int +main () +{ +unsigned char answer[PACKETSZ]; + res_query("foo.bar",C_IN,T_A,answer,PACKETSZ); + dn_skipname(0,0); dn_expand(0,0,0,0,0); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_resolver=yes ; need_compat=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_resolver" >&5 +$as_echo "$have_resolver" >&6; } + fi + fi + + if test x"$have_resolver" = xyes ; then + +$as_echo "#define HAVE_SYSTEM_RESOLVER 1" >>confdefs.h + + DNSLIBS="$DNSLIBS $LIBS" + if test x"$need_compat" = xyes ; then + +$as_echo "#define BIND_8_COMPAT 1" >>confdefs.h + + fi + if test "$use_libdns" = yes; then + show_tor_support=yes + fi + elif test "$use_libdns" = yes; then + show_tor_support=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The system's DNS resolver is not usable. +*** Dirmngr functionality is limited. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The system's DNS resolver is not usable. +*** Dirmngr functionality is limited. +***" >&2;} + show_tor_support="${show_tor_support} (no system resolver)" + fi + + if test "$have_w32_system" = yes; then + if test "$use_libdns" = yes; then + DNSLIBS="$DNSLIBS -liphlpapi" + fi + fi + + LIBS=$_dns_save_libs +fi + + + + +# +# Check for LDAP +# +# Note that running the check changes the variable +# gnupg_have_ldap from "n/a" to "no" or "yes". + +# Check whether --enable-ldap was given. +if test "${enable_ldap+set}" = set; then : + enableval=$enable_ldap; if test "$enableval" = "no"; then gnupg_have_ldap=no; fi +fi + + +if test "$gnupg_have_ldap" != "no" ; then + if test "$build_dirmngr" = "yes" ; then + +# Try and link a LDAP test program to weed out unusable LDAP +# libraries. -lldap [-llber [-lresolv]] is for older OpenLDAPs. +# OpenLDAP, circa 1999, was terrible with creating weird dependencies. +# If all else fails, the user can play guess-the-dependency by using +# something like ./configure LDAPLIBS="-Lfoo -lbar" +gnupg_have_ldap=no + +# Check whether --with-ldap was given. +if test "${with_ldap+set}" = set; then : + withval=$with_ldap; _ldap_with=$withval +fi + + +if test x$_ldap_with != xno ; then + + if test -d "$withval" ; then + LDAP_CPPFLAGS="-I$withval/include" + LDAP_LDFLAGS="-L$withval/lib" + fi + + _ldap_save_cppflags=$CPPFLAGS + CPPFLAGS="${LDAP_CPPFLAGS} ${CPPFLAGS}" + _ldap_save_ldflags=$LDFLAGS + LDFLAGS="${LDAP_LDFLAGS} ${LDFLAGS}" + + for MY_LDAPLIBS in ${LDAPLIBS+"$LDAPLIBS"} "-lldap" "-lldap -llber" "-lldap -llber -lresolv" "-lwldap32"; do + _ldap_save_libs=$LIBS + LIBS="$MY_LDAPLIBS $NETLIBS $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether LDAP via \"$MY_LDAPLIBS\" is present and sane" >&5 +$as_echo_n "checking whether LDAP via \"$MY_LDAPLIBS\" is present and sane... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +int +main () +{ +ldap_open("foobar",1234); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gnupg_cv_func_ldap_init=yes +else + gnupg_cv_func_ldap_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_func_ldap_init" >&5 +$as_echo "$gnupg_cv_func_ldap_init" >&6; } + + if test $gnupg_cv_func_ldap_init = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether I can make LDAP be sane with lber.h" >&5 +$as_echo_n "checking whether I can make LDAP be sane with lber.h... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +ldap_open("foobar",1234); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gnupg_cv_func_ldaplber_init=yes +else + gnupg_cv_func_ldaplber_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_func_ldaplber_init" >&5 +$as_echo "$gnupg_cv_func_ldaplber_init" >&6; } + fi + + if test "$gnupg_cv_func_ldaplber_init" = yes ; then + +$as_echo "#define NEED_LBER_H 1" >>confdefs.h + + fi + + if test "$gnupg_cv_func_ldap_init" = yes || \ + test "$gnupg_cv_func_ldaplber_init" = yes ; then + LDAPLIBS="$LDAP_LDFLAGS $MY_LDAPLIBS" + GPGKEYS_LDAP="gpg2keys_ldap$EXEEXT" + gnupg_have_ldap=yes + + for ac_func in ldap_get_option ldap_set_option +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + # The extra test for ldap_start_tls_sA is for W32 because + # that is the actual function in the library. + for ac_func in ldap_start_tls_s ldap_start_tls_sA +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + if test "$ac_cv_func_ldap_get_option" != yes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether LDAP supports ld_errno" >&5 +$as_echo_n "checking whether LDAP supports ld_errno... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +LDAP *ldap; ldap->ld_errno; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gnupg_cv_func_ldap_ld_errno=yes +else + gnupg_cv_func_ldap_ld_errno=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_func_ldap_ld_errno" >&5 +$as_echo "$gnupg_cv_func_ldap_ld_errno" >&6; } + + if test "$gnupg_cv_func_ldap_ld_errno" = yes ; then + +$as_echo "#define HAVE_LDAP_LD_ERRNO 1" >>confdefs.h + + fi + fi + fi + + LIBS=$_ldap_save_libs + + if test "$GPGKEYS_LDAP" != "" ; then break; fi + done + + + + + + CPPFLAGS=$_ldap_save_cppflags + LDFLAGS=$_ldap_save_ldflags +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_free in -llber" >&5 +$as_echo_n "checking for ber_free in -llber... " >&6; } +if ${ac_cv_lib_lber_ber_free+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-llber $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ber_free (); +int +main () +{ +return ber_free (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_lber_ber_free=yes +else + ac_cv_lib_lber_ber_free=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_free" >&5 +$as_echo "$ac_cv_lib_lber_ber_free" >&6; } +if test "x$ac_cv_lib_lber_ber_free" = xyes; then : + LBER_LIBS="$LBER_LIBS -llber" + +$as_echo "#define HAVE_LBER 1" >>confdefs.h + + have_lber=yes + +fi + + fi +fi + +if test "$gnupg_have_ldap" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** Building without LDAP support. +*** No CRL access or X.509 certificate search available. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** Building without LDAP support. +*** No CRL access or X.509 certificate search available. +***" >&2;} +fi + + if test "$gnupg_have_ldap" = yes; then + USE_LDAP_TRUE= + USE_LDAP_FALSE='#' +else + USE_LDAP_TRUE='#' + USE_LDAP_FALSE= +fi + +if test "$gnupg_have_ldap" = yes ; then + +$as_echo "#define USE_LDAP 1" >>confdefs.h + +else + use_ldapwrapper=no +fi + +if test "$use_ldapwrapper" = yes; then + +$as_echo "#define USE_LDAPWRAPPER 1" >>confdefs.h + +fi + if test "$use_ldapwrapper" = yes; then + USE_LDAPWRAPPER_TRUE= + USE_LDAPWRAPPER_FALSE='#' +else + USE_LDAPWRAPPER_TRUE='#' + USE_LDAPWRAPPER_FALSE= +fi + + + + + +# +# Check for sendmail +# +# This isn't necessarily sendmail itself, but anything that gives a +# sendmail-ish interface to the outside world. That includes Exim, +# Postfix, etc. Basically, anything that can handle "sendmail -t". + +# Check whether --with-mailprog was given. +if test "${with_mailprog+set}" = set; then : + withval=$with_mailprog; +else + with_mailprog=yes +fi + +if test x"$with_mailprog" = xyes ; then + # Extract the first word of "sendmail", so it can be a program name with args. +set dummy sendmail; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_SENDMAIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $SENDMAIL in + [\\/]* | ?:[\\/]*) + ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/sbin:/usr/libexec:/usr/lib" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +SENDMAIL=$ac_cv_path_SENDMAIL +if test -n "$SENDMAIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5 +$as_echo "$SENDMAIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +elif test x"$with_mailprog" != xno ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a mail transport program" >&5 +$as_echo_n "checking for a mail transport program... " >&6; } + SENDMAIL=$with_mailprog + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_mailprog" >&5 +$as_echo "$with_mailprog" >&6; } +fi + + +# +# Construct a printable name of the OS +# +case "${host}" in + *-mingw32ce*) + PRINTABLE_OS_NAME="W32CE" + ;; + *-mingw32*) + PRINTABLE_OS_NAME="MingW32" + ;; + *-*-cygwin*) + PRINTABLE_OS_NAME="Cygwin" + ;; + i?86-emx-os2 | i?86-*-os2*emx ) + PRINTABLE_OS_NAME="OS/2" + ;; + i?86-*-msdosdjgpp*) + PRINTABLE_OS_NAME="MSDOS/DJGPP" + try_dynload=no + ;; + *-linux*) + PRINTABLE_OS_NAME="GNU/Linux" + ;; + *) + PRINTABLE_OS_NAME=`uname -s || echo "Unknown"` + ;; +esac + +cat >>confdefs.h <<_ACEOF +#define PRINTABLE_OS_NAME "$PRINTABLE_OS_NAME" +_ACEOF + + + +# +# Checking for iconv +# +if test "$require_iconv" = yes; then + + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by GCC" >&5 +$as_echo_n "checking for ld used by GCC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | [A-Za-z]:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${acl_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break ;; + *) + test "$with_gnu_ld" != yes && break ;; + esac + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$acl_cv_path_LD" +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${acl_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$acl_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$acl_cv_prog_gnu_ld + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5 +$as_echo_n "checking for shared library run path origin... " >&6; } +if ${acl_cv_rpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5 +$as_echo "$acl_cv_rpath" >&6; } + wl="$acl_cv_wl" + acl_libext="$acl_cv_libext" + acl_shlibext="$acl_cv_shlibext" + acl_libname_spec="$acl_cv_libname_spec" + acl_library_names_spec="$acl_cv_library_names_spec" + acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + acl_hardcode_direct="$acl_cv_hardcode_direct" + acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" + # Check whether --enable-rpath was given. +if test "${enable_rpath+set}" = set; then : + enableval=$enable_rpath; : +else + enable_rpath=yes +fi + + + + acl_libdirstem=lib + searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` + if test -n "$searchpath"; then + acl_save_IFS="${IFS= }"; IFS=":" + for searchdir in $searchpath; do + if test -d "$searchdir"; then + case "$searchdir" in + */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; + *) searchdir=`cd "$searchdir" && pwd` + case "$searchdir" in + */lib64 ) acl_libdirstem=lib64 ;; + esac ;; + esac + fi + done + IFS="$acl_save_IFS" + fi + + + + + + + + + + use_additional=yes + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + +# Check whether --with-libiconv-prefix was given. +if test "${with_libiconv_prefix+set}" = set; then : + withval=$with_libiconv_prefix; + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi + +fi + + LIBICONV= + LTLIBICONV= + INCICONV= + LIBICONV_PREFIX= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='iconv ' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value" + else + : + fi + else + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + else + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + if test "$acl_hardcode_direct" = yes; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + haveit= + for x in $LDFLAGS $LIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so" + else + LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a" + else + LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name" + fi + fi + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + LIBICONV_PREFIX="$basedir" + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INCICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + if test -n "$found_la"; then + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBICONV; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + LIBICONV="${LIBICONV}${LIBICONV:+ }$dep" + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep" + ;; + esac + done + fi + else + LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name" + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" + else + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBICONV="${LIBICONV}${LIBICONV:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir" + done + fi + + + + + + + + am_save_CPPFLAGS="$CPPFLAGS" + + for element in $INCICONV; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5 +$as_echo_n "checking for iconv... " >&6; } +if ${am_cv_func_iconv+:} false; then : + $as_echo_n "(cached) " >&6 +else + + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + am_cv_func_iconv=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + am_cv_lib_iconv=yes + am_cv_func_iconv=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$am_save_LIBS" + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5 +$as_echo "$am_cv_func_iconv" >&6; } + if test "$am_cv_func_iconv" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working iconv" >&5 +$as_echo_n "checking for working iconv... " >&6; } +if ${am_cv_func_iconv_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + + am_save_LIBS="$LIBS" + if test $am_cv_lib_iconv = yes; then + LIBS="$LIBS $LIBICONV" + fi + if test "$cross_compiling" = yes; then : + case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int main () +{ + /* Test against AIX 5.1 bug: Failures are not distinguishable from successful + returns. */ + { + iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); + if (cd_utf8_to_88591 != (iconv_t)(-1)) + { + static const char input[] = "\342\202\254"; /* EURO SIGN */ + char buf[10]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_utf8_to_88591, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + return 1; + } + } +#if 0 /* This bug could be worked around by the caller. */ + /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + char buf[50]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_88591_to_utf8, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if ((int)res > 0) + return 1; + } + } +#endif + /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is + provided. */ + if (/* Try standardized names. */ + iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) + /* Try IRIX, OSF/1 names. */ + && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) + /* Try AIX names. */ + && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) + /* Try HP-UX names. */ + && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) + return 1; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + am_cv_func_iconv_works=yes +else + am_cv_func_iconv_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + LIBS="$am_save_LIBS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv_works" >&5 +$as_echo "$am_cv_func_iconv_works" >&6; } + case "$am_cv_func_iconv_works" in + *no) am_func_iconv=no am_cv_lib_iconv=no ;; + *) am_func_iconv=yes ;; + esac + else + am_func_iconv=no am_cv_lib_iconv=no + fi + if test "$am_func_iconv" = yes; then + +$as_echo "#define HAVE_ICONV 1" >>confdefs.h + + fi + if test "$am_cv_lib_iconv" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5 +$as_echo_n "checking how to link with libiconv... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5 +$as_echo "$LIBICONV" >&6; } + else + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + + + + if test "$am_cv_func_iconv" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv declaration" >&5 +$as_echo_n "checking for iconv declaration... " >&6; } + if ${am_cv_proto_iconv+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +extern +#ifdef __cplusplus +"C" +#endif +#if defined(__STDC__) || defined(__cplusplus) +size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); +#else +size_t iconv(); +#endif + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + am_cv_proto_iconv_arg1="" +else + am_cv_proto_iconv_arg1="const" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);" +fi + + am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${ac_t:- + }$am_cv_proto_iconv" >&5 +$as_echo "${ac_t:- + }$am_cv_proto_iconv" >&6; } + +cat >>confdefs.h <<_ACEOF +#define ICONV_CONST $am_cv_proto_iconv_arg1 +_ACEOF + + fi + +else + LIBICONV= + LTLIBICONV= + + +fi + + +# +# Check for gettext +# +# This is "GNU gnupg" - The project-id script from gettext +# needs this string +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gettext" >&5 +$as_echo "$as_me: checking for gettext" >&6;} + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NLS is requested" >&5 +$as_echo_n "checking whether NLS is requested... " >&6; } + # Check whether --enable-nls was given. +if test "${enable_nls+set}" = set; then : + enableval=$enable_nls; USE_NLS=$enableval +else + USE_NLS=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5 +$as_echo "$USE_NLS" >&6; } + + + + + GETTEXT_MACRO_VERSION=0.17 + + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_MSGFMT+:} false; then : + $as_echo_n "(cached) " >&6 +else + case "$MSGFMT" in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + echo "$as_me: trying $ac_dir/$ac_word..." >&5 + if $ac_dir/$ac_word --statistics /dev/null >&5 2>&1 && + (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":" + ;; +esac +fi +MSGFMT="$ac_cv_path_MSGFMT" +if test "$MSGFMT" != ":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5 +$as_echo "$MSGFMT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + # Extract the first word of "gmsgfmt", so it can be a program name with args. +set dummy gmsgfmt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_GMSGFMT+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $GMSGFMT in + [\\/]* | ?:[\\/]*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT" + ;; +esac +fi +GMSGFMT=$ac_cv_path_GMSGFMT +if test -n "$GMSGFMT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GMSGFMT" >&5 +$as_echo "$GMSGFMT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;; + *) MSGFMT_015=$MSGFMT ;; + esac + + case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;; + *) GMSGFMT_015=$GMSGFMT ;; + esac + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "xgettext", so it can be a program name with args. +set dummy xgettext; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_XGETTEXT+:} false; then : + $as_echo_n "(cached) " >&6 +else + case "$XGETTEXT" in + [\\/]* | ?:[\\/]*) + ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + echo "$as_me: trying $ac_dir/$ac_word..." >&5 + if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 && + (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":" + ;; +esac +fi +XGETTEXT="$ac_cv_path_XGETTEXT" +if test "$XGETTEXT" != ":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5 +$as_echo "$XGETTEXT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + rm -f messages.po + + case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;; + *) XGETTEXT_015=$XGETTEXT ;; + esac + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "msgmerge", so it can be a program name with args. +set dummy msgmerge; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_MSGMERGE+:} false; then : + $as_echo_n "(cached) " >&6 +else + case "$MSGMERGE" in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + echo "$as_me: trying $ac_dir/$ac_word..." >&5 + if $ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1; then + ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":" + ;; +esac +fi +MSGMERGE="$ac_cv_path_MSGMERGE" +if test "$MSGMERGE" != ":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGMERGE" >&5 +$as_echo "$MSGMERGE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$localedir" || localedir='${datadir}/locale' + + + test -n "${XGETTEXT_EXTRA_OPTIONS+set}" || XGETTEXT_EXTRA_OPTIONS= + + + ac_config_commands="$ac_config_commands po-directories" + + + +if test "$try_gettext" = yes; then + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFPreferencesCopyAppValue" >&5 +$as_echo_n "checking for CFPreferencesCopyAppValue... " >&6; } +if ${gt_cv_func_CFPreferencesCopyAppValue+:} false; then : + $as_echo_n "(cached) " >&6 +else + gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +CFPreferencesCopyAppValue(NULL, NULL) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gt_cv_func_CFPreferencesCopyAppValue=yes +else + gt_cv_func_CFPreferencesCopyAppValue=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$gt_save_LIBS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFPreferencesCopyAppValue" >&5 +$as_echo "$gt_cv_func_CFPreferencesCopyAppValue" >&6; } + if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then + +$as_echo "#define HAVE_CFPREFERENCESCOPYAPPVALUE 1" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFLocaleCopyCurrent" >&5 +$as_echo_n "checking for CFLocaleCopyCurrent... " >&6; } +if ${gt_cv_func_CFLocaleCopyCurrent+:} false; then : + $as_echo_n "(cached) " >&6 +else + gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +CFLocaleCopyCurrent(); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gt_cv_func_CFLocaleCopyCurrent=yes +else + gt_cv_func_CFLocaleCopyCurrent=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$gt_save_LIBS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFLocaleCopyCurrent" >&5 +$as_echo "$gt_cv_func_CFLocaleCopyCurrent" >&6; } + if test $gt_cv_func_CFLocaleCopyCurrent = yes; then + +$as_echo "#define HAVE_CFLOCALECOPYCURRENT 1" >>confdefs.h + + fi + INTL_MACOSX_LIBS= + if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then + INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" + fi + + + + + + + LIBINTL= + LTLIBINTL= + POSUB= + + case " $gt_needs " in + *" need-formatstring-macros "*) gt_api_version=3 ;; + *" need-ngettext "*) gt_api_version=2 ;; + *) gt_api_version=1 ;; + esac + gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc" + gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl" + + if test "$USE_NLS" = "yes"; then + gt_use_preinstalled_gnugettext=no + + + if test $gt_api_version -ge 3; then + gt_revision_test_code=' +#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) +#endif +typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; +' + else + gt_revision_test_code= + fi + if test $gt_api_version -ge 2; then + gt_expression_test_code=' + * ngettext ("", "", 0)' + else + gt_expression_test_code= + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libc" >&5 +$as_echo_n "checking for GNU gettext in libc... " >&6; } +if eval \${$gt_func_gnugettext_libc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +$gt_revision_test_code +extern int _nl_msg_cat_cntr; +extern int *_nl_domain_bindings; +int +main () +{ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$gt_func_gnugettext_libc=yes" +else + eval "$gt_func_gnugettext_libc=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$gt_func_gnugettext_libc + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + + if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then + + + + + + am_save_CPPFLAGS="$CPPFLAGS" + + for element in $INCICONV; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5 +$as_echo_n "checking for iconv... " >&6; } +if ${am_cv_func_iconv+:} false; then : + $as_echo_n "(cached) " >&6 +else + + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + am_cv_func_iconv=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + am_cv_lib_iconv=yes + am_cv_func_iconv=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$am_save_LIBS" + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5 +$as_echo "$am_cv_func_iconv" >&6; } + if test "$am_cv_func_iconv" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working iconv" >&5 +$as_echo_n "checking for working iconv... " >&6; } +if ${am_cv_func_iconv_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + + am_save_LIBS="$LIBS" + if test $am_cv_lib_iconv = yes; then + LIBS="$LIBS $LIBICONV" + fi + if test "$cross_compiling" = yes; then : + case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int main () +{ + /* Test against AIX 5.1 bug: Failures are not distinguishable from successful + returns. */ + { + iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); + if (cd_utf8_to_88591 != (iconv_t)(-1)) + { + static const char input[] = "\342\202\254"; /* EURO SIGN */ + char buf[10]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_utf8_to_88591, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + return 1; + } + } +#if 0 /* This bug could be worked around by the caller. */ + /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + char buf[50]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_88591_to_utf8, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if ((int)res > 0) + return 1; + } + } +#endif + /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is + provided. */ + if (/* Try standardized names. */ + iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) + /* Try IRIX, OSF/1 names. */ + && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) + /* Try AIX names. */ + && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) + /* Try HP-UX names. */ + && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) + return 1; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + am_cv_func_iconv_works=yes +else + am_cv_func_iconv_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + LIBS="$am_save_LIBS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv_works" >&5 +$as_echo "$am_cv_func_iconv_works" >&6; } + case "$am_cv_func_iconv_works" in + *no) am_func_iconv=no am_cv_lib_iconv=no ;; + *) am_func_iconv=yes ;; + esac + else + am_func_iconv=no am_cv_lib_iconv=no + fi + if test "$am_func_iconv" = yes; then + +$as_echo "#define HAVE_ICONV 1" >>confdefs.h + + fi + if test "$am_cv_lib_iconv" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5 +$as_echo_n "checking how to link with libiconv... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5 +$as_echo "$LIBICONV" >&6; } + else + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + + + + + + + + + use_additional=yes + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + +# Check whether --with-libintl-prefix was given. +if test "${with_libintl_prefix+set}" = set; then : + withval=$with_libintl_prefix; + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi + +fi + + LIBINTL= + LTLIBINTL= + INCINTL= + LIBINTL_PREFIX= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='intl ' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIBINTL="${LIBINTL}${LIBINTL:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$value" + else + : + fi + else + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + else + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + if test "$acl_hardcode_direct" = yes; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + haveit= + for x in $LDFLAGS $LIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so" + else + LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }$found_a" + else + LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir -l$name" + fi + fi + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + LIBINTL_PREFIX="$basedir" + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INCINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + if test -n "$found_la"; then + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBINTL; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + LIBINTL="${LIBINTL}${LIBINTL:+ }$dep" + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$dep" + ;; + esac + done + fi + else + LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name" + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBINTL="${LIBINTL}${LIBINTL:+ }$flag" + else + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBINTL="${LIBINTL}${LIBINTL:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-R$found_dir" + done + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libintl" >&5 +$as_echo_n "checking for GNU gettext in libintl... " >&6; } +if eval \${$gt_func_gnugettext_libintl+:} false; then : + $as_echo_n "(cached) " >&6 +else + gt_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCINTL" + gt_save_LIBS="$LIBS" + LIBS="$LIBS $LIBINTL" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +$gt_revision_test_code +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (const char *); +int +main () +{ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("") + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$gt_func_gnugettext_libintl=yes" +else + eval "$gt_func_gnugettext_libintl=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then + LIBS="$LIBS $LIBICONV" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +$gt_revision_test_code +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (const char *); +int +main () +{ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("") + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + LIBINTL="$LIBINTL $LIBICONV" + LTLIBINTL="$LTLIBINTL $LTLIBICONV" + eval "$gt_func_gnugettext_libintl=yes" + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi + CPPFLAGS="$gt_save_CPPFLAGS" + LIBS="$gt_save_LIBS" +fi +eval ac_res=\$$gt_func_gnugettext_libintl + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + fi + + if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \ + || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \ + && test "$PACKAGE" != gettext-runtime \ + && test "$PACKAGE" != gettext-tools; }; then + gt_use_preinstalled_gnugettext=yes + else + LIBINTL= + LTLIBINTL= + INCINTL= + fi + + + + if test -n "$INTL_MACOSX_LIBS"; then + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + LIBINTL="$LIBINTL $INTL_MACOSX_LIBS" + LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS" + fi + fi + + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + +$as_echo "#define ENABLE_NLS 1" >>confdefs.h + + else + USE_NLS=no + fi + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use NLS" >&5 +$as_echo_n "checking whether to use NLS... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5 +$as_echo "$USE_NLS" >&6; } + if test "$USE_NLS" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking where the gettext function comes from" >&5 +$as_echo_n "checking where the gettext function comes from... " >&6; } + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then + gt_source="external libintl" + else + gt_source="libc" + fi + else + gt_source="included intl directory" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_source" >&5 +$as_echo "$gt_source" >&6; } + fi + + if test "$USE_NLS" = "yes"; then + + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libintl" >&5 +$as_echo_n "checking how to link with libintl... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBINTL" >&5 +$as_echo "$LIBINTL" >&6; } + + for element in $INCINTL; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + fi + + +$as_echo "#define HAVE_GETTEXT 1" >>confdefs.h + + +$as_echo "#define HAVE_DCGETTEXT 1" >>confdefs.h + + fi + + POSUB=po + fi + + + + INTLLIBS="$LIBINTL" + + + + + + + + # gettext requires some extra checks. These really should be part of + # the basic AM_GNU_GETTEXT macro. TODO: move other gettext-specific + # function checks to here. + + for ac_func in strchr +do : + ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr" +if test "x$ac_cv_func_strchr" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRCHR 1 +_ACEOF + +fi +done + +else + USE_NLS=no + USE_INCLUDED_LIBINTL=no + BUILD_INCLUDED_LIBINTL=no + POSUB=po + + + + +fi + +# We use HAVE_LANGINFO_CODESET in a couple of places. + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_langinfo and CODESET" >&5 +$as_echo_n "checking for nl_langinfo and CODESET... " >&6; } +if ${am_cv_langinfo_codeset+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +char* cs = nl_langinfo(CODESET); return !cs; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + am_cv_langinfo_codeset=yes +else + am_cv_langinfo_codeset=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_langinfo_codeset" >&5 +$as_echo "$am_cv_langinfo_codeset" >&6; } + if test $am_cv_langinfo_codeset = yes; then + +$as_echo "#define HAVE_LANGINFO_CODESET 1" >>confdefs.h + + fi + + +# Checks required for our use of locales + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LC_MESSAGES" >&5 +$as_echo_n "checking for LC_MESSAGES... " >&6; } +if ${gt_cv_val_LC_MESSAGES+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +return LC_MESSAGES + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gt_cv_val_LC_MESSAGES=yes +else + gt_cv_val_LC_MESSAGES=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_val_LC_MESSAGES" >&5 +$as_echo "$gt_cv_val_LC_MESSAGES" >&6; } + if test $gt_cv_val_LC_MESSAGES = yes; then + +$as_echo "#define HAVE_LC_MESSAGES 1" >>confdefs.h + + fi + + + +# +# SELinux support +# +if test "$selinux_support" = yes ; then + +$as_echo "#define ENABLE_SELINUX_HACKS 1" >>confdefs.h + +fi + + +# +# Checks for header files. +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for header files" >&5 +$as_echo "$as_me: checking for header files" >&6;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +for ac_header in string.h unistd.h langinfo.h termio.h locale.h getopt.h \ + pty.h utmp.h pwd.h inttypes.h signal.h sys/select.h \ + signal.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if ${ac_cv_header_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + + + +# +# Checks for typedefs, structures, and compiler characteristics. +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for system characteristics" >&5 +$as_echo "$as_me: checking for system characteristics" >&6;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 +$as_echo_n "checking for working volatile... " >&6; } +if ${ac_cv_c_volatile+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +volatile int x; +int * volatile y = (int *) 0; +return !x && !y; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_volatile=yes +else + ac_cv_c_volatile=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5 +$as_echo "$ac_cv_c_volatile" >&6; } +if test $ac_cv_c_volatile = no; then + +$as_echo "#define volatile /**/" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" +if test "x$ac_cv_type_mode_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if ${ac_cv_type_signal+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_type_signal=int +else + ac_cv_type_signal=void +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +ac_fn_c_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include +/* NetBSD declares sys_siglist in unistd.h. */ +#ifdef HAVE_UNISTD_H +# include +#endif + +" +if test "x$ac_cv_have_decl_sys_siglist" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SYS_SIGLIST $ac_have_decl +_ACEOF + + + + + + + for ac_header in $ac_header_list +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + if test $ac_cv_header_sys_socket_h = yes; then + SYS_SOCKET_H='' + else + for ac_header in winsock2.h ws2tcpip.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + SYS_SOCKET_H='sys/socket.h' + fi + + + ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include + #if HAVE_SYS_SOCKET_H + # include + #elif HAVE_WS2TCPIP_H + # include + #endif +" +if test "x$ac_cv_type_socklen_t" = xyes; then : + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socklen_t equivalent" >&5 +$as_echo_n "checking for socklen_t equivalent... " >&6; } + if ${gl_cv_gl_cv_socklen_t_equiv+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Systems have either "struct sockaddr *" or + # "void *" as the second argument to getpeername + gl_cv_socklen_t_equiv= + for arg2 in "struct sockaddr" void; do + for t in int size_t "unsigned int" "long int" "unsigned long int"; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + + int getpeername (int, $arg2 *, $t *); +int +main () +{ +$t len; + getpeername (0, 0, &len); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gl_cv_socklen_t_equiv="$t" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$gl_cv_socklen_t_equiv" != "" && break + done + test "$gl_cv_socklen_t_equiv" != "" && break + done + +fi + + if test "$gl_cv_socklen_t_equiv" = ""; then + as_fn_error $? "Cannot find a type to use in place of socklen_t" "$LINENO" 5 + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_socklen_t_equiv" >&5 +$as_echo "$gl_cv_socklen_t_equiv" >&6; } + +cat >>confdefs.h <<_ACEOF +#define socklen_t $gl_cv_socklen_t_equiv +_ACEOF + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing inet_addr" >&5 +$as_echo_n "checking for library containing inet_addr... " >&6; } +if ${ac_cv_search_inet_addr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char inet_addr (); +int +main () +{ +return inet_addr (); + ; + return 0; +} +_ACEOF +for ac_lib in '' nsl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_inet_addr=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_inet_addr+:} false; then : + break +fi +done +if ${ac_cv_search_inet_addr+:} false; then : + +else + ac_cv_search_inet_addr=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_addr" >&5 +$as_echo "$ac_cv_search_inet_addr" >&6; } +ac_res=$ac_cv_search_inet_addr +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +# Check whether --enable-endian-check was given. +if test "${enable_endian_check+set}" = set; then : + enableval=$enable_endian_check; endiancheck=$enableval +else + endiancheck=yes +fi + + +if test x"$endiancheck" = xyes ; then + + tmp_assumed_endian=big + tmp_assume_warn="" + if test "$cross_compiling" = yes; then + case "$host_cpu" in + i[345678]* ) + tmp_assumed_endian=little + ;; + *) + ;; + esac + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking endianness" >&5 +$as_echo_n "checking endianness... " >&6; } + if ${gnupg_cv_c_endian+:} false; then : + $as_echo_n "(cached) " >&6 +else + gnupg_cv_c_endian=unknown + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include +int +main () +{ + + #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros + #endif + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include +int +main () +{ + + #if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_c_endian=big +else + gnupg_cv_c_endian=little +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test "$gnupg_cv_c_endian" = unknown; then + if test "$cross_compiling" = yes; then : + gnupg_cv_c_endian=$tmp_assumed_endian + tmp_assumed_warn=" (assumed)" + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +main () { + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); + } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + gnupg_cv_c_endian=little +else + gnupg_cv_c_endian=big +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${gnupg_cv_c_endian}${tmp_assumed_warn}" >&5 +$as_echo "${gnupg_cv_c_endian}${tmp_assumed_warn}" >&6; } + if test "$gnupg_cv_c_endian" = little; then + +$as_echo "#define LITTLE_ENDIAN_HOST 1" >>confdefs.h + + else + +$as_echo "#define BIG_ENDIAN_HOST 1" >>confdefs.h + + fi + +fi + +# fixme: we should get rid of the byte type + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for byte typedef" >&5 +$as_echo_n "checking for byte typedef... " >&6; } + if ${gnupg_cv_typedef_byte+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE 1 + #include + #include +int +main () +{ + + #undef byte + int a = sizeof(byte); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_typedef_byte=yes +else + gnupg_cv_typedef_byte=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_typedef_byte" >&5 +$as_echo "$gnupg_cv_typedef_byte" >&6; } + if test "$gnupg_cv_typedef_byte" = yes; then + +$as_echo "#define HAVE_BYTE_TYPEDEF 1" >>confdefs.h + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ushort typedef" >&5 +$as_echo_n "checking for ushort typedef... " >&6; } + if ${gnupg_cv_typedef_ushort+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE 1 + #include + #include +int +main () +{ + + #undef ushort + int a = sizeof(ushort); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_typedef_ushort=yes +else + gnupg_cv_typedef_ushort=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_typedef_ushort" >&5 +$as_echo "$gnupg_cv_typedef_ushort" >&6; } + if test "$gnupg_cv_typedef_ushort" = yes; then + +$as_echo "#define HAVE_USHORT_TYPEDEF 1" >>confdefs.h + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ulong typedef" >&5 +$as_echo_n "checking for ulong typedef... " >&6; } + if ${gnupg_cv_typedef_ulong+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE 1 + #include + #include +int +main () +{ + + #undef ulong + int a = sizeof(ulong); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_typedef_ulong=yes +else + gnupg_cv_typedef_ulong=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_typedef_ulong" >&5 +$as_echo "$gnupg_cv_typedef_ulong" >&6; } + if test "$gnupg_cv_typedef_ulong" = yes; then + +$as_echo "#define HAVE_ULONG_TYPEDEF 1" >>confdefs.h + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for u16 typedef" >&5 +$as_echo_n "checking for u16 typedef... " >&6; } + if ${gnupg_cv_typedef_u16+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE 1 + #include + #include +int +main () +{ + + #undef u16 + int a = sizeof(u16); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_typedef_u16=yes +else + gnupg_cv_typedef_u16=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_typedef_u16" >&5 +$as_echo "$gnupg_cv_typedef_u16" >&6; } + if test "$gnupg_cv_typedef_u16" = yes; then + +$as_echo "#define HAVE_U16_TYPEDEF 1" >>confdefs.h + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for u32 typedef" >&5 +$as_echo_n "checking for u32 typedef... " >&6; } + if ${gnupg_cv_typedef_u32+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _GNU_SOURCE 1 + #include + #include +int +main () +{ + + #undef u32 + int a = sizeof(u32); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_typedef_u32=yes +else + gnupg_cv_typedef_u32=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_typedef_u32" >&5 +$as_echo "$gnupg_cv_typedef_u32" >&6; } + if test "$gnupg_cv_typedef_u32" = yes; then + +$as_echo "#define HAVE_U32_TYPEDEF 1" >>confdefs.h + + fi + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5 +$as_echo_n "checking size of unsigned short... " >&6; } +if ${ac_cv_sizeof_unsigned_short+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_short" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (unsigned short) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_short=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5 +$as_echo "$ac_cv_sizeof_unsigned_short" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5 +$as_echo_n "checking size of unsigned int... " >&6; } +if ${ac_cv_sizeof_unsigned_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_int" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (unsigned int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_int=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5 +$as_echo "$ac_cv_sizeof_unsigned_int" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5 +$as_echo_n "checking size of unsigned long... " >&6; } +if ${ac_cv_sizeof_unsigned_long+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_long" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (unsigned long) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5 +$as_echo "$ac_cv_sizeof_unsigned_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long long" >&5 +$as_echo_n "checking size of unsigned long long... " >&6; } +if ${ac_cv_sizeof_unsigned_long_long+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long long))" "ac_cv_sizeof_unsigned_long_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_long_long" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (unsigned long long) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_long_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long_long" >&5 +$as_echo "$ac_cv_sizeof_unsigned_long_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long +_ACEOF + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if ${ac_cv_header_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5 +$as_echo_n "checking size of time_t... " >&6; } +if ${ac_cv_sizeof_time_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" " +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +"; then : + +else + if test "$ac_cv_type_time_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (time_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_time_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5 +$as_echo "$ac_cv_sizeof_time_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_TIME_T $ac_cv_sizeof_time_t +_ACEOF + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time_t is unsigned" >&5 +$as_echo_n "checking whether time_t is unsigned... " >&6; } +if ${gnupg_cv_time_t_unsigned+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +int +main () +{ +static int test_array [1 - 2 * !(((time_t)-1) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_time_t_unsigned=no +else + gnupg_cv_time_t_unsigned=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_time_t_unsigned" >&5 +$as_echo "$gnupg_cv_time_t_unsigned" >&6; } + if test $gnupg_cv_time_t_unsigned = yes; then + +$as_echo "#define HAVE_UNSIGNED_TIME_T 1" >>confdefs.h + + fi + + + +if test "$ac_cv_sizeof_unsigned_short" = "0" \ + || test "$ac_cv_sizeof_unsigned_int" = "0" \ + || test "$ac_cv_sizeof_unsigned_long" = "0"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Hmmm, something is wrong with the sizes - using defaults" >&5 +$as_echo "$as_me: WARNING: Hmmm, something is wrong with the sizes - using defaults" >&2;}; +fi + + +# +# Checks for library functions. +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library functions" >&5 +$as_echo "$as_me: checking for library functions" >&6;} +ac_fn_c_check_decl "$LINENO" "getpagesize" "ac_cv_have_decl_getpagesize" "$ac_includes_default" +if test "x$ac_cv_have_decl_getpagesize" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_GETPAGESIZE $ac_have_decl +_ACEOF + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } +if ${ac_cv_sys_largefile_source+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_sys_largefile_source=no; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGEFILE_SOURCE 1 +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_sys_largefile_source=1; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_cv_sys_largefile_source=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5 +$as_echo "$ac_cv_sys_largefile_source" >&6; } +case $ac_cv_sys_largefile_source in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source +_ACEOF +;; +esac +rm -rf conftest* + +# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug +# in glibc 2.1.3, but that breaks too many other things. +# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. +if test $ac_cv_sys_largefile_source != unknown; then + +$as_echo "#define HAVE_FSEEKO 1" >>confdefs.h + +fi + +for ac_func in vprintf +do : + ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" +if test "x$ac_cv_func_vprintf" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VPRINTF 1 +_ACEOF + +ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" +if test "x$ac_cv_func__doprnt" = xyes; then : + +$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h + +fi + +fi +done + + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +for ac_header in vfork.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" +if test "x$ac_cv_header_vfork_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VFORK_H 1 +_ACEOF + +fi + +done + +for ac_func in fork vfork +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if ${ac_cv_func_fork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_fork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_fork_works=yes +else + ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +$as_echo "$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if ${ac_cv_func_vfork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_vfork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_vfork_works=yes +else + ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h + +else + +$as_echo "#define vfork fork" >>confdefs.h + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h + +fi + +for ac_func in strerror strlwr tcgetattr mmap canonicalize_file_name +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in strcasecmp strncasecmp ctermid times gmtime_r strtoull +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in setenv unsetenv fcntl ftruncate inet_ntop +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in canonicalize_file_name +do : + ac_fn_c_check_func "$LINENO" "canonicalize_file_name" "ac_cv_func_canonicalize_file_name" +if test "x$ac_cv_func_canonicalize_file_name" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CANONICALIZE_FILE_NAME 1 +_ACEOF + +fi +done + +for ac_func in gettimeofday getrusage getrlimit setrlimit clock_gettime +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in atexit raise getpagesize strftime nl_langinfo setlocale +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in waitpid wait4 sigaction sigprocmask pipe getaddrinfo +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in ttyname rand ftello fsync stat lstat +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +for ac_func in memicmp stpcpy strsep strlwr strtoul memmove stricmp strtol \ + memrchr isascii timegm getrusage setrlimit stat setlocale \ + flockfile funlockfile getpwnam getpwuid \ + getenv inet_pton strpbrk +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# On some systems (e.g. Solaris) nanosleep requires linking to librl. +# Given that we use nanosleep only as an optimization over a select +# based wait function we want it only if it is available in libc. +_save_libs="$LIBS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing nanosleep" >&5 +$as_echo_n "checking for library containing nanosleep... " >&6; } +if ${ac_cv_search_nanosleep+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char nanosleep (); +int +main () +{ +return nanosleep (); + ; + return 0; +} +_ACEOF +for ac_lib in '' ; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_nanosleep=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_nanosleep+:} false; then : + break +fi +done +if ${ac_cv_search_nanosleep+:} false; then : + +else + ac_cv_search_nanosleep=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_nanosleep" >&5 +$as_echo "$ac_cv_search_nanosleep" >&6; } +ac_res=$ac_cv_search_nanosleep +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h + +fi + +LIBS="$_save_libs" + + +# See whether libc supports the Linux inotify interface +case "${host}" in + *-*-linux*) + for ac_func in inotify_init +do : + ac_fn_c_check_func "$LINENO" "inotify_init" "ac_cv_func_inotify_init" +if test "x$ac_cv_func_inotify_init" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_INOTIFY_INIT 1 +_ACEOF + +fi +done + + ;; +esac + + +if test "$have_android_system" = yes; then + # On Android ttyname is a stub but prints an error message. + +$as_echo "#define HAVE_BROKEN_TTYNAME 1" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "struct sigaction" "ac_cv_type_struct_sigaction" "#include +" +if test "x$ac_cv_type_struct_sigaction" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_SIGACTION 1 +_ACEOF + + +fi +ac_fn_c_check_type "$LINENO" "sigset_t" "ac_cv_type_sigset_t" "#include +" +if test "x$ac_cv_type_sigset_t" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_SIGSET_T 1 +_ACEOF + + +fi + + +# Dirmngr requires mmap on Unix systems. +if test $ac_cv_func_mmap != yes -a $mmap_needed = yes; then + as_fn_error $? "Sorry, the current implemenation requires mmap." "$LINENO" 5 +fi + +# +# W32 specific test +# +for ac_header in sys/stat.h unistd.h direct.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if mkdir takes one argument" >&5 +$as_echo_n "checking if mkdir takes one argument... " >&6; } +if ${gnupg_cv_mkdir_takes_one_arg+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_DIRECT_H +# include +#endif +int +main () +{ +mkdir ("foo", 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + gnupg_cv_mkdir_takes_one_arg=no +else + gnupg_cv_mkdir_takes_one_arg=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_mkdir_takes_one_arg" >&5 +$as_echo "$gnupg_cv_mkdir_takes_one_arg" >&6; } +if test $gnupg_cv_mkdir_takes_one_arg = yes ; then + +$as_echo "#define MKDIR_TAKES_ONE_ARG 1" >>confdefs.h + +fi + + +# +# Sanity check regex. Tests adapted from mutt. +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether regular expression support is requested" >&5 +$as_echo_n "checking whether regular expression support is requested... " >&6; } +# Check whether --enable-regex was given. +if test "${enable_regex+set}" = set; then : + enableval=$enable_regex; use_regex=$enableval +else + use_regex=yes +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_regex" >&5 +$as_echo "$use_regex" >&6; } + +if test "$use_regex" = yes ; then + _cppflags="${CPPFLAGS}" + _ldflags="${LDFLAGS}" + +# Check whether --with-regex was given. +if test "${with_regex+set}" = set; then : + withval=$with_regex; + if test -d "$withval" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + +else + withval="" +fi + + + # Does the system have regex functions at all? + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regcomp" >&5 +$as_echo_n "checking for library containing regcomp... " >&6; } +if ${ac_cv_search_regcomp+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char regcomp (); +int +main () +{ +return regcomp (); + ; + return 0; +} +_ACEOF +for ac_lib in '' regex; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_regcomp=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_regcomp+:} false; then : + break +fi +done +if ${ac_cv_search_regcomp+:} false; then : + +else + ac_cv_search_regcomp=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regcomp" >&5 +$as_echo "$ac_cv_search_regcomp" >&6; } +ac_res=$ac_cv_search_regcomp +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + ac_fn_c_check_func "$LINENO" "regcomp" "ac_cv_func_regcomp" +if test "x$ac_cv_func_regcomp" = xyes; then : + gnupg_cv_have_regex=yes +else + gnupg_cv_have_regex=no +fi + + + if test $gnupg_cv_have_regex = no; then + use_regex=no + else + if test x"$cross_compiling" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling; assuming regexp libray is not broken" >&5 +$as_echo "$as_me: WARNING: cross compiling; assuming regexp libray is not broken" >&2;} + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether your system's regexp library is broken" >&5 +$as_echo_n "checking whether your system's regexp library is broken... " >&6; } +if ${gnupg_cv_regex_broken+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + gnupg_cv_regex_broken=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + gnupg_cv_regex_broken=no +else + gnupg_cv_regex_broken=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gnupg_cv_regex_broken" >&5 +$as_echo "$gnupg_cv_regex_broken" >&6; } + + if test $gnupg_cv_regex_broken = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: your regex is broken - disabling regex use" >&5 +$as_echo "$as_me: WARNING: your regex is broken - disabling regex use" >&2;} + use_regex=no + fi + fi + fi + CPPFLAGS="${_cppflags}" + LDFLAGS="${_ldflags}" +fi + +if test "$use_regex" != yes ; then + +$as_echo "#define DISABLE_REGEX 1" >>confdefs.h + +fi + if test x"$use_regex" != xyes; then + DISABLE_REGEX_TRUE= + DISABLE_REGEX_FALSE='#' +else + DISABLE_REGEX_TRUE='#' + DISABLE_REGEX_FALSE= +fi + + + + +# +# Do we have zlib? Must do it here because Solaris failed +# when compiling a conftest (due to the "-lz" from LIBS). +# Note that we combine zlib and bzlib2 in ZLIBS. +# +if test "$use_zip" = yes ; then + _cppflags="${CPPFLAGS}" + _ldflags="${LDFLAGS}" + +# Check whether --with-zlib was given. +if test "${with_zlib+set}" = set; then : + withval=$with_zlib; + if test -d "$withval"; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + +fi + + + ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" +if test "x$ac_cv_header_zlib_h" = xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflateInit2_ in -lz" >&5 +$as_echo_n "checking for deflateInit2_ in -lz... " >&6; } +if ${ac_cv_lib_z_deflateInit2_+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char deflateInit2_ (); +int +main () +{ +return deflateInit2_ (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_z_deflateInit2_=yes +else + ac_cv_lib_z_deflateInit2_=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflateInit2_" >&5 +$as_echo "$ac_cv_lib_z_deflateInit2_" >&6; } +if test "x$ac_cv_lib_z_deflateInit2_" = xyes; then : + + ZLIBS="-lz" + +$as_echo "#define HAVE_ZIP 1" >>confdefs.h + + +else + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags} +fi + +else + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags} +fi + + +fi + + +# +# Check whether we can support bzip2 +# +if test "$use_bzip2" = yes ; then + _cppflags="${CPPFLAGS}" + _ldflags="${LDFLAGS}" + +# Check whether --with-bzip2 was given. +if test "${with_bzip2+set}" = set; then : + withval=$with_bzip2; + if test -d "$withval" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + +else + withval="" +fi + + + # Checking alongside stdio.h as an early version of bzip2 (1.0) + # required stdio.h to be included before bzlib.h, and Solaris 9 is + # woefully out of date. + if test "$withval" != no ; then + ac_fn_c_check_header_compile "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "#include +" +if test "x$ac_cv_header_bzlib_h" = xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZ2_bzCompressInit in -lbz2" >&5 +$as_echo_n "checking for BZ2_bzCompressInit in -lbz2... " >&6; } +if ${ac_cv_lib_bz2_BZ2_bzCompressInit+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbz2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char BZ2_bzCompressInit (); +int +main () +{ +return BZ2_bzCompressInit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bz2_BZ2_bzCompressInit=yes +else + ac_cv_lib_bz2_BZ2_bzCompressInit=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzCompressInit" >&5 +$as_echo "$ac_cv_lib_bz2_BZ2_bzCompressInit" >&6; } +if test "x$ac_cv_lib_bz2_BZ2_bzCompressInit" = xyes; then : + + have_bz2=yes + ZLIBS="$ZLIBS -lbz2" + +$as_echo "#define HAVE_BZIP2 1" >>confdefs.h + + +else + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags} +fi + +else + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags} +fi + + + fi +fi + if test x"$have_bz2" = "xyes"; then + ENABLE_BZIP2_SUPPORT_TRUE= + ENABLE_BZIP2_SUPPORT_FALSE='#' +else + ENABLE_BZIP2_SUPPORT_TRUE='#' + ENABLE_BZIP2_SUPPORT_FALSE= +fi + + + + +# Check for readline support + + +# Check whether --with-readline was given. +if test "${with_readline+set}" = set; then : + withval=$with_readline; _do_readline=$withval +else + _do_readline=yes +fi + + + gnupg_cv_have_readline=no + if test "$_do_readline" != "no" ; then + if test -d "$withval" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + + for _termcap in "" "-ltermcap" "-lcurses" "-lncurses" ; do + _readline_save_libs=$LIBS + _combo="-lreadline${_termcap:+ $_termcap}" + LIBS="$LIBS $_combo" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether readline via \"$_combo\" is present and sane" >&5 +$as_echo_n "checking whether readline via \"$_combo\" is present and sane... " >&6; } + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include + +int +main () +{ + +rl_completion_func_t *completer; +add_history("foobar"); +rl_catch_signals=0; +rl_inhibit_completion=0; +rl_attempted_completion_function=NULL; +rl_completion_matches(NULL,NULL); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + _found_readline=yes +else + _found_readline=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_found_readline" >&5 +$as_echo "$_found_readline" >&6; } + + LIBS=$_readline_save_libs + + if test $_found_readline = yes ; then + +$as_echo "#define HAVE_LIBREADLINE 1" >>confdefs.h + + LIBREADLINE=$_combo + + gnupg_cv_have_readline=yes + break + fi + done + + unset _termcap + unset _readline_save_libs + unset _combo + unset _found_readline + fi + + + +if test "$development_version" = yes; then + +$as_echo "#define IS_DEVELOPMENT_VERSION 1" >>confdefs.h + +fi + + if test x$cross_compiling = xyes; then + CROSS_COMPILING_TRUE= + CROSS_COMPILING_FALSE='#' +else + CROSS_COMPILING_TRUE='#' + CROSS_COMPILING_FALSE= +fi + + + + if ${MAKE-make} --version 2>/dev/null | grep '^GNU ' >/dev/null 2>&1; then + : + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** It seems that you are not using GNU make. Some make tools have serious +*** flaws and you may not be able to build this software at all. Before you +*** complain, please try GNU make: GNU make is easy to build and available +*** at all GNU archives. It is always available from ftp.gnu.org:/gnu/make. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** It seems that you are not using GNU make. Some make tools have serious +*** flaws and you may not be able to build this software at all. Before you +*** complain, please try GNU make: GNU make is easy to build and available +*** at all GNU archives. It is always available from ftp.gnu.org:/gnu/make. +***" >&2;} + fi + + +# Add some extra libs here so that previous tests don't fail for +# mysterious reasons - the final link step should bail out. +# W32SOCKLIBS is also defined so that if can be used for tools not +# requiring any network stuff but linking to code in libcommon which +# tracks in winsock stuff (e.g. init_common_subsystems). +if test "$have_w32_system" = yes; then + if test "$have_w32ce_system" = yes; then + W32SOCKLIBS="-lws2" + else + W32SOCKLIBS="-lws2_32" + fi + NETLIBS="${NETLIBS} ${W32SOCKLIBS}" +fi + + + + +# +# Setup gcc specific options +# +USE_C99_CFLAGS= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cc features" >&5 +$as_echo "$as_me: checking for cc features" >&6;} +if test "$GCC" = yes; then + mycflags= + mycflags_save=$CFLAGS + + # Check whether gcc does not emit a diagnositc for unknow -Wno-* + # options. This is the case for gcc >= 4.6 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc ignores unknown -Wno-* options" >&5 +$as_echo_n "checking if gcc ignores unknown -Wno-* options... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) +#kickerror +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + _gcc_silent_wno=yes +else + _gcc_silent_wno=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_silent_wno" >&5 +$as_echo "$_gcc_silent_wno" >&6; } + + # Note that it is okay to use CFLAGS here because these are just + # warning options and the user should have a chance of overriding + # them. + if test "$USE_MAINTAINER_MODE" = "yes"; then + mycflags="$mycflags -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes" + mycflags="$mycflags -Wformat -Wno-format-y2k -Wformat-security" + if test x"$_gcc_silent_wno" = xyes ; then + _gcc_wopt=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wno-missing-field-initializers" >&5 +$as_echo_n "checking if gcc supports -Wno-missing-field-initializers... " >&6; } + CFLAGS="-Wno-missing-field-initializers" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + _gcc_wopt=yes +else + _gcc_wopt=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_wopt" >&5 +$as_echo "$_gcc_wopt" >&6; } + fi + if test x"$_gcc_wopt" = xyes ; then + mycflags="$mycflags -W -Wno-sign-compare" + mycflags="$mycflags -Wno-missing-field-initializers" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wdeclaration-after-statement" >&5 +$as_echo_n "checking if gcc supports -Wdeclaration-after-statement... " >&6; } + CFLAGS="-Wdeclaration-after-statement" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + _gcc_wopt=yes +else + _gcc_wopt=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_wopt" >&5 +$as_echo "$_gcc_wopt" >&6; } + if test x"$_gcc_wopt" = xyes ; then + mycflags="$mycflags -Wdeclaration-after-statement" + fi + else + mycflags="$mycflags -Wall" + fi + + if test x"$_gcc_silent_wno" = xyes ; then + _gcc_psign=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wno-pointer-sign" >&5 +$as_echo_n "checking if gcc supports -Wno-pointer-sign... " >&6; } + CFLAGS="-Wno-pointer-sign" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + _gcc_psign=yes +else + _gcc_psign=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_psign" >&5 +$as_echo "$_gcc_psign" >&6; } + fi + if test x"$_gcc_psign" = xyes ; then + mycflags="$mycflags -Wno-pointer-sign" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wpointer-arith" >&5 +$as_echo_n "checking if gcc supports -Wpointer-arith... " >&6; } + CFLAGS="-Wpointer-arith" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + _gcc_psign=yes +else + _gcc_psign=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_psign" >&5 +$as_echo "$_gcc_psign" >&6; } + if test x"$_gcc_psign" = xyes ; then + mycflags="$mycflags -Wpointer-arith" + fi + + CFLAGS="$mycflags $mycflags_save" + if test "$use_libdns" = yes; then + # dirmngr/dns.{c,h} require C99 and GNU extensions. */ + USE_C99_CFLAGS="-std=gnu99" + fi +fi + + + + +# +# This is handy for debugging so the compiler doesn't rearrange +# things and eliminate variables. +# +# Check whether --enable-optimization was given. +if test "${enable_optimization+set}" = set; then : + enableval=$enable_optimization; if test $enableval = no ; then + CFLAGS=`echo $CFLAGS | sed s/-O[1-9]\ /-O0\ /g` + fi +fi + + +# +# Add user CFLAGS. +# +CFLAGS="$CFLAGS $CFLAGS_orig" + +# +# Decide what to build +# + +build_scdaemon_extra="" +if test "$build_scdaemon" = "yes"; then + if test $have_libusb = no; then + build_scdaemon_extra="without internal CCID driver" + fi + if test -n "$build_scdaemon_extra"; then + build_scdaemon_extra="(${build_scdaemon_extra})" + fi +fi + + +# +# Set variables for use by automake makefiles. +# + if test "$build_gpg" = "yes"; then + BUILD_GPG_TRUE= + BUILD_GPG_FALSE='#' +else + BUILD_GPG_TRUE='#' + BUILD_GPG_FALSE= +fi + + if test "$build_gpgsm" = "yes"; then + BUILD_GPGSM_TRUE= + BUILD_GPGSM_FALSE='#' +else + BUILD_GPGSM_TRUE='#' + BUILD_GPGSM_FALSE= +fi + + if test "$build_agent" = "yes"; then + BUILD_AGENT_TRUE= + BUILD_AGENT_FALSE='#' +else + BUILD_AGENT_TRUE='#' + BUILD_AGENT_FALSE= +fi + + if test "$build_scdaemon" = "yes"; then + BUILD_SCDAEMON_TRUE= + BUILD_SCDAEMON_FALSE='#' +else + BUILD_SCDAEMON_TRUE='#' + BUILD_SCDAEMON_FALSE= +fi + + if test "$build_g13" = "yes"; then + BUILD_G13_TRUE= + BUILD_G13_FALSE='#' +else + BUILD_G13_TRUE='#' + BUILD_G13_FALSE= +fi + + if test "$build_dirmngr" = "yes"; then + BUILD_DIRMNGR_TRUE= + BUILD_DIRMNGR_FALSE='#' +else + BUILD_DIRMNGR_TRUE='#' + BUILD_DIRMNGR_FALSE= +fi + + if test "$build_tools" = "yes"; then + BUILD_TOOLS_TRUE= + BUILD_TOOLS_FALSE='#' +else + BUILD_TOOLS_TRUE='#' + BUILD_TOOLS_FALSE= +fi + + if test "$build_doc" = "yes"; then + BUILD_DOC_TRUE= + BUILD_DOC_FALSE='#' +else + BUILD_DOC_TRUE='#' + BUILD_DOC_FALSE= +fi + + if test "$build_symcryptrun" = "yes"; then + BUILD_SYMCRYPTRUN_TRUE= + BUILD_SYMCRYPTRUN_FALSE='#' +else + BUILD_SYMCRYPTRUN_TRUE='#' + BUILD_SYMCRYPTRUN_FALSE= +fi + + if test "$build_gpgtar" = "yes"; then + BUILD_GPGTAR_TRUE= + BUILD_GPGTAR_FALSE='#' +else + BUILD_GPGTAR_TRUE='#' + BUILD_GPGTAR_FALSE= +fi + + if test "$build_wks_tools" = "yes"; then + BUILD_WKS_TOOLS_TRUE= + BUILD_WKS_TOOLS_FALSE='#' +else + BUILD_WKS_TOOLS_TRUE='#' + BUILD_WKS_TOOLS_FALSE= +fi + + + if test "$card_support" = yes; then + ENABLE_CARD_SUPPORT_TRUE= + ENABLE_CARD_SUPPORT_FALSE='#' +else + ENABLE_CARD_SUPPORT_TRUE='#' + ENABLE_CARD_SUPPORT_FALSE= +fi + + if test "$use_trust_models" = no; then + NO_TRUST_MODELS_TRUE= + NO_TRUST_MODELS_FALSE='#' +else + NO_TRUST_MODELS_TRUE='#' + NO_TRUST_MODELS_FALSE= +fi + + if test "$use_tofu" = yes; then + USE_TOFU_TRUE= + USE_TOFU_FALSE='#' +else + USE_TOFU_TRUE='#' + USE_TOFU_FALSE= +fi + + +# +# Set some defines for use gpgconf. +# +if test "$build_gpg" = yes ; then + +$as_echo "#define BUILD_WITH_GPG 1" >>confdefs.h + +fi +if test "$build_gpgsm" = yes ; then + +$as_echo "#define BUILD_WITH_GPGSM 1" >>confdefs.h + +fi +if test "$build_agent" = yes ; then + +$as_echo "#define BUILD_WITH_AGENT 1" >>confdefs.h + +fi +if test "$build_scdaemon" = yes ; then + +$as_echo "#define BUILD_WITH_SCDAEMON 1" >>confdefs.h + +fi +if test "$build_dirmngr" = yes ; then + +$as_echo "#define BUILD_WITH_DIRMNGR 1" >>confdefs.h + +fi +if test "$build_g13" = yes ; then + +$as_echo "#define BUILD_WITH_G13 1" >>confdefs.h + +fi + + +# +# Define Name strings +# + +cat >>confdefs.h <<_ACEOF +#define GNUPG_NAME "GnuPG" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPG_NAME "gpg" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPG_DISP_NAME "GnuPG" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPGSM_NAME "gpgsm" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPGSM_DISP_NAME "GPGSM" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPG_AGENT_NAME "gpg-agent" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPG_AGENT_DISP_NAME "GPG Agent" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define SCDAEMON_NAME "scdaemon" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SCDAEMON_DISP_NAME "SCDaemon" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define DIRMNGR_NAME "dirmngr" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define DIRMNGR_DISP_NAME "DirMngr" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define G13_NAME "g13" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define G13_DISP_NAME "G13" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPGCONF_NAME "gpgconf" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPGCONF_DISP_NAME "GPGConf" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPGTAR_NAME "gpgtar" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPG_AGENT_SOCK_NAME "S.gpg-agent" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPG_AGENT_EXTRA_SOCK_NAME "S.gpg-agent.extra" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPG_AGENT_BROWSER_SOCK_NAME "S.gpg-agent.browser" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define GPG_AGENT_SSH_SOCK_NAME "S.gpg-agent.ssh" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define DIRMNGR_INFO_NAME "DIRMNGR_INFO" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SCDAEMON_SOCK_NAME "S.scdaemon" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define DIRMNGR_SOCK_NAME "S.dirmngr" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define DIRMNGR_DEFAULT_KEYSERVER "hkps://hkps.pool.sks-keyservers.net" +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define GPGEXT_GPG "gpg" +_ACEOF + + +if test "$have_w32_system" = yes; then + +cat >>confdefs.h <<_ACEOF +#define GNUPG_REGISTRY_DIR "\\\\Software\\\\GNU\\\\GnuPG" +_ACEOF + +fi + + +# +# Provide information about the build. +# +BUILD_REVISION="ade3246" + + +cat >>confdefs.h <<_ACEOF +#define BUILD_REVISION "$BUILD_REVISION" +_ACEOF + + +BUILD_VERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./'` +BUILD_VERSION="${BUILD_VERSION}44515" +BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,` + + + +# Check whether --enable-build-timestamp was given. +if test "${enable_build_timestamp+set}" = set; then : + enableval=$enable_build_timestamp; if test "$enableval" = "yes"; then + BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` + else + BUILD_TIMESTAMP="$enableval" + fi + BUILD_HOSTNAME="$ac_hostname" +else + BUILD_TIMESTAMP="" + BUILD_HOSTNAME="" +fi + + + +cat >>confdefs.h <<_ACEOF +#define BUILD_TIMESTAMP "$BUILD_TIMESTAMP" +_ACEOF + + + + +# +# Print errors here so that they are visible all +# together and the user can acquire them all together. +# +die=no +if test "$have_gpg_error" = "no"; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** You need libgpg-error to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libgpg-error +*** (at least version $NEED_GPG_ERROR_VERSION is required.) +***" >&5 +$as_echo "$as_me: +*** +*** You need libgpg-error to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libgpg-error +*** (at least version $NEED_GPG_ERROR_VERSION is required.) +***" >&6;} +fi +if test "$have_libgcrypt" = "no"; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** You need libgcrypt to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libgcrypt/ +*** (at least version $NEED_LIBGCRYPT_VERSION (API $NEED_LIBGCRYPT_API) is required.) +***" >&5 +$as_echo "$as_me: +*** +*** You need libgcrypt to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libgcrypt/ +*** (at least version $NEED_LIBGCRYPT_VERSION (API $NEED_LIBGCRYPT_API) is required.) +***" >&6;} +fi +if test "$have_libassuan" = "no"; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** You need libassuan to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libassuan/ +*** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required). +***" >&5 +$as_echo "$as_me: +*** +*** You need libassuan to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libassuan/ +*** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required). +***" >&6;} +fi +if test "$have_ksba" = "no"; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** You need libksba to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libksba/ +*** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required). +***" >&5 +$as_echo "$as_me: +*** +*** You need libksba to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libksba/ +*** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required). +***" >&6;} +fi +if test "$gnupg_have_ldap" = yes; then + if test "$have_w32ce_system" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** Note that CeGCC might be broken, a package fixing this is: +*** http://files.kolab.org/local/windows-ce/ +*** source/wldap32_0.1-mingw32ce.orig.tar.gz +*** binary/wldap32-ce-arm-dev_0.1-1_all.deb +***" >&5 +$as_echo "$as_me: +*** Note that CeGCC might be broken, a package fixing this is: +*** http://files.kolab.org/local/windows-ce/ +*** source/wldap32_0.1-mingw32ce.orig.tar.gz +*** binary/wldap32-ce-arm-dev_0.1-1_all.deb +***" >&6;} + fi +fi +if test "$have_npth" = "no"; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** It is now required to build with support for the +*** New Portable Threads Library (nPth). Please install this +*** library first. The library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/npth/ +*** (at least version $NEED_NPTH_VERSION (API $NEED_NPTH_API) is required). +***" >&5 +$as_echo "$as_me: +*** +*** It is now required to build with support for the +*** New Portable Threads Library (nPth). Please install this +*** library first. The library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/npth/ +*** (at least version $NEED_NPTH_VERSION (API $NEED_NPTH_API) is required). +***" >&6;} +fi + +if test "$require_iconv" = yes; then + if test "$am_func_iconv" != yes; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** The system does not provide a working iconv function. Please +*** install a suitable library; for example GNU Libiconv which is +*** available at: +*** http://ftp.gnu.org/gnu/libiconv/ +***" >&5 +$as_echo "$as_me: +*** +*** The system does not provide a working iconv function. Please +*** install a suitable library; for example GNU Libiconv which is +*** available at: +*** http://ftp.gnu.org/gnu/libiconv/ +***" >&6;} + fi +fi + +if test "$use_ccid_driver" = yes; then + if test "$have_libusb" != yes; then + die=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: +*** +*** You need libusb to build the internal ccid driver. Please +*** install a libusb suitable for your system. +***" >&5 +$as_echo "$as_me: +*** +*** You need libusb to build the internal ccid driver. Please +*** install a libusb suitable for your system. +***" >&6;} + fi +fi + +if test "$die" = "yes"; then + as_fn_error $? " +*** +*** Required libraries not found. Please consult the above messages +*** and install them before running configure again. +***" "$LINENO" 5 +fi + + + +ac_config_files="$ac_config_files m4/Makefile Makefile po/Makefile.in common/Makefile common/w32info-rc.h kbx/Makefile g10/Makefile sm/Makefile agent/Makefile scd/Makefile g13/Makefile dirmngr/Makefile tools/gpg-zip tools/Makefile doc/Makefile tests/Makefile tests/gpgscm/Makefile tests/openpgp/Makefile tests/migrations/Makefile tests/gpgme/Makefile tests/pkits/Makefile g10/gpg.w32-manifest" + + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GNUPG_AGENT_PGM_TRUE}" && test -z "${GNUPG_AGENT_PGM_FALSE}"; then + as_fn_error $? "conditional \"GNUPG_AGENT_PGM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GNUPG_PINENTRY_PGM_TRUE}" && test -z "${GNUPG_PINENTRY_PGM_FALSE}"; then + as_fn_error $? "conditional \"GNUPG_PINENTRY_PGM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GNUPG_SCDAEMON_PGM_TRUE}" && test -z "${GNUPG_SCDAEMON_PGM_FALSE}"; then + as_fn_error $? "conditional \"GNUPG_SCDAEMON_PGM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GNUPG_DIRMNGR_PGM_TRUE}" && test -z "${GNUPG_DIRMNGR_PGM_FALSE}"; then + as_fn_error $? "conditional \"GNUPG_DIRMNGR_PGM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GNUPG_PROTECT_TOOL_PGM_TRUE}" && test -z "${GNUPG_PROTECT_TOOL_PGM_FALSE}"; then + as_fn_error $? "conditional \"GNUPG_PROTECT_TOOL_PGM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GNUPG_DIRMNGR_LDAP_PGM_TRUE}" && test -z "${GNUPG_DIRMNGR_LDAP_PGM_FALSE}"; then + as_fn_error $? "conditional \"GNUPG_DIRMNGR_LDAP_PGM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${USE_GPG2_HACK_TRUE}" && test -z "${USE_GPG2_HACK_FALSE}"; then + as_fn_error $? "conditional \"USE_GPG2_HACK\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${USE_LIBDNS_TRUE}" && test -z "${USE_LIBDNS_FALSE}"; then + as_fn_error $? "conditional \"USE_LIBDNS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_USTAR_TRUE}" && test -z "${HAVE_USTAR_FALSE}"; then + as_fn_error $? "conditional \"HAVE_USTAR\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_DOSISH_SYSTEM_TRUE}" && test -z "${HAVE_DOSISH_SYSTEM_FALSE}"; then + as_fn_error $? "conditional \"HAVE_DOSISH_SYSTEM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${USE_SIMPLE_GETTEXT_TRUE}" && test -z "${USE_SIMPLE_GETTEXT_FALSE}"; then + as_fn_error $? "conditional \"USE_SIMPLE_GETTEXT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_W32_SYSTEM_TRUE}" && test -z "${HAVE_W32_SYSTEM_FALSE}"; then + as_fn_error $? "conditional \"HAVE_W32_SYSTEM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_W32CE_SYSTEM_TRUE}" && test -z "${HAVE_W32CE_SYSTEM_FALSE}"; then + as_fn_error $? "conditional \"HAVE_W32CE_SYSTEM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_ANDROID_SYSTEM_TRUE}" && test -z "${HAVE_ANDROID_SYSTEM_FALSE}"; then + as_fn_error $? "conditional \"HAVE_ANDROID_SYSTEM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${SQLITE3_TRUE}" && test -z "${SQLITE3_FALSE}"; then + as_fn_error $? "conditional \"SQLITE3\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${USE_LDAP_TRUE}" && test -z "${USE_LDAP_FALSE}"; then + as_fn_error $? "conditional \"USE_LDAP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${USE_LDAPWRAPPER_TRUE}" && test -z "${USE_LDAPWRAPPER_FALSE}"; then + as_fn_error $? "conditional \"USE_LDAPWRAPPER\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DISABLE_REGEX_TRUE}" && test -z "${DISABLE_REGEX_FALSE}"; then + as_fn_error $? "conditional \"DISABLE_REGEX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_BZIP2_SUPPORT_TRUE}" && test -z "${ENABLE_BZIP2_SUPPORT_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_BZIP2_SUPPORT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${CROSS_COMPILING_TRUE}" && test -z "${CROSS_COMPILING_FALSE}"; then + as_fn_error $? "conditional \"CROSS_COMPILING\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_GPG_TRUE}" && test -z "${BUILD_GPG_FALSE}"; then + as_fn_error $? "conditional \"BUILD_GPG\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_GPGSM_TRUE}" && test -z "${BUILD_GPGSM_FALSE}"; then + as_fn_error $? "conditional \"BUILD_GPGSM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_AGENT_TRUE}" && test -z "${BUILD_AGENT_FALSE}"; then + as_fn_error $? "conditional \"BUILD_AGENT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_SCDAEMON_TRUE}" && test -z "${BUILD_SCDAEMON_FALSE}"; then + as_fn_error $? "conditional \"BUILD_SCDAEMON\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_G13_TRUE}" && test -z "${BUILD_G13_FALSE}"; then + as_fn_error $? "conditional \"BUILD_G13\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_DIRMNGR_TRUE}" && test -z "${BUILD_DIRMNGR_FALSE}"; then + as_fn_error $? "conditional \"BUILD_DIRMNGR\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_TOOLS_TRUE}" && test -z "${BUILD_TOOLS_FALSE}"; then + as_fn_error $? "conditional \"BUILD_TOOLS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_DOC_TRUE}" && test -z "${BUILD_DOC_FALSE}"; then + as_fn_error $? "conditional \"BUILD_DOC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_SYMCRYPTRUN_TRUE}" && test -z "${BUILD_SYMCRYPTRUN_FALSE}"; then + as_fn_error $? "conditional \"BUILD_SYMCRYPTRUN\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_GPGTAR_TRUE}" && test -z "${BUILD_GPGTAR_FALSE}"; then + as_fn_error $? "conditional \"BUILD_GPGTAR\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${BUILD_WKS_TOOLS_TRUE}" && test -z "${BUILD_WKS_TOOLS_FALSE}"; then + as_fn_error $? "conditional \"BUILD_WKS_TOOLS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_CARD_SUPPORT_TRUE}" && test -z "${ENABLE_CARD_SUPPORT_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_CARD_SUPPORT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${NO_TRUST_MODELS_TRUE}" && test -z "${NO_TRUST_MODELS_FALSE}"; then + as_fn_error $? "conditional \"NO_TRUST_MODELS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${USE_TOFU_TRUE}" && test -z "${USE_TOFU_FALSE}"; then + as_fn_error $? "conditional \"USE_TOFU\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by gnupg $as_me 2.1.17, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +gnupg config.status 2.1.17 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +# Capture the value of obsolete ALL_LINGUAS because we need it to compute + # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it + # from automake < 1.5. + eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' + # Capture the value of LINGUAS because we need it to compute CATALOGS. + LINGUAS="${LINGUAS-%UNSET%}" + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "po-directories") CONFIG_COMMANDS="$CONFIG_COMMANDS po-directories" ;; + "m4/Makefile") CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "po/Makefile.in") CONFIG_FILES="$CONFIG_FILES po/Makefile.in" ;; + "common/Makefile") CONFIG_FILES="$CONFIG_FILES common/Makefile" ;; + "common/w32info-rc.h") CONFIG_FILES="$CONFIG_FILES common/w32info-rc.h" ;; + "kbx/Makefile") CONFIG_FILES="$CONFIG_FILES kbx/Makefile" ;; + "g10/Makefile") CONFIG_FILES="$CONFIG_FILES g10/Makefile" ;; + "sm/Makefile") CONFIG_FILES="$CONFIG_FILES sm/Makefile" ;; + "agent/Makefile") CONFIG_FILES="$CONFIG_FILES agent/Makefile" ;; + "scd/Makefile") CONFIG_FILES="$CONFIG_FILES scd/Makefile" ;; + "g13/Makefile") CONFIG_FILES="$CONFIG_FILES g13/Makefile" ;; + "dirmngr/Makefile") CONFIG_FILES="$CONFIG_FILES dirmngr/Makefile" ;; + "tools/gpg-zip") CONFIG_FILES="$CONFIG_FILES tools/gpg-zip" ;; + "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + "tests/gpgscm/Makefile") CONFIG_FILES="$CONFIG_FILES tests/gpgscm/Makefile" ;; + "tests/openpgp/Makefile") CONFIG_FILES="$CONFIG_FILES tests/openpgp/Makefile" ;; + "tests/migrations/Makefile") CONFIG_FILES="$CONFIG_FILES tests/migrations/Makefile" ;; + "tests/gpgme/Makefile") CONFIG_FILES="$CONFIG_FILES tests/gpgme/Makefile" ;; + "tests/pkits/Makefile") CONFIG_FILES="$CONFIG_FILES tests/pkits/Makefile" ;; + "g10/gpg.w32-manifest") CONFIG_FILES="$CONFIG_FILES g10/gpg.w32-manifest" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "po-directories":C) + for ac_file in $CONFIG_FILES; do + # Support "outfile[:infile[:infile...]]" + case "$ac_file" in + *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + esac + # PO directories have a Makefile.in generated from Makefile.in.in. + case "$ac_file" in */Makefile.in) + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + # Treat a directory as a PO directory if and only if it has a + # POTFILES.in file. This allows packages to have multiple PO + # directories under different names or in different locations. + if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then + rm -f "$ac_dir/POTFILES" + test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" + cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" + POMAKEFILEDEPS="POTFILES.in" + # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend + # on $ac_dir but don't depend on user-specified configuration + # parameters. + if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then + # The LINGUAS file contains the set of available languages. + if test -n "$OBSOLETE_ALL_LINGUAS"; then + test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" + fi + ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"` + # Hide the ALL_LINGUAS assigment from automake < 1.5. + eval 'ALL_LINGUAS''=$ALL_LINGUAS_' + POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" + else + # The set of available languages was given in configure.in. + # Hide the ALL_LINGUAS assigment from automake < 1.5. + eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' + fi + # Compute POFILES + # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po) + # Compute UPDATEPOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update) + # Compute DUMMYPOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop) + # Compute GMOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo) + case "$ac_given_srcdir" in + .) srcdirpre= ;; + *) srcdirpre='$(srcdir)/' ;; + esac + POFILES= + UPDATEPOFILES= + DUMMYPOFILES= + GMOFILES= + for lang in $ALL_LINGUAS; do + POFILES="$POFILES $srcdirpre$lang.po" + UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" + DUMMYPOFILES="$DUMMYPOFILES $lang.nop" + GMOFILES="$GMOFILES $srcdirpre$lang.gmo" + done + # CATALOGS depends on both $ac_dir and the user's LINGUAS + # environment variable. + INST_LINGUAS= + if test -n "$ALL_LINGUAS"; then + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "$LINGUAS"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + INST_LINGUAS="$INST_LINGUAS $presentlang" + fi + done + fi + CATALOGS= + if test -n "$INST_LINGUAS"; then + for lang in $INST_LINGUAS; do + CATALOGS="$CATALOGS $lang.gmo" + done + fi + test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" + sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" + for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do + if test -f "$f"; then + case "$f" in + *.orig | *.bak | *~) ;; + *) cat "$f" >> "$ac_dir/Makefile" ;; + esac + fi + done + fi + ;; + esac + done ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + + +echo " + GnuPG v${VERSION} has been configured as follows: + + Revision: ade3246 (44515) + Platform: $PRINTABLE_OS_NAME ($host) + + OpenPGP: $build_gpg + S/MIME: $build_gpgsm + Agent: $build_agent + Smartcard: $build_scdaemon $build_scdaemon_extra + G13: $build_g13 + Dirmngr: $build_dirmngr + Gpgtar: $build_gpgtar + WKS tools: $build_wks_tools + + Protect tool: $show_gnupg_protect_tool_pgm + LDAP wrapper: $show_gnupg_dirmngr_ldap_pgm + Default agent: $show_gnupg_agent_pgm + Default pinentry: $show_gnupg_pinentry_pgm + Default scdaemon: $show_gnupg_scdaemon_pgm + Default dirmngr: $show_gnupg_dirmngr_pgm + + Dirmngr auto start: $dirmngr_auto_start + Readline support: $gnupg_cv_have_readline + LDAP support: $gnupg_have_ldap + TLS support: $use_tls_library + TOFU support: $use_tofu + Tor support: $show_tor_support +" +if test x"$use_regex" != xyes ; then +echo " + Warning: No regular expression support available. + OpenPGP trust signatures won't work. + gpg-check-pattern will not be built. +" +fi +if test "x${gpg_config_script_warn}" != x; then +cat <. + +# Process this file with autoconf to produce a configure script. +AC_PREREQ(2.61) +min_automake_version="1.14" + +# To build a release you need to create a tag with the version number +# (git tag -s gnupg-2.n.m) and run "./autogen.sh --force". Please +# bump the version number immediately *after* the release and do +# another commit and push so that the git magic is able to work. +m4_define([mym4_package],[gnupg]) +m4_define([mym4_major], [2]) +m4_define([mym4_minor], [1]) +m4_define([mym4_micro], [17]) + +# To start a new development series, i.e a new major or minor number +# you need to mark an arbitrary commit before the first beta release +# with an annotated tag. For example the 2.1 branch starts off with +# the tag "gnupg-2.1-base". This is used as the base for counting +# beta numbers before the first release of a series. + +# Below is m4 magic to extract and compute the git revision number, +# the decimalized short revision number, a beta version string and a +# flag indicating a development version (mym4_isbeta). Note that the +# m4 processing is done by autoconf and not during the configure run. +m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ + mym4_package mym4_major mym4_minor mym4_micro),[:])) +m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) +m4_define([mym4_version], m4_argn(4, mym4_verslist)) +m4_define([mym4_revision], m4_argn(7, mym4_verslist)) +m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) +m4_esyscmd([echo ]mym4_version[>VERSION]) +AC_INIT([mym4_package],[mym4_version], [https://bugs.gnupg.org]) + +NEED_GPG_ERROR_VERSION=1.24 + +NEED_LIBGCRYPT_API=1 +NEED_LIBGCRYPT_VERSION=1.7.0 + +NEED_LIBASSUAN_API=2 +NEED_LIBASSUAN_VERSION=2.4.3 + +NEED_KSBA_API=1 +NEED_KSBA_VERSION=1.3.4 + +NEED_NTBTLS_API=1 +NEED_NTBTLS_VERSION=0.1.0 + +NEED_NPTH_API=1 +NEED_NPTH_VERSION=1.2 + + +NEED_GNUTLS_VERSION=3.0 + +NEED_SQLITE_VERSION=3.7 + +development_version=mym4_isbeta +PACKAGE=$PACKAGE_NAME +PACKAGE_GT=${PACKAGE_NAME}2 +VERSION=$PACKAGE_VERSION + +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_SRCDIR([sm/gpgsm.c]) +AC_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) +AC_CANONICAL_HOST +AB_INIT + +AC_GNU_SOURCE + +# Before we do anything with the C compiler, we first save the user's +# CFLAGS (they are restored at the end of the configure script). This +# is because some configure checks don't work with -Werror, but we'd +# like to use -Werror with our build. +CFLAGS_orig=$CFLAGS +CFLAGS= + +# Some status variables. +have_gpg_error=no +have_libgcrypt=no +have_libassuan=no +have_ksba=no +have_ntbtls=no +have_gnutls=no +have_sqlite=no +have_npth=no +have_libusb=no +have_system_resolver=no +gnupg_have_ldap="n/a" + +use_zip=yes +use_bzip2=yes +use_exec=yes +use_trust_models=yes +use_tofu=yes +use_libdns=yes +card_support=yes +use_ccid_driver=auto +dirmngr_auto_start=yes +use_tls_library=no +large_secmem=no +show_tor_support=no + + +GNUPG_BUILD_PROGRAM(gpg, yes) +GNUPG_BUILD_PROGRAM(gpgsm, yes) +# The agent is a required part and can't be disabled anymore. +build_agent=yes +GNUPG_BUILD_PROGRAM(scdaemon, yes) +GNUPG_BUILD_PROGRAM(g13, no) +GNUPG_BUILD_PROGRAM(dirmngr, yes) +GNUPG_BUILD_PROGRAM(tools, yes) +GNUPG_BUILD_PROGRAM(doc, yes) +GNUPG_BUILD_PROGRAM(symcryptrun, no) +# We use gpgtar to unpack test data, hence we always build it. If the +# user opts out, we simply don't install it. +GNUPG_BUILD_PROGRAM(gpgtar, yes) +GNUPG_BUILD_PROGRAM(wks-tools, no) + +AC_SUBST(PACKAGE) +AC_SUBST(PACKAGE_GT) +AC_SUBST(VERSION) +AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) +AC_DEFINE_UNQUOTED(PACKAGE_GT, "$PACKAGE_GT", + [Name of this package for gettext]) +AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) +AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT", + [Bug report address]) +AC_DEFINE_UNQUOTED(NEED_LIBGCRYPT_VERSION, "$NEED_LIBGCRYPT_VERSION", + [Required version of Libgcrypt]) +AC_DEFINE_UNQUOTED(NEED_KSBA_VERSION, "$NEED_KSBA_VERSION", + [Required version of Libksba]) +AC_DEFINE_UNQUOTED(NEED_NTBTLS_VERSION, "$NEED_NTBTLS_VERSION", + [Required version of NTBTLS]) + + + +# The default is to use the modules from this package and the few +# other packages in a standard place; i.e where this package gets +# installed. With these options it is possible to override these +# ${prefix} depended values with fixed paths, which can't be replaced +# at make time. See also am/cmacros.am and the defaults in AH_BOTTOM. +AC_ARG_WITH(agent-pgm, + [ --with-agent-pgm=PATH Use PATH as the default for the agent)], + GNUPG_AGENT_PGM="$withval", GNUPG_AGENT_PGM="" ) +AC_SUBST(GNUPG_AGENT_PGM) +AM_CONDITIONAL(GNUPG_AGENT_PGM, test -n "$GNUPG_AGENT_PGM") +show_gnupg_agent_pgm="(default)" +test -n "$GNUPG_AGENT_PGM" && show_gnupg_agent_pgm="$GNUPG_AGENT_PGM" + +AC_ARG_WITH(pinentry-pgm, + [ --with-pinentry-pgm=PATH Use PATH as the default for the pinentry)], + GNUPG_PINENTRY_PGM="$withval", GNUPG_PINENTRY_PGM="" ) +AC_SUBST(GNUPG_PINENTRY_PGM) +AM_CONDITIONAL(GNUPG_PINENTRY_PGM, test -n "$GNUPG_PINENTRY_PGM") +show_gnupg_pinentry_pgm="(default)" +test -n "$GNUPG_PINENTRY_PGM" && show_gnupg_pinentry_pgm="$GNUPG_PINENTRY_PGM" + + +AC_ARG_WITH(scdaemon-pgm, + [ --with-scdaemon-pgm=PATH Use PATH as the default for the scdaemon)], + GNUPG_SCDAEMON_PGM="$withval", GNUPG_SCDAEMON_PGM="" ) +AC_SUBST(GNUPG_SCDAEMON_PGM) +AM_CONDITIONAL(GNUPG_SCDAEMON_PGM, test -n "$GNUPG_SCDAEMON_PGM") +show_gnupg_scdaemon_pgm="(default)" +test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM" + + +AC_ARG_WITH(dirmngr-pgm, + [ --with-dirmngr-pgm=PATH Use PATH as the default for the dirmngr)], + GNUPG_DIRMNGR_PGM="$withval", GNUPG_DIRMNGR_PGM="" ) +AC_SUBST(GNUPG_DIRMNGR_PGM) +AM_CONDITIONAL(GNUPG_DIRMNGR_PGM, test -n "$GNUPG_DIRMNGR_PGM") +show_gnupg_dirmngr_pgm="(default)" +test -n "$GNUPG_DIRMNGR_PGM" && show_gnupg_dirmngr_pgm="$GNUPG_DIRMNGR_PGM" + +AC_ARG_WITH(protect-tool-pgm, + [ --with-protect-tool-pgm=PATH Use PATH as the default for the protect-tool)], + GNUPG_PROTECT_TOOL_PGM="$withval", GNUPG_PROTECT_TOOL_PGM="" ) +AC_SUBST(GNUPG_PROTECT_TOOL_PGM) +AM_CONDITIONAL(GNUPG_PROTECT_TOOL_PGM, test -n "$GNUPG_PROTECT_TOOL_PGM") +show_gnupg_protect_tool_pgm="(default)" +test -n "$GNUPG_PROTECT_TOOL_PGM" \ + && show_gnupg_protect_tool_pgm="$GNUPG_PROTECT_TOOL_PGM" + +AC_ARG_WITH(dirmngr-ldap-pgm, + [ --with-dirmngr-ldap-pgm=PATH Use PATH as the default for the dirmngr ldap wrapper)], + GNUPG_DIRMNGR_LDAP_PGM="$withval", GNUPG_DIRMNGR_LDAP_PGM="" ) +AC_SUBST(GNUPG_DIRMNGR_LDAP_PGM) +AM_CONDITIONAL(GNUPG_DIRMNGR_LDAP_PGM, test -n "$GNUPG_DIRMNGR_LDAP_PGM") +show_gnupg_dirmngr_ldap_pgm="(default)" +test -n "$GNUPG_DIRMNGR_LDAP_PGM" \ + && show_gnupg_dirmngr_ldap_pgm="$GNUPG_DIRMNGR_LDAP_PGM" + +# +# On some platforms gpg2 is usually installed as gpg without using a +# symlink. For correct operation of gpgconf it needs to know the +# installed name of gpg. This option sets "gpg2"'s installed name to +# just "gpg". Note that it might be required to rename gpg2 to gpg +# manually after the build process. +# +AC_ARG_ENABLE(gpg2-is-gpg, + AC_HELP_STRING([--enable-gpg2-is-gpg],[Set installed name of gpg2 to gpg]), + gpg2_is_gpg=$enableval) +if test "$gpg2_is_gpg" != "yes"; then + AC_DEFINE(USE_GPG2_HACK, 1, [Define to install gpg as gpg2]) +fi +AM_CONDITIONAL(USE_GPG2_HACK, test "$gpg2_is_gpg" != "yes") + + +# SELinux support includes tracking of sensitive files to avoid +# leaking their contents through processing these files by gpg itself +AC_MSG_CHECKING([whether SELinux support is requested]) +AC_ARG_ENABLE(selinux-support, + AC_HELP_STRING([--enable-selinux-support], + [enable SELinux support]), + selinux_support=$enableval, selinux_support=no) +AC_MSG_RESULT($selinux_support) + + +AC_MSG_CHECKING([whether to allocate extra secure memory]) +AC_ARG_ENABLE(large-secmem, + AC_HELP_STRING([--enable-large-secmem], + [allocate extra secure memory]), + large_secmem=$enableval, large_secmem=no) +AC_MSG_RESULT($large_secmem) +if test "$large_secmem" = yes ; then + SECMEM_BUFFER_SIZE=65536 +else + SECMEM_BUFFER_SIZE=32768 +fi +AC_DEFINE_UNQUOTED(SECMEM_BUFFER_SIZE,$SECMEM_BUFFER_SIZE, + [Size of secure memory buffer]) + +AC_MSG_CHECKING([whether to enable trust models]) +AC_ARG_ENABLE(trust-models, + AC_HELP_STRING([--disable-trust-models], + [disable all trust models except "always"]), + use_trust_models=$enableval) +AC_MSG_RESULT($use_trust_models) +if test "$use_trust_models" = no ; then + AC_DEFINE(NO_TRUST_MODELS, 1, + [Define to include only trust-model always]) +fi + +AC_MSG_CHECKING([whether to enable TOFU]) +AC_ARG_ENABLE(tofu, + AC_HELP_STRING([--disable-tofu], + [disable the TOFU trust model]), + use_tofu=$enableval, use_tofu=$use_trust_models) +AC_MSG_RESULT($use_tofu) +if test "$use_trust_models" = no && test "$use_tofu" = yes; then + AC_MSG_ERROR([both --disable-trust-models and --enable-tofu given]) +fi + +AC_MSG_CHECKING([whether to enable libdns]) +AC_ARG_ENABLE(libdns, + AC_HELP_STRING([--disable-libdns], + [do not build with libdns support]), + use_libdns=$enableval, use_libdns=yes) +AC_MSG_RESULT($use_libdns) +if test x"$use_libdns" = xyes ; then + AC_DEFINE(USE_LIBDNS, 1, [Build with integrated libdns support]) +fi +AM_CONDITIONAL(USE_LIBDNS, test "$use_libdns" = yes) + + +# +# Options to disable algorithm +# + +GNUPG_GPG_DISABLE_ALGO([rsa],[RSA public key]) +# Elgamal is a MUST algorithm +# DSA is a MUST algorithm +GNUPG_GPG_DISABLE_ALGO([ecdh],[ECDH public key]) +GNUPG_GPG_DISABLE_ALGO([ecdsa],[ECDSA public key]) +GNUPG_GPG_DISABLE_ALGO([eddsa],[EdDSA public key]) + +GNUPG_GPG_DISABLE_ALGO([idea],[IDEA cipher]) +# 3DES is a MUST algorithm +GNUPG_GPG_DISABLE_ALGO([cast5],[CAST5 cipher]) +GNUPG_GPG_DISABLE_ALGO([blowfish],[BLOWFISH cipher]) +GNUPG_GPG_DISABLE_ALGO([aes128],[AES128 cipher]) +GNUPG_GPG_DISABLE_ALGO([aes192],[AES192 cipher]) +GNUPG_GPG_DISABLE_ALGO([aes256],[AES256 cipher]) +GNUPG_GPG_DISABLE_ALGO([twofish],[TWOFISH cipher]) +GNUPG_GPG_DISABLE_ALGO([camellia128],[CAMELLIA128 cipher]) +GNUPG_GPG_DISABLE_ALGO([camellia192],[CAMELLIA192 cipher]) +GNUPG_GPG_DISABLE_ALGO([camellia256],[CAMELLIA256 cipher]) + +GNUPG_GPG_DISABLE_ALGO([md5],[MD5 hash]) +# SHA1 is a MUST algorithm +GNUPG_GPG_DISABLE_ALGO([rmd160],[RIPE-MD160 hash]) +GNUPG_GPG_DISABLE_ALGO([sha224],[SHA-224 hash]) +# SHA256 is a MUST algorithm for GnuPG. +GNUPG_GPG_DISABLE_ALGO([sha384],[SHA-384 hash]) +GNUPG_GPG_DISABLE_ALGO([sha512],[SHA-512 hash]) + + +# Allow disabling of zip support. +# This is in general not a good idea because according to rfc4880 OpenPGP +# implementations SHOULD support ZLIB. +AC_MSG_CHECKING([whether to enable the ZIP and ZLIB compression algorithm]) +AC_ARG_ENABLE(zip, + AC_HELP_STRING([--disable-zip], + [disable the ZIP and ZLIB compression algorithm]), + use_zip=$enableval) +AC_MSG_RESULT($use_zip) + +# Allow disabling of bzib2 support. +# It is defined only after we confirm the library is available later +AC_MSG_CHECKING([whether to enable the BZIP2 compression algorithm]) +AC_ARG_ENABLE(bzip2, + AC_HELP_STRING([--disable-bzip2],[disable the BZIP2 compression algorithm]), + use_bzip2=$enableval) +AC_MSG_RESULT($use_bzip2) + +# Configure option to allow or disallow execution of external +# programs, like a photo viewer. +AC_MSG_CHECKING([whether to enable external program execution]) +AC_ARG_ENABLE(exec, + AC_HELP_STRING([--disable-exec],[disable all external program execution]), + use_exec=$enableval) +AC_MSG_RESULT($use_exec) +if test "$use_exec" = no ; then + AC_DEFINE(NO_EXEC,1,[Define to disable all external program execution]) +fi + +if test "$use_exec" = yes ; then + AC_MSG_CHECKING([whether to enable photo ID viewing]) + AC_ARG_ENABLE(photo-viewers, + [ --disable-photo-viewers disable photo ID viewers], + [if test "$enableval" = no ; then + AC_DEFINE(DISABLE_PHOTO_VIEWER,1,[define to disable photo viewing]) + fi],enableval=yes) + gnupg_cv_enable_photo_viewers=$enableval + AC_MSG_RESULT($enableval) + + if test "$gnupg_cv_enable_photo_viewers" = yes ; then + AC_MSG_CHECKING([whether to use a fixed photo ID viewer]) + AC_ARG_WITH(photo-viewer, + [ --with-photo-viewer=FIXED_VIEWER set a fixed photo ID viewer], + [if test "$withval" = yes ; then + withval=no + elif test "$withval" != no ; then + AC_DEFINE_UNQUOTED(FIXED_PHOTO_VIEWER,"$withval", + [if set, restrict photo-viewer to this]) + fi],withval=no) + AC_MSG_RESULT($withval) + fi +fi + + +# +# Check for the key/uid cache size. This can't be zero, but can be +# pretty small on embedded systems. This is used for the gpg part. +# +AC_MSG_CHECKING([for the size of the key and uid cache]) +AC_ARG_ENABLE(key-cache, + AC_HELP_STRING([--enable-key-cache=SIZE], + [Set key cache to SIZE (default 4096)]),,enableval=4096) +if test "$enableval" = "no"; then + enableval=5 +elif test "$enableval" = "yes" || test "$enableval" = ""; then + enableval=4096 +fi +changequote(,)dnl +key_cache_size=`echo "$enableval" | sed 's/[A-Za-z]//g'` +changequote([,])dnl +if test "$enableval" != "$key_cache_size" || test "$key_cache_size" -lt 5; then + AC_MSG_ERROR([invalid key-cache size]) +fi +AC_MSG_RESULT($key_cache_size) +AC_DEFINE_UNQUOTED(PK_UID_CACHE_SIZE,$key_cache_size, + [Size of the key and UID caches]) + + + +# +# Check whether we want to use Linux capabilities +# +AC_MSG_CHECKING([whether use of capabilities is requested]) +AC_ARG_WITH(capabilities, + [ --with-capabilities use linux capabilities [default=no]], +[use_capabilities="$withval"],[use_capabilities=no]) +AC_MSG_RESULT($use_capabilities) + +# +# Check whether to disable the card support +AC_MSG_CHECKING([whether smartcard support is requested]) +AC_ARG_ENABLE(card-support, + AC_HELP_STRING([--disable-card-support], + [disable smartcard support]), + card_support=$enableval) +AC_MSG_RESULT($card_support) +if test "$card_support" = yes ; then + AC_DEFINE(ENABLE_CARD_SUPPORT,1,[Define to include smartcard support]) +else + build_scdaemon=no +fi + +# +# Allow disabling of internal CCID support. +# It is defined only after we confirm the library is available later +# +AC_MSG_CHECKING([whether to enable the internal CCID driver]) +AC_ARG_ENABLE(ccid-driver, + AC_HELP_STRING([--disable-ccid-driver], + [disable the internal CCID driver]), + use_ccid_driver=$enableval) +AC_MSG_RESULT($use_ccid_driver) + +AC_MSG_CHECKING([whether to auto start dirmngr]) +AC_ARG_ENABLE(dirmngr-auto-start, + AC_HELP_STRING([--disable-dirmngr-auto-start], + [disable auto starting of the dirmngr]), + dirmngr_auto_start=$enableval) +AC_MSG_RESULT($dirmngr_auto_start) +if test "$dirmngr_auto_start" = yes ; then + AC_DEFINE(USE_DIRMNGR_AUTO_START,1, + [Define to enable auto starting of the dirmngr]) +fi + + +# +# To avoid double inclusion of config.h which might happen at some +# places, we add the usual double inclusion protection at the top of +# config.h. +# +AH_TOP([ +#ifndef GNUPG_CONFIG_H_INCLUDED +#define GNUPG_CONFIG_H_INCLUDED +]) + +# +# Stuff which goes at the bottom of config.h. +# +AH_BOTTOM([ +/* This is the major version number of GnuPG so that + source included files can test for this. Note, that + we use 2 here even for GnuPG 1.9.x. */ +#define GNUPG_MAJOR_VERSION 2 + +/* Now to separate file name parts. + Please note that the string version must not contain more + than one character because the code assumes strlen()==1 */ +#ifdef HAVE_DOSISH_SYSTEM +#define DIRSEP_C '\\' +#define DIRSEP_S "\\" +#define EXTSEP_C '.' +#define EXTSEP_S "." +#define PATHSEP_C ';' +#define PATHSEP_S ";" +#define EXEEXT_S ".exe" +#else +#define DIRSEP_C '/' +#define DIRSEP_S "/" +#define EXTSEP_C '.' +#define EXTSEP_S "." +#define PATHSEP_C ':' +#define PATHSEP_S ":" +#define EXEEXT_S "" +#endif + +/* This is the same as VERSION, but should be overridden if the + platform cannot handle things like dots '.' in filenames. Set + SAFE_VERSION_DOT and SAFE_VERSION_DASH to whatever SAFE_VERSION + uses for dots and dashes. */ +#define SAFE_VERSION VERSION +#define SAFE_VERSION_DOT '.' +#define SAFE_VERSION_DASH '-' + +/* Some global constants. */ +#ifdef HAVE_DOSISH_SYSTEM +# ifdef HAVE_DRIVE_LETTERS +# define GNUPG_DEFAULT_HOMEDIR "c:/gnupg" +# else +# define GNUPG_DEFAULT_HOMEDIR "/gnupg" +# endif +#elif defined(__VMS) +#define GNUPG_DEFAULT_HOMEDIR "/SYS$LOGIN/gnupg" +#else +#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg" +#endif +#define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d" +#define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d" + +/* For some systems (DOS currently), we hardcode the path here. For + POSIX systems the values are constructed by the Makefiles, so that + the values may be overridden by the make invocations; this is to + comply with the GNU coding standards. Note that these values are + only defaults. */ +#ifdef HAVE_DOSISH_SYSTEM +# ifdef HAVE_DRIVE_LETTERS +# define GNUPG_BINDIR "c:\\gnupg" +# define GNUPG_LIBEXECDIR "c:\\gnupg" +# define GNUPG_LIBDIR "c:\\gnupg" +# define GNUPG_DATADIR "c:\\gnupg" +# define GNUPG_SYSCONFDIR "c:\\gnupg" +# else +# define GNUPG_BINDIR "\\gnupg" +# define GNUPG_LIBEXECDIR "\\gnupg" +# define GNUPG_LIBDIR "\\gnupg" +# define GNUPG_DATADIR "\\gnupg" +# define GNUPG_SYSCONFDIR "\\gnupg" +# endif +#endif + +/* Derive some other constants. */ +#if !(defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID)) +#define EXEC_TEMPFILE_ONLY +#endif + + +/* We didn't define endianness above, so get it from OS macros. This + is intended for making fat binary builds on OS X. */ +#if !defined(BIG_ENDIAN_HOST) && !defined(LITTLE_ENDIAN_HOST) +#if defined(__BIG_ENDIAN__) +#define BIG_ENDIAN_HOST 1 +#elif defined(__LITTLE_ENDIAN__) +#define LITTLE_ENDIAN_HOST 1 +#else +#error "No endianness found" +#endif +#endif + + +/* Hack used for W32: ldap.m4 also tests for the ASCII version of + ldap_start_tls_s because that is the actual symbol used in the + library. winldap.h redefines it to our commonly used value, + thus we define our usual macro here. */ +#ifdef HAVE_LDAP_START_TLS_SA +# ifndef HAVE_LDAP_START_TLS_S +# define HAVE_LDAP_START_TLS_S 1 +# endif +#endif + +/* Provide the es_ macro for estream. */ +#define GPGRT_ENABLE_ES_MACROS 1 + +/* Tell libgcrypt not to use its own libgpg-error implementation. */ +#define USE_LIBGPG_ERROR 1 + +/* Tell Libgcrypt not to include deprecated definitions. */ +#define GCRYPT_NO_DEPRECATED 1 + +/* Our HTTP code is used in estream mode. */ +#define HTTP_USE_ESTREAM 1 + +/* Under W32 we do an explicit socket initialization, thus we need to + avoid the on-demand initialization which would also install an atexit + handler. */ +#define HTTP_NO_WSASTARTUP + +/* Under Windows we use the gettext code from libgpg-error. */ +#define GPG_ERR_ENABLE_GETTEXT_MACROS + +/* Under WindowsCE we use the strerror replacement from libgpg-error. */ +#define GPG_ERR_ENABLE_ERRNO_MACROS + +#endif /*GNUPG_CONFIG_H_INCLUDED*/ +]) + + +AM_MAINTAINER_MODE +AC_ARG_VAR(SYSROOT,[locate config scripts also below that directory]) + +# Checks for programs. +AC_MSG_NOTICE([checking for programs]) +AC_PROG_MAKE_SET +AM_SANITY_CHECK +missing_dir=`cd $ac_aux_dir && pwd` +AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) +AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) +AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) +AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) +AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) +AM_SILENT_RULES +AC_PROG_AWK +AC_PROG_CC +AC_PROG_CPP +AM_PROG_CC_C_O +if test "x$ac_cv_prog_cc_c89" = "xno" ; then + AC_MSG_ERROR([[No C-89 compiler found]]) +fi +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_RANLIB +AC_CHECK_TOOL(AR, ar, :) +AC_PATH_PROG(PERL,"perl") +AC_CHECK_TOOL(WINDRES, windres, :) +AC_ISC_POSIX +AC_SYS_LARGEFILE +GNUPG_CHECK_USTAR + + +# We need to compile and run a program on the build machine. A +# comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in +# the AC archive is broken for autoconf 2.57. Given that there is no +# newer version of that macro, we assume that it is also broken for +# autoconf 2.61 and thus we use a simple but usually sufficient +# approach. +AC_MSG_CHECKING(for cc for build) +if test "$cross_compiling" = "yes"; then + CC_FOR_BUILD="${CC_FOR_BUILD-cc}" +else + CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" +fi +AC_MSG_RESULT($CC_FOR_BUILD) +AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler]) + +# We need to call this macro because other pkg-config macros are +# not always used. +PKG_PROG_PKG_CONFIG + + +try_gettext=yes +require_iconv=yes +have_dosish_system=no +have_w32_system=no +have_w32ce_system=no +have_android_system=no +use_simple_gettext=no +use_ldapwrapper=yes +mmap_needed=yes +case "${host}" in + *-mingw32*) + # special stuff for Windoze NT + ac_cv_have_dev_random=no + AC_DEFINE(USE_ONLY_8DOT3,1, + [Set this to limit filenames to the 8.3 format]) + AC_DEFINE(USE_SIMPLE_GETTEXT,1, + [Because the Unix gettext has too much overhead on + MingW32 systems and these systems lack Posix functions, + we use a simplified version of gettext]) + have_dosish_system=yes + have_w32_system=yes + require_iconv=no + use_ldapwrapper=no # Fixme: Do this only for CE. + case "${host}" in + *-mingw32ce*) + have_w32ce_system=yes + ;; + *) + AC_DEFINE(HAVE_DRIVE_LETTERS,1, + [Defined if the OS supports drive letters.]) + ;; + esac + try_gettext="no" + use_simple_gettext=yes + mmap_needed=no + ;; + i?86-emx-os2 | i?86-*-os2*emx ) + # OS/2 with the EMX environment + ac_cv_have_dev_random=no + AC_DEFINE(HAVE_DRIVE_LETTERS) + have_dosish_system=yes + try_gettext="no" + ;; + + i?86-*-msdosdjgpp*) + # DOS with the DJGPP environment + ac_cv_have_dev_random=no + AC_DEFINE(HAVE_DRIVE_LETTERS) + have_dosish_system=yes + try_gettext="no" + ;; + + *-*-hpux*) + if test -z "$GCC" ; then + CFLAGS="-Ae -D_HPUX_SOURCE $CFLAGS" + fi + ;; + *-dec-osf4*) + if test -z "$GCC" ; then + # Suppress all warnings + # to get rid of the unsigned/signed char mismatch warnings. + CFLAGS="-w $CFLAGS" + fi + ;; + *-dec-osf5*) + if test -z "$GCC" ; then + # Use the newer compiler `-msg_disable ptrmismatch1' to + # get rid of the unsigned/signed char mismatch warnings. + # Using this may hide other pointer mismatch warnings, but + # it at least lets other warning classes through + CFLAGS="-msg_disable ptrmismatch1 $CFLAGS" + fi + ;; + m68k-atari-mint) + ;; + *-linux-android*) + have_android_system=yes + # Android is fully utf-8 and we do not want to use iconv to + # keeps things simple + require_iconv=no + ;; + *) + ;; +esac + +if test "$have_dosish_system" = yes; then + AC_DEFINE(HAVE_DOSISH_SYSTEM,1, + [Defined if we run on some of the PCDOS like systems + (DOS, Windoze. OS/2) with special properties like + no file modes, case insensitive file names and preferred + use of backslashes as directory name separators.]) +fi +AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) + +AM_CONDITIONAL(USE_SIMPLE_GETTEXT, test x"$use_simple_gettext" = xyes) + +if test "$have_w32_system" = yes; then + AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system]) + if test "$have_w32ce_system" = yes; then + AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) + fi +fi +AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) +AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) + +if test "$have_android_system" = yes; then + AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system]) +fi +AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes) + + +# (These need to go after AC_PROG_CC so that $EXEEXT is defined) +AC_DEFINE_UNQUOTED(EXEEXT,"$EXEEXT",[The executable file extension, if any]) + + +# +# Checks for libraries. +# +AC_MSG_NOTICE([checking for libraries]) + + +# +# libgpg-error is a library with error codes shared between GnuPG +# related projects. +# +AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", + have_gpg_error=yes,have_gpg_error=no) + + +# +# Libgcrypt is our generic crypto library +# +AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION", + have_libgcrypt=yes,have_libgcrypt=no) + + +# +# libassuan is used for IPC +# +AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION", + have_libassuan=yes,have_libassuan=no) +if test "$have_libassuan" = "yes"; then + AC_DEFINE_UNQUOTED(GNUPG_LIBASSUAN_VERSION, "$libassuan_version", + [version of the libassuan library]) + show_tor_support="only .onion" +fi + + +# +# libksba is our X.509 support library +# +AM_PATH_KSBA("$NEED_KSBA_API:$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no) + + +# +# libusb allows us to use the integrated CCID smartcard reader driver. +# +# FiXME: Use GNUPG_CHECK_LIBUSB and modify to use separate AC_SUBSTs. +if test "$use_ccid_driver" = auto || test "$use_ccid_driver" = yes; then + case "${host}" in + *-mingw32*) + LIBUSB_NAME= + LIBUSB_LIBS= + LIBUSB_CPPFLAGS= + ;; + *-*-darwin*) + LIBUSB_NAME=usb-1.0 + LIBUSB_LIBS="-Wl,-framework,CoreFoundation -Wl,-framework,IOKit" + ;; + *-*-freebsd*) + # FreeBSD has a native 1.0 compatible library by -lusb. + LIBUSB_NAME=usb + LIBUSB_LIBS= + ;; + *) + LIBUSB_NAME=usb-1.0 + LIBUSB_LIBS= + ;; + esac +fi +if test x"$LIBUSB_NAME" != x ; then + AC_CHECK_LIB($LIBUSB_NAME, libusb_init, + [ LIBUSB_LIBS="-l$LIBUSB_NAME $LIBUSB_LIBS" + have_libusb=yes ]) + AC_MSG_CHECKING([libusb include dir]) + usb_incdir_found="no" + for _incdir in "" "/usr/include/libusb-1.0" "/usr/local/include/libusb-1.0"; do + _libusb_save_cppflags=$CPPFLAGS + if test -n "${_incdir}"; then + CPPFLAGS="-I${_incdir} ${CPPFLAGS}" + fi + AC_PREPROC_IFELSE([AC_LANG_SOURCE([[@%:@include ]])], + [usb_incdir=${_incdir}; usb_incdir_found="yes"], []) + CPPFLAGS=${_libusb_save_cppflags} + if test "$usb_incdir_found" = "yes"; then + break + fi + done + if test "$usb_incdir_found" = "yes"; then + AC_MSG_RESULT([${usb_incdir}]) + else + AC_MSG_RESULT([not found]) + usb_incdir="" + have_libusb=no + if test "$use_ccid_driver" != yes; then + use_ccid_driver=no + fi + LIBUSB_LIBS="" + fi + + if test "$have_libusb" = yes; then + AC_DEFINE(HAVE_LIBUSB,1, [defined if libusb is available]) + fi + if test x"$usb_incdir" = x; then + LIBUSB_CPPFLAGS="" + else + LIBUSB_CPPFLAGS="-I${usb_incdir}" + fi +fi +AC_SUBST(LIBUSB_LIBS) +AC_SUBST(LIBUSB_CPPFLAGS) + +# +# Check wether it is necessary to link against libdl. +# (For example to load libpcsclite) +# +gnupg_dlopen_save_libs="$LIBS" +LIBS="" +AC_SEARCH_LIBS(dlopen, c dl,,,) +DL_LIBS=$LIBS +AC_SUBST(DL_LIBS) +LIBS="$gnupg_dlopen_save_libs" + + +# Checks for g10 + +AC_ARG_ENABLE(sqlite, + AC_HELP_STRING([--disable-sqlite], + [disable the use of SQLITE]), + try_sqlite=$enableval, try_sqlite=yes) + +if test x"$use_tofu" = xyes ; then + if test x"$try_sqlite" = xyes ; then + PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= $NEED_SQLITE_VERSION], + [have_sqlite=yes], + [have_sqlite=no]) + fi + if test "$have_sqlite" = "yes"; then + : + AC_SUBST([SQLITE3_CFLAGS]) + AC_SUBST([SQLITE3_LIBS]) + else + use_tofu=no + tmp=$(echo "$SQLITE3_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g') + AC_MSG_WARN([[ +*** +*** Building without SQLite support - TOFU disabled +*** +*** $tmp]]) + fi +fi + +AM_CONDITIONAL(SQLITE3, test "$have_sqlite" = "yes") + +if test x"$use_tofu" = xyes ; then + AC_DEFINE(USE_TOFU, 1, [Enable to build the TOFU code]) +fi + + +# Checks for g13 + +AC_PATH_PROG(ENCFS, encfs, /usr/bin/encfs) +AC_DEFINE_UNQUOTED(ENCFS, + "${ENCFS}", [defines the filename of the encfs program]) + +AC_PATH_PROG(FUSERMOUNT, fusermount, /usr/bin/fusermount) +AC_DEFINE_UNQUOTED(FUSERMOUNT, + "${FUSERMOUNT}", [defines the filename of the fusermount program]) + + +# Checks for dirmngr + + +# +# Checks for symcryptrun: +# + +# libutil has openpty() and login_tty(). +AC_CHECK_LIB(util, openpty, + [ LIBUTIL_LIBS="$LIBUTIL_LIBS -lutil" + AC_DEFINE(HAVE_LIBUTIL,1, + [defined if libutil is available]) + ]) +AC_SUBST(LIBUTIL_LIBS) + +# shred is used to clean temporary plain text files. +AC_PATH_PROG(SHRED, shred, /usr/bin/shred) +AC_DEFINE_UNQUOTED(SHRED, + "${SHRED}", [defines the filename of the shred program]) + + +# +# Check whether the nPth library is available +# +AM_PATH_NPTH("$NEED_NPTH_API:$NEED_NPTH_VERSION",have_npth=yes,have_npth=no) +if test "$have_npth" = "yes"; then + AC_DEFINE(HAVE_NPTH, 1, + [Defined if the New Portable Thread Library is available]) + AC_DEFINE(USE_NPTH, 1, + [Defined if support for nPth is requested and nPth is available]) +else + AC_MSG_WARN([[ +*** +*** To support concurrent access for example in gpg-agent and the SCdaemon +*** we need the support of the New Portable Threads Library. +***]]) +fi + + +# +# NTBTLS is our TLS library. If it is not available fallback to +# GNUTLS. +# +AC_ARG_ENABLE(ntbtls, + AC_HELP_STRING([--disable-ntbtls], + [disable the use of NTBTLS as TLS library]), + try_ntbtls=$enableval, try_ntbtls=yes) +if test x"$try_ntbtls" = xyes ; then + AM_PATH_NTBTLS("$NEED_NTBTLS_API:$NEED_NTBTLS_VERSION", + [have_ntbtls=yes],[have_ntbtls=no]) +fi +if test "$have_ntbtls" = yes ; then + use_tls_library=ntbtls + AC_DEFINE(HTTP_USE_NTBTLS, 1, [Enable NTBTLS support in http.c]) +else + AC_ARG_ENABLE(gnutls, + AC_HELP_STRING([--disable-gnutls], + [disable GNUTLS as fallback TLS library]), + try_gnutls=$enableval, try_gnutls=yes) + if test x"$try_gnutls" = xyes ; then + PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= $NEED_GNUTLS_VERSION], + [have_gnutls=yes], + [have_gnutls=no]) + fi + if test "$have_gnutls" = "yes"; then + AC_SUBST([LIBGNUTLS_CFLAGS]) + AC_SUBST([LIBGNUTLS_LIBS]) + use_tls_library=gnutls + AC_DEFINE(HTTP_USE_GNUTLS, 1, [Enable GNUTLS support in http.c]) + else + tmp=$(echo "$LIBGNUTLS_PKG_ERRORS" | tr '\n' '\v' | sed 's/\v/\n*** /g') + AC_MSG_WARN([[ +*** +*** Building without NTBTLS and GNUTLS - no TLS access to keyservers. +*** +*** $tmp]]) + fi +fi + + +AC_MSG_NOTICE([checking for networking options]) + +# +# Must check for network library requirements before doing link tests +# for ldap, for example. If ldap libs are static (or dynamic and without +# ELF runtime link paths), then link will fail and LDAP support won't +# be detected. +# +AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname, + [NETLIBS="-lnsl $NETLIBS"])) +AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt, + [NETLIBS="-lsocket $NETLIBS"])) + + +# +# Check standard resolver functions. +# +if test "$build_dirmngr" = "yes"; then + _dns_save_libs=$LIBS + LIBS="" + + # Find the system resolver which can always be enabled with + # the dirmngr option --standard-resolver. + + # the double underscore thing is a glibc-ism? + AC_SEARCH_LIBS(res_query,resolv bind,, + AC_SEARCH_LIBS(__res_query,resolv bind,,have_resolver=no)) + AC_SEARCH_LIBS(dn_expand,resolv bind,, + AC_SEARCH_LIBS(__dn_expand,resolv bind,,have_resolver=no)) + + # macOS renames dn_skipname into res_9_dn_skipname in , + # and for some reason fools us into believing we don't need + # -lresolv even if we do. Since the test program checking for the + # symbol does not include , we need to check for the + # renamed symbol explicitly. + AC_SEARCH_LIBS(res_9_dn_skipname,resolv bind,, + AC_SEARCH_LIBS(dn_skipname,resolv bind,, + AC_SEARCH_LIBS(__dn_skipname,resolv bind,,have_resolver=no))) + + if test x"$have_resolver" != xno ; then + + # Make sure that the BIND 4 resolver interface is workable before + # enabling any code that calls it. At some point I'll rewrite the + # code to use the BIND 8 resolver API. + # We might also want to use libdns instead. + + AC_MSG_CHECKING([whether the resolver is usable]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include +#include +#include +#include ]], +[[unsigned char answer[PACKETSZ]; + res_query("foo.bar",C_IN,T_A,answer,PACKETSZ); + dn_skipname(0,0); + dn_expand(0,0,0,0,0); +]])],have_resolver=yes,have_resolver=no) + AC_MSG_RESULT($have_resolver) + + # This is Apple-specific and somewhat bizarre as they changed the + # define in bind 8 for some reason. + + if test x"$have_resolver" != xyes ; then + AC_MSG_CHECKING( + [whether I can make the resolver usable with BIND_8_COMPAT]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define BIND_8_COMPAT +#include +#include +#include +#include ]], +[[unsigned char answer[PACKETSZ]; + res_query("foo.bar",C_IN,T_A,answer,PACKETSZ); + dn_skipname(0,0); dn_expand(0,0,0,0,0); +]])],[have_resolver=yes ; need_compat=yes]) + AC_MSG_RESULT($have_resolver) + fi + fi + + if test x"$have_resolver" = xyes ; then + AC_DEFINE(HAVE_SYSTEM_RESOLVER,1,[The system's resolver is usable.]) + DNSLIBS="$DNSLIBS $LIBS" + if test x"$need_compat" = xyes ; then + AC_DEFINE(BIND_8_COMPAT,1,[an Apple OSXism]) + fi + if test "$use_libdns" = yes; then + show_tor_support=yes + fi + elif test "$use_libdns" = yes; then + show_tor_support=yes + else + AC_MSG_WARN([[ +*** +*** The system's DNS resolver is not usable. +*** Dirmngr functionality is limited. +***]]) + show_tor_support="${show_tor_support} (no system resolver)" + fi + + if test "$have_w32_system" = yes; then + if test "$use_libdns" = yes; then + DNSLIBS="$DNSLIBS -liphlpapi" + fi + fi + + LIBS=$_dns_save_libs +fi + +AC_SUBST(DNSLIBS) + + +# +# Check for LDAP +# +# Note that running the check changes the variable +# gnupg_have_ldap from "n/a" to "no" or "yes". + +AC_ARG_ENABLE(ldap, + AC_HELP_STRING([--disable-ldap],[disable LDAP support]), + [if test "$enableval" = "no"; then gnupg_have_ldap=no; fi]) + +if test "$gnupg_have_ldap" != "no" ; then + if test "$build_dirmngr" = "yes" ; then + GNUPG_CHECK_LDAP($NETLIBS) + AC_CHECK_LIB(lber, ber_free, + [ LBER_LIBS="$LBER_LIBS -llber" + AC_DEFINE(HAVE_LBER,1, + [defined if liblber is available]) + have_lber=yes + ]) + fi +fi +AC_SUBST(LBER_LIBS) +if test "$gnupg_have_ldap" = "no"; then + AC_MSG_WARN([[ +*** +*** Building without LDAP support. +*** No CRL access or X.509 certificate search available. +***]]) +fi + +AM_CONDITIONAL(USE_LDAP, [test "$gnupg_have_ldap" = yes]) +if test "$gnupg_have_ldap" = yes ; then + AC_DEFINE(USE_LDAP,1,[Defined if LDAP is support]) +else + use_ldapwrapper=no +fi + +if test "$use_ldapwrapper" = yes; then + AC_DEFINE(USE_LDAPWRAPPER,1, [Build dirmngr with LDAP wrapper process]) +fi +AM_CONDITIONAL(USE_LDAPWRAPPER, test "$use_ldapwrapper" = yes) + + + + +# +# Check for sendmail +# +# This isn't necessarily sendmail itself, but anything that gives a +# sendmail-ish interface to the outside world. That includes Exim, +# Postfix, etc. Basically, anything that can handle "sendmail -t". +AC_ARG_WITH(mailprog, + AC_HELP_STRING([--with-mailprog=NAME], + [use "NAME -t" for mail transport]), + ,with_mailprog=yes) +if test x"$with_mailprog" = xyes ; then + AC_PATH_PROG(SENDMAIL,sendmail,,$PATH:/usr/sbin:/usr/libexec:/usr/lib) +elif test x"$with_mailprog" != xno ; then + AC_MSG_CHECKING([for a mail transport program]) + AC_SUBST(SENDMAIL,$with_mailprog) + AC_MSG_RESULT($with_mailprog) +fi + + +# +# Construct a printable name of the OS +# +case "${host}" in + *-mingw32ce*) + PRINTABLE_OS_NAME="W32CE" + ;; + *-mingw32*) + PRINTABLE_OS_NAME="MingW32" + ;; + *-*-cygwin*) + PRINTABLE_OS_NAME="Cygwin" + ;; + i?86-emx-os2 | i?86-*-os2*emx ) + PRINTABLE_OS_NAME="OS/2" + ;; + i?86-*-msdosdjgpp*) + PRINTABLE_OS_NAME="MSDOS/DJGPP" + try_dynload=no + ;; + *-linux*) + PRINTABLE_OS_NAME="GNU/Linux" + ;; + *) + PRINTABLE_OS_NAME=`uname -s || echo "Unknown"` + ;; +esac +AC_DEFINE_UNQUOTED(PRINTABLE_OS_NAME, "$PRINTABLE_OS_NAME", + [A human readable text with the name of the OS]) + + +# +# Checking for iconv +# +if test "$require_iconv" = yes; then + AM_ICONV +else + LIBICONV= + LTLIBICONV= + AC_SUBST(LIBICONV) + AC_SUBST(LTLIBICONV) +fi + + +# +# Check for gettext +# +# This is "GNU gnupg" - The project-id script from gettext +# needs this string +# +AC_MSG_NOTICE([checking for gettext]) +AM_PO_SUBDIRS +AM_GNU_GETTEXT_VERSION([0.17]) +if test "$try_gettext" = yes; then + AM_GNU_GETTEXT([external],[need-ngettext]) + + # gettext requires some extra checks. These really should be part of + # the basic AM_GNU_GETTEXT macro. TODO: move other gettext-specific + # function checks to here. + + AC_CHECK_FUNCS(strchr) +else + USE_NLS=no + USE_INCLUDED_LIBINTL=no + BUILD_INCLUDED_LIBINTL=no + POSUB=po + AC_SUBST(USE_NLS) + AC_SUBST(USE_INCLUDED_LIBINTL) + AC_SUBST(BUILD_INCLUDED_LIBINTL) + AC_SUBST(POSUB) +fi + +# We use HAVE_LANGINFO_CODESET in a couple of places. +AM_LANGINFO_CODESET + +# Checks required for our use of locales +gt_LC_MESSAGES + + +# +# SELinux support +# +if test "$selinux_support" = yes ; then + AC_DEFINE(ENABLE_SELINUX_HACKS,1,[Define to enable SELinux support]) +fi + + +# +# Checks for header files. +# +AC_MSG_NOTICE([checking for header files]) +AC_HEADER_STDC +AC_CHECK_HEADERS([string.h unistd.h langinfo.h termio.h locale.h getopt.h \ + pty.h utmp.h pwd.h inttypes.h signal.h sys/select.h \ + signal.h]) +AC_HEADER_TIME + + +# +# Checks for typedefs, structures, and compiler characteristics. +# +AC_MSG_NOTICE([checking for system characteristics]) +AC_C_CONST +AC_C_INLINE +AC_C_VOLATILE +AC_TYPE_SIZE_T +AC_TYPE_MODE_T +AC_TYPE_SIGNAL +AC_DECL_SYS_SIGLIST + +gl_HEADER_SYS_SOCKET +gl_TYPE_SOCKLEN_T + +AC_SEARCH_LIBS([inet_addr], [nsl]) + +AC_ARG_ENABLE(endian-check, + AC_HELP_STRING([--disable-endian-check], + [disable the endian check and trust the OS provided macros]), + endiancheck=$enableval,endiancheck=yes) + +if test x"$endiancheck" = xyes ; then + GNUPG_CHECK_ENDIAN +fi + +# fixme: we should get rid of the byte type +GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF) +GNUPG_CHECK_TYPEDEF(ushort, HAVE_USHORT_TYPEDEF) +GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF) +GNUPG_CHECK_TYPEDEF(u16, HAVE_U16_TYPEDEF) +GNUPG_CHECK_TYPEDEF(u32, HAVE_U32_TYPEDEF) + +AC_CHECK_SIZEOF(unsigned short) +AC_CHECK_SIZEOF(unsigned int) +AC_CHECK_SIZEOF(unsigned long) +AC_CHECK_SIZEOF(unsigned long long) +AC_HEADER_TIME +AC_CHECK_SIZEOF(time_t,,[[ +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +]]) +GNUPG_TIME_T_UNSIGNED + + +if test "$ac_cv_sizeof_unsigned_short" = "0" \ + || test "$ac_cv_sizeof_unsigned_int" = "0" \ + || test "$ac_cv_sizeof_unsigned_long" = "0"; then + AC_MSG_WARN([Hmmm, something is wrong with the sizes - using defaults]); +fi + + +# +# Checks for library functions. +# +AC_MSG_NOTICE([checking for library functions]) +AC_CHECK_DECLS(getpagesize) +AC_FUNC_FSEEKO +AC_FUNC_VPRINTF +AC_FUNC_FORK +AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap canonicalize_file_name]) +AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r strtoull]) +AC_CHECK_FUNCS([setenv unsetenv fcntl ftruncate inet_ntop]) +AC_CHECK_FUNCS([canonicalize_file_name]) +AC_CHECK_FUNCS([gettimeofday getrusage getrlimit setrlimit clock_gettime]) +AC_CHECK_FUNCS([atexit raise getpagesize strftime nl_langinfo setlocale]) +AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe getaddrinfo]) +AC_CHECK_FUNCS([ttyname rand ftello fsync stat lstat]) +AC_CHECK_FUNCS([memicmp stpcpy strsep strlwr strtoul memmove stricmp strtol \ + memrchr isascii timegm getrusage setrlimit stat setlocale \ + flockfile funlockfile getpwnam getpwuid \ + getenv inet_pton strpbrk]) + +# On some systems (e.g. Solaris) nanosleep requires linking to librl. +# Given that we use nanosleep only as an optimization over a select +# based wait function we want it only if it is available in libc. +_save_libs="$LIBS" +AC_SEARCH_LIBS([nanosleep], [], + [AC_DEFINE(HAVE_NANOSLEEP,1, + [Define to 1 if you have the `nanosleep' function in libc.])]) +LIBS="$_save_libs" + + +# See whether libc supports the Linux inotify interface +case "${host}" in + *-*-linux*) + AC_CHECK_FUNCS([inotify_init]) + ;; +esac + + +if test "$have_android_system" = yes; then + # On Android ttyname is a stub but prints an error message. + AC_DEFINE(HAVE_BROKEN_TTYNAME,1, + [Defined if ttyname does not work properly]) +fi + +AC_CHECK_TYPES([struct sigaction, sigset_t],,,[#include ]) + +# Dirmngr requires mmap on Unix systems. +if test $ac_cv_func_mmap != yes -a $mmap_needed = yes; then + AC_MSG_ERROR([[Sorry, the current implemenation requires mmap.]]) +fi + +# +# W32 specific test +# +GNUPG_FUNC_MKDIR_TAKES_ONE_ARG + +# +# Sanity check regex. Tests adapted from mutt. +# +AC_MSG_CHECKING([whether regular expression support is requested]) +AC_ARG_ENABLE(regex, + AC_HELP_STRING([--disable-regex], + [do not handle regular expressions in trust signatures]), + use_regex=$enableval, use_regex=yes) +AC_MSG_RESULT($use_regex) + +if test "$use_regex" = yes ; then + _cppflags="${CPPFLAGS}" + _ldflags="${LDFLAGS}" + AC_ARG_WITH(regex, + AC_HELP_STRING([--with-regex=DIR],[look for regex in DIR]), + [ + if test -d "$withval" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + ],withval="") + + # Does the system have regex functions at all? + AC_SEARCH_LIBS([regcomp], [regex]) + AC_CHECK_FUNC(regcomp, gnupg_cv_have_regex=yes, gnupg_cv_have_regex=no) + + if test $gnupg_cv_have_regex = no; then + use_regex=no + else + if test x"$cross_compiling" = xyes; then + AC_MSG_WARN([cross compiling; assuming regexp libray is not broken]) + else + AC_CACHE_CHECK([whether your system's regexp library is broken], + [gnupg_cv_regex_broken], + AC_TRY_RUN([ +#include +#include +main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }], + gnupg_cv_regex_broken=no, gnupg_cv_regex_broken=yes, gnupg_cv_regex_broken=yes)) + + if test $gnupg_cv_regex_broken = yes; then + AC_MSG_WARN([your regex is broken - disabling regex use]) + use_regex=no + fi + fi + fi + CPPFLAGS="${_cppflags}" + LDFLAGS="${_ldflags}" +fi + +if test "$use_regex" != yes ; then + AC_DEFINE(DISABLE_REGEX,1, [Define to disable regular expression support]) +fi +AM_CONDITIONAL(DISABLE_REGEX, test x"$use_regex" != xyes) + + + +# +# Do we have zlib? Must do it here because Solaris failed +# when compiling a conftest (due to the "-lz" from LIBS). +# Note that we combine zlib and bzlib2 in ZLIBS. +# +if test "$use_zip" = yes ; then + _cppflags="${CPPFLAGS}" + _ldflags="${LDFLAGS}" + AC_ARG_WITH(zlib, + [ --with-zlib=DIR use libz in DIR],[ + if test -d "$withval"; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + ]) + + AC_CHECK_HEADER(zlib.h, + AC_CHECK_LIB(z, deflateInit2_, + [ + ZLIBS="-lz" + AC_DEFINE(HAVE_ZIP,1, [Defined if ZIP and ZLIB are supported]) + ], + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}), + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}) +fi + + +# +# Check whether we can support bzip2 +# +if test "$use_bzip2" = yes ; then + _cppflags="${CPPFLAGS}" + _ldflags="${LDFLAGS}" + AC_ARG_WITH(bzip2, + AC_HELP_STRING([--with-bzip2=DIR],[look for bzip2 in DIR]), + [ + if test -d "$withval" ; then + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + fi + ],withval="") + + # Checking alongside stdio.h as an early version of bzip2 (1.0) + # required stdio.h to be included before bzlib.h, and Solaris 9 is + # woefully out of date. + if test "$withval" != no ; then + AC_CHECK_HEADER(bzlib.h, + AC_CHECK_LIB(bz2,BZ2_bzCompressInit, + [ + have_bz2=yes + ZLIBS="$ZLIBS -lbz2" + AC_DEFINE(HAVE_BZIP2,1, + [Defined if the bz2 compression library is available]) + ], + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}), + CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags},[#include ]) + fi +fi +AM_CONDITIONAL(ENABLE_BZIP2_SUPPORT,test x"$have_bz2" = "xyes") +AC_SUBST(ZLIBS) + + +# Check for readline support +GNUPG_CHECK_READLINE + + +if test "$development_version" = yes; then + AC_DEFINE(IS_DEVELOPMENT_VERSION,1, + [Defined if this is not a regular release]) +fi + +AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes) + +GNUPG_CHECK_GNUMAKE + +# Add some extra libs here so that previous tests don't fail for +# mysterious reasons - the final link step should bail out. +# W32SOCKLIBS is also defined so that if can be used for tools not +# requiring any network stuff but linking to code in libcommon which +# tracks in winsock stuff (e.g. init_common_subsystems). +if test "$have_w32_system" = yes; then + if test "$have_w32ce_system" = yes; then + W32SOCKLIBS="-lws2" + else + W32SOCKLIBS="-lws2_32" + fi + NETLIBS="${NETLIBS} ${W32SOCKLIBS}" +fi + +AC_SUBST(NETLIBS) +AC_SUBST(W32SOCKLIBS) + +# +# Setup gcc specific options +# +USE_C99_CFLAGS= +AC_MSG_NOTICE([checking for cc features]) +if test "$GCC" = yes; then + mycflags= + mycflags_save=$CFLAGS + + # Check whether gcc does not emit a diagnositc for unknow -Wno-* + # options. This is the case for gcc >= 4.6 + AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) +#kickerror +#endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no]) + AC_MSG_RESULT($_gcc_silent_wno) + + # Note that it is okay to use CFLAGS here because these are just + # warning options and the user should have a chance of overriding + # them. + if test "$USE_MAINTAINER_MODE" = "yes"; then + mycflags="$mycflags -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes" + mycflags="$mycflags -Wformat -Wno-format-y2k -Wformat-security" + if test x"$_gcc_silent_wno" = xyes ; then + _gcc_wopt=yes + else + AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) + CFLAGS="-Wno-missing-field-initializers" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], + [_gcc_wopt=yes],[_gcc_wopt=no]) + AC_MSG_RESULT($_gcc_wopt) + fi + if test x"$_gcc_wopt" = xyes ; then + mycflags="$mycflags -W -Wno-sign-compare" + mycflags="$mycflags -Wno-missing-field-initializers" + fi + + AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement]) + CFLAGS="-Wdeclaration-after-statement" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) + AC_MSG_RESULT($_gcc_wopt) + if test x"$_gcc_wopt" = xyes ; then + mycflags="$mycflags -Wdeclaration-after-statement" + fi + else + mycflags="$mycflags -Wall" + fi + + if test x"$_gcc_silent_wno" = xyes ; then + _gcc_psign=yes + else + AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign]) + CFLAGS="-Wno-pointer-sign" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], + [_gcc_psign=yes],[_gcc_psign=no]) + AC_MSG_RESULT($_gcc_psign) + fi + if test x"$_gcc_psign" = xyes ; then + mycflags="$mycflags -Wno-pointer-sign" + fi + + AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) + CFLAGS="-Wpointer-arith" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no) + AC_MSG_RESULT($_gcc_psign) + if test x"$_gcc_psign" = xyes ; then + mycflags="$mycflags -Wpointer-arith" + fi + + CFLAGS="$mycflags $mycflags_save" + if test "$use_libdns" = yes; then + # dirmngr/dns.{c,h} require C99 and GNU extensions. */ + USE_C99_CFLAGS="-std=gnu99" + fi +fi + +AC_SUBST(USE_C99_CFLAGS) + + +# +# This is handy for debugging so the compiler doesn't rearrange +# things and eliminate variables. +# +AC_ARG_ENABLE(optimization, + AC_HELP_STRING([--disable-optimization], + [disable compiler optimization]), + [if test $enableval = no ; then + CFLAGS=`echo $CFLAGS | sed s/-O[[1-9]]\ /-O0\ /g` + fi]) + +# +# Add user CFLAGS. +# +CFLAGS="$CFLAGS $CFLAGS_orig" + +# +# Decide what to build +# + +build_scdaemon_extra="" +if test "$build_scdaemon" = "yes"; then + if test $have_libusb = no; then + build_scdaemon_extra="without internal CCID driver" + fi + if test -n "$build_scdaemon_extra"; then + build_scdaemon_extra="(${build_scdaemon_extra})" + fi +fi + + +# +# Set variables for use by automake makefiles. +# +AM_CONDITIONAL(BUILD_GPG, test "$build_gpg" = "yes") +AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes") +AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes") +AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes") +AM_CONDITIONAL(BUILD_G13, test "$build_g13" = "yes") +AM_CONDITIONAL(BUILD_DIRMNGR, test "$build_dirmngr" = "yes") +AM_CONDITIONAL(BUILD_TOOLS, test "$build_tools" = "yes") +AM_CONDITIONAL(BUILD_DOC, test "$build_doc" = "yes") +AM_CONDITIONAL(BUILD_SYMCRYPTRUN, test "$build_symcryptrun" = "yes") +AM_CONDITIONAL(BUILD_GPGTAR, test "$build_gpgtar" = "yes") +AM_CONDITIONAL(BUILD_WKS_TOOLS, test "$build_wks_tools" = "yes") + +AM_CONDITIONAL(ENABLE_CARD_SUPPORT, test "$card_support" = yes) +AM_CONDITIONAL(NO_TRUST_MODELS, test "$use_trust_models" = no) +AM_CONDITIONAL(USE_TOFU, test "$use_tofu" = yes) + +# +# Set some defines for use gpgconf. +# +if test "$build_gpg" = yes ; then + AC_DEFINE(BUILD_WITH_GPG,1,[Defined if GPG is to be build]) +fi +if test "$build_gpgsm" = yes ; then + AC_DEFINE(BUILD_WITH_GPGSM,1,[Defined if GPGSM is to be build]) +fi +if test "$build_agent" = yes ; then + AC_DEFINE(BUILD_WITH_AGENT,1,[Defined if GPG-AGENT is to be build]) +fi +if test "$build_scdaemon" = yes ; then + AC_DEFINE(BUILD_WITH_SCDAEMON,1,[Defined if SCDAEMON is to be build]) +fi +if test "$build_dirmngr" = yes ; then + AC_DEFINE(BUILD_WITH_DIRMNGR,1,[Defined if SCDAEMON is to be build]) +fi +if test "$build_g13" = yes ; then + AC_DEFINE(BUILD_WITH_G13,1,[Defined if G13 is to be build]) +fi + + +# +# Define Name strings +# +AC_DEFINE_UNQUOTED(GNUPG_NAME, "GnuPG", [The name of the project]) + +AC_DEFINE_UNQUOTED(GPG_NAME, "gpg", [The name of the OpenPGP tool]) +AC_DEFINE_UNQUOTED(GPG_DISP_NAME, "GnuPG", [The displayed name of gpg]) + +AC_DEFINE_UNQUOTED(GPGSM_NAME, "gpgsm", [The name of the S/MIME tool]) +AC_DEFINE_UNQUOTED(GPGSM_DISP_NAME, "GPGSM", [The displayed name of gpgsm]) + +AC_DEFINE_UNQUOTED(GPG_AGENT_NAME, "gpg-agent", [The name of the agent]) +AC_DEFINE_UNQUOTED(GPG_AGENT_DISP_NAME, "GPG Agent", + [The displayed name of gpg-agent]) + +AC_DEFINE_UNQUOTED(SCDAEMON_NAME, "scdaemon", [The name of the scdaemon]) +AC_DEFINE_UNQUOTED(SCDAEMON_DISP_NAME, "SCDaemon", + [The displayed name of scdaemon]) + +AC_DEFINE_UNQUOTED(DIRMNGR_NAME, "dirmngr", [The name of the dirmngr]) +AC_DEFINE_UNQUOTED(DIRMNGR_DISP_NAME, "DirMngr", + [The displayed name of dirmngr]) + +AC_DEFINE_UNQUOTED(G13_NAME, "g13", [The name of the g13 tool]) +AC_DEFINE_UNQUOTED(G13_DISP_NAME, "G13", [The displayed name of g13]) + +AC_DEFINE_UNQUOTED(GPGCONF_NAME, "gpgconf", [The name of the gpgconf tool]) +AC_DEFINE_UNQUOTED(GPGCONF_DISP_NAME, "GPGConf", + [The displayed name of gpgconf]) + +AC_DEFINE_UNQUOTED(GPGTAR_NAME, "gpgtar", [The name of the gpgtar tool]) + +AC_DEFINE_UNQUOTED(GPG_AGENT_SOCK_NAME, "S.gpg-agent", + [The name of the agent socket]) +AC_DEFINE_UNQUOTED(GPG_AGENT_EXTRA_SOCK_NAME, "S.gpg-agent.extra", + [The name of the agent socket for remote access]) +AC_DEFINE_UNQUOTED(GPG_AGENT_BROWSER_SOCK_NAME, "S.gpg-agent.browser", + [The name of the agent socket for browsers]) +AC_DEFINE_UNQUOTED(GPG_AGENT_SSH_SOCK_NAME, "S.gpg-agent.ssh", + [The name of the agent socket for ssh]) +AC_DEFINE_UNQUOTED(DIRMNGR_INFO_NAME, "DIRMNGR_INFO", + [The name of the dirmngr info envvar]) +AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon", + [The name of the SCdaemon socket]) +AC_DEFINE_UNQUOTED(DIRMNGR_SOCK_NAME, "S.dirmngr", + [The name of the dirmngr socket]) +AC_DEFINE_UNQUOTED(DIRMNGR_DEFAULT_KEYSERVER, + "hkps://hkps.pool.sks-keyservers.net", + [The default keyserver for dirmngr to use, if none is explicitly given]) + +AC_DEFINE_UNQUOTED(GPGEXT_GPG, "gpg", [The standard binary file suffix]) + +if test "$have_w32_system" = yes; then + AC_DEFINE_UNQUOTED(GNUPG_REGISTRY_DIR, "\\\\Software\\\\GNU\\\\GnuPG", + [The directory part of the W32 registry keys]) +fi + + +# +# Provide information about the build. +# +BUILD_REVISION="mym4_revision" +AC_SUBST(BUILD_REVISION) +AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", + [GIT commit id revision used to build this package]) + +changequote(,)dnl +BUILD_VERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./'` +changequote([,])dnl +BUILD_VERSION="${BUILD_VERSION}mym4_revision_dec" +BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,` +AC_SUBST(BUILD_VERSION) +AC_SUBST(BUILD_FILEVERSION) + +AC_ARG_ENABLE([build-timestamp], + AC_HELP_STRING([--enable-build-timestamp], + [set an explicit build timestamp for reproducibility. + (default is the current time in ISO-8601 format)]), + [if test "$enableval" = "yes"; then + BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` + else + BUILD_TIMESTAMP="$enableval" + fi + BUILD_HOSTNAME="$ac_hostname"], + [BUILD_TIMESTAMP="" + BUILD_HOSTNAME=""]) +AC_SUBST(BUILD_TIMESTAMP) +AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", + [The time this package was configured for a build]) +AC_SUBST(BUILD_HOSTNAME) + + +# +# Print errors here so that they are visible all +# together and the user can acquire them all together. +# +die=no +if test "$have_gpg_error" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libgpg-error to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libgpg-error +*** (at least version $NEED_GPG_ERROR_VERSION is required.) +***]]) +fi +if test "$have_libgcrypt" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libgcrypt to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libgcrypt/ +*** (at least version $NEED_LIBGCRYPT_VERSION (API $NEED_LIBGCRYPT_API) is required.) +***]]) +fi +if test "$have_libassuan" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libassuan to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libassuan/ +*** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required). +***]]) +fi +if test "$have_ksba" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libksba to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/libksba/ +*** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required). +***]]) +fi +if test "$gnupg_have_ldap" = yes; then + if test "$have_w32ce_system" = yes; then + AC_MSG_NOTICE([[ +*** Note that CeGCC might be broken, a package fixing this is: +*** http://files.kolab.org/local/windows-ce/ +*** source/wldap32_0.1-mingw32ce.orig.tar.gz +*** binary/wldap32-ce-arm-dev_0.1-1_all.deb +***]]) + fi +fi +if test "$have_npth" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** It is now required to build with support for the +*** New Portable Threads Library (nPth). Please install this +*** library first. The library is for example available at +*** ftp://ftp.gnupg.org/gcrypt/npth/ +*** (at least version $NEED_NPTH_VERSION (API $NEED_NPTH_API) is required). +***]]) +fi + +if test "$require_iconv" = yes; then + if test "$am_func_iconv" != yes; then + die=yes + AC_MSG_NOTICE([[ +*** +*** The system does not provide a working iconv function. Please +*** install a suitable library; for example GNU Libiconv which is +*** available at: +*** http://ftp.gnu.org/gnu/libiconv/ +***]]) + fi +fi + +if test "$use_ccid_driver" = yes; then + if test "$have_libusb" != yes; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libusb to build the internal ccid driver. Please +*** install a libusb suitable for your system. +***]]) + fi +fi + +if test "$die" = "yes"; then + AC_MSG_ERROR([[ +*** +*** Required libraries not found. Please consult the above messages +*** and install them before running configure again. +***]]) +fi + + + +AC_CONFIG_FILES([ m4/Makefile +Makefile +po/Makefile.in +common/Makefile +common/w32info-rc.h +kbx/Makefile +g10/Makefile +sm/Makefile +agent/Makefile +scd/Makefile +g13/Makefile +dirmngr/Makefile +tools/gpg-zip +tools/Makefile +doc/Makefile +tests/Makefile +tests/gpgscm/Makefile +tests/openpgp/Makefile +tests/migrations/Makefile +tests/gpgme/Makefile +tests/pkits/Makefile +g10/gpg.w32-manifest +]) + + +AC_OUTPUT + + +echo " + GnuPG v${VERSION} has been configured as follows: + + Revision: mym4_revision (mym4_revision_dec) + Platform: $PRINTABLE_OS_NAME ($host) + + OpenPGP: $build_gpg + S/MIME: $build_gpgsm + Agent: $build_agent + Smartcard: $build_scdaemon $build_scdaemon_extra + G13: $build_g13 + Dirmngr: $build_dirmngr + Gpgtar: $build_gpgtar + WKS tools: $build_wks_tools + + Protect tool: $show_gnupg_protect_tool_pgm + LDAP wrapper: $show_gnupg_dirmngr_ldap_pgm + Default agent: $show_gnupg_agent_pgm + Default pinentry: $show_gnupg_pinentry_pgm + Default scdaemon: $show_gnupg_scdaemon_pgm + Default dirmngr: $show_gnupg_dirmngr_pgm + + Dirmngr auto start: $dirmngr_auto_start + Readline support: $gnupg_cv_have_readline + LDAP support: $gnupg_have_ldap + TLS support: $use_tls_library + TOFU support: $use_tofu + Tor support: $show_tor_support +" +if test x"$use_regex" != xyes ; then +echo " + Warning: No regular expression support available. + OpenPGP trust signatures won't work. + gpg-check-pattern will not be built. +" +fi +if test "x${gpg_config_script_warn}" != x; then +cat < + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-11-24 Werner Koch + + * ks-engine-http.c (ks_http_help): Do not print help for hkp. + * ks-engine-hkp.c (ks_hkp_help): Print help only for hkp. + (send_request): Remove test code. + (map_host): Use xtrymalloc. + + * certcache.c (classify_pattern): Remove unused variable and make + explicit substring search work. + +2011-06-01 Marcus Brinkmann + + * Makefile.am (dirmngr_ldap_CFLAGS): Add $(LIBGCRYPT_CFLAGS), + which is needed by common/util.h. + +2011-04-25 Werner Koch + + * ks-engine-hkp.c (ks_hkp_search): Mark classify_user_id for use + with OpenPGP. + (ks_hkp_get): Ditto. + +2011-04-12 Werner Koch + + * ks-engine-hkp.c (ks_hkp_search, ks_hkp_get, ks_hkp_put): Factor + code out to .. + (make_host_part): new. + (hostinfo_s): New. + (create_new_hostinfo, find_hostinfo, sort_hostpool) + (select_random_host, map_host, mark_host_dead) + (ks_hkp_print_hosttable): New. + +2011-02-23 Werner Koch + + * certcache.c (get_cert_bysubject): Take care of a NULL argument. + (find_cert_bysubject): Ditto. Fixes bug#1300. + +2011-02-09 Werner Koch + + * ks-engine-kdns.c: New but only the framework. + + * server.c (cmd_keyserver): Add option --help. + (dirmngr_status_help): New. + * ks-action.c (ks_print_help): New. + (ks_action_help): New. + * ks-engine-finger.c (ks_finger_help): New. + * ks-engine-http.c (ks_http_help): New. + * ks-engine-hkp.c (ks_hkp_help): New. + + * ks-action.c (ks_action_fetch): Support http URLs. + * ks-engine-http.c: New. + + * ks-engine-finger.c (ks_finger_get): Rename to ks_finger_fetch. + Change caller. + +2011-02-08 Werner Koch + + * server.c (cmd_ks_fetch): New. + * ks-action.c (ks_action_fetch): New. + * ks-engine-finger.c: New. + +2011-02-03 Werner Koch + + * Makefile.am (dirmngr_LDADD): Remove -llber. + +2011-01-25 Werner Koch + + * dirmngr.c (handle_connections): Rewrite loop to use pth-select + so to sync timeouts to the full second. + (pth_thread_id): New. + (main) [W32CE]: Fix setting of default homedir. + + * ldap-wrapper.c (ldap_wrapper_thread): Sync to the full second. + Increate pth_wait timeout from 1 to 2 seconds. + +2011-01-20 Werner Koch + + * server.c (release_ctrl_keyservers): New. + (cmd_keyserver, cmd_ks_seach, cmd_ks_get, cmd_ks_put): New. + * dirmngr.h (uri_item_t): New. + (struct server_control_s): Add field KEYSERVERS. + * ks-engine-hkp.c: New. + * ks-engine.h: New. + * ks-action.c, ks-action.h: New. + * server.c: Include ks-action.h. + (cmd_ks_search): New. + * Makefile.am (dirmngr_SOURCES): Add new files. + +2011-01-19 Werner Koch + + * dirmngr.c (main): Use es_printf for --gpgconf-list. + +2010-12-14 Werner Koch + + * cdb.h (struct cdb) [W32]: Add field CDB_MAPPING. + * cdblib.c (cdb_init) [W32]: Save mapping handle. + (cdb_free) [W32]: Don't leak the mapping handle from cdb_init by + using the saved one. + + * crlcache.c (crl_cache_insert): Close unused matching files. + + * dirmngr.c (main) [W32CE]: Change homedir in daemon mode to /gnupg. + +2010-12-07 Werner Koch + + * dirmngr.c (TIMERTICK_INTERVAL) [W32CE]: Change to 60s. + +2010-11-23 Werner Koch + + * Makefile.am (dirmngr_LDFLAGS): Add extra_bin_ldflags. + (dirmngr_client_LDFLAGS): Ditto. + +2010-10-21 Werner Koch + + * dirmngr.c (main): Changed faked system time warning + +2010-10-15 Werner Koch + + * Makefile.am (CLEANFILES): Add no-libgcrypt.c. + +2010-09-16 Werner Koch + + * validate.c (validate_cert_chain): Use GPG_ERR_MISSING_ISSUER_CERT. + +2010-08-13 Werner Koch + + * Makefile.am (dirmngr_SOURCES): Add w32-ldap-help.h. + + * dirmngr_ldap.c (fetch_ldap): Call ldap_unbind. + + * w32-ldap-help.h: New. + * dirmngr_ldap.c [W32CE]: Include w32-ldap-help.h and use the + mapped ldap functions. + +2010-08-12 Werner Koch + + * crlcache.c (update_dir, crl_cache_insert): s/unlink/gnupg_remove/. + + * dirmngr.c (dirmngr_sighup_action): New. + + * server.c (cmd_killdirmngr, cmd_reloaddirmngr): New. + (struct server_local_s): Add field STOPME. + (start_command_handler): Act on STOPME. + +2010-08-06 Werner Koch + + * dirmngr.c (JNLIB_NEED_AFLOCAL): Define macro. + (main): Use SUN_LEN macro. + (main) [W32]: Allow EEXIST in addition to EADDRINUSE. + +2010-08-05 Werner Koch + + * server.c (set_error, leave_cmd): New. + (cmd_validate, cmd_ldapserver, cmd_isvalid, cmd_checkcrl) + (cmd_checkocsp, cmd_lookup, cmd_listcrls, cmd_cachecert): Use + leave_cmd. + (cmd_getinfo): New. + (data_line_cookie_write, data_line_cookie_close): New. + (cmd_listcrls): Replace assuan_get_data_fp by es_fopencookie. + + * misc.c (create_estream_ksba_reader, my_estream_ksba_reader_cb): New. + * certcache.c (load_certs_from_dir): Use create_estream_ksba_reader. + * crlcache.c (crl_cache_load): Ditto. + +2010-08-03 Werner Koch + + * dirmngr_ldap.c (pth_enter, pth_leave) [USE_LDAPWRAPPER]: Turn + into functions for use in a 'for' control stmt. + +2010-07-26 Werner Koch + + * dirmngr_ldap.c (print_ldap_entries): Remove special fwrite case + for W32 because that is now handles by estream. + +2010-07-25 Werner Koch + + * Makefile.am (dirmngr_SOURCES) [!USE_LDAPWRAPPER]: Build + ldap-wrapper-ce. + * ldap-wrapper-ce.c: New. + + * dirmngr_ldap.c (opt): Remove global variable ... + (my_opt_t): ... and declare a type instead. + (main): Define a MY_OPT variable and change all references to OPT + to this. + (set_timeout, print_ldap_entries, fetch_ldap, process_url): Pass + MYOPT arg. + +2010-07-24 Werner Koch + + * dirmngr_ldap.c (main): Init common subsystems. Call + es_set_binary. + +2010-07-19 Werner Koch + + * dirmngr.c: Include ldap-wrapper.h. + (launch_reaper_thread): Move code to ... + * ldap-wrapper.c (ldap_wrapper_launch_thread): .. here. Change + callers. + (ldap_wrapper_thread): Rename to ... + (wrapper_thread): this and make local. + + * ldap.c (destroy_wrapper, print_log_line) + (read_log_data, ldap_wrapper_thread) + (ldap_wrapper_wait_connections, ldap_wrapper_release_context) + (ldap_wrapper_connection_cleanup, reader_callback, ldap_wrapper): + Factor code out to ... + * ldap-wrapper.c: new. + (ldap_wrapper): Make public. + (read_buffer): Copy from ldap.c. + * ldap-wrapper.h: New. + * Makefile.am (dirmngr_SOURCES): Add new files. + +2010-07-16 Werner Koch + + * http.c, http.h: Remove. + + * dirmngr-err.h: New. + * dirmngr.h: Include dirmngr-err.h instead of gpg-error.h + + * cdblib.c: Replace assignments to ERRNO by a call to + gpg_err_set_errno. Include dirmngr-err.h. + (cdb_free) [__MINGW32CE__]: Do not use get_osfhandle. + + * dirmngr.c [!HAVE_SIGNAL_H]: Don't include signal.h. + (USE_W32_SERVICE): New. Use this to control the use of the W32 + service system. + +2010-07-06 Werner Koch + + * dirmngr.c (main): Print note on directory name changes. + + Replace almost all uses of stdio by estream. + + * b64dec.c, b64enc.c: Remove. They are duplicated in ../common/. + +2010-06-28 Werner Koch + + * dirmngr_ldap.c (my_i18n_init): Remove. + (main): Call i18n_init instead of above function. + + * dirmngr-client.c (my_i18n_init): Remove. + (main): Call i18n_init instead of above function. + + * Makefile.am (dirmngr_LDADD): Add ../gl/libgnu. + (dirmngr_ldap_LDADD, dirmngr_client_LDADD): Ditto. + +2010-06-09 Werner Koch + + * i18n.h: Remove. + + * Makefile.am (no-libgcrypt.c): New rule. + + * exechelp.h: Remove. + * exechelp.c: Remove. + (dirmngr_release_process): Change callers to use the gnupg func. + (dirmngr_wait_process): Likewise. + (dirmngr_kill_process): Likewise. This actually implements it for + W32. + * ldap.c (ldap_wrapper): s/get_dirmngr_ldap_path/gnupg_module_name/. + (ldap_wrapper_thread): Use gnupg_wait_process and adjust for + changed semantics. + (ldap_wrapper): Replace xcalloc by xtrycalloc. Replace spawn + mechanism. + + * server.c (start_command_handler): Remove assuan_set_log_stream. + + * validate.c: Remove gcrypt.h and ksba.h. + + * ldapserver.c: s/util.h/dirmngr.h/. + + * dirmngr.c (sleep) [W32]: Remove macro. + (main): s/sleep/gnupg_sleep/. + (pid_suffix_callback): Change arg type. + (my_gcry_logger): Remove. + (fixed_gcry_pth_init): New. + (main): Use it. + (FD2INT): Remove. + +2010-06-08 Werner Koch + + * misc.h (copy_time): Remove and replace by gnupg_copy_time which + allows to set a null date. + * misc.c (dump_isotime, get_time, get_isotime, set_time) + (check_isotime, add_isotime): Remove and replace all calls by the + versions from common/gettime.c. + + * crlcache.c, misc.c, misc.h: s/dirmngr_isotime_t/gnupg_isotime_t/. + * server.c, ldap.c: Reorder include directives. + * crlcache.h, misc.h: Remove all include directives. + + * certcache.c (cmp_simple_canon_sexp): Remove. + (compare_serialno): Rewrite using cmp_simple_canon_sexp from + common/sexputil.c + + * error.h: Remove. + + * dirmngr.c: Remove transitional option "--ignore-ocsp-servic-url". + (opts): Use ARGPARSE macros. + (i18n_init): Remove. + (main): Use GnuPG init functions. + + * dirmngr.h: Remove duplicated stuff now taken from ../common. + + * get-path.c, util.h: Remove. + + * Makefile.am: Adjust to GnuPG system. + * estream.c, estream.h, estream-printf.c, estream-printf.h: Remove. + +2010-06-07 Werner Koch + + * OAUTHORS, ONEWS, ChangeLog.1: New. + + * ChangeLog, Makefile.am, b64dec.c, b64enc.c, cdb.h, cdblib.c + * certcache.c, certcache.h, crlcache.c, crlcache.h, crlfetch.c + * crlfetch.h, dirmngr-client.c, dirmngr.c, dirmngr.h + * dirmngr_ldap.c, error.h, estream-printf.c, estream-printf.h + * estream.c, estream.h, exechelp.c, exechelp.h, get-path.c, http.c + * http.h, i18n.h, ldap-url.c, ldap-url.h, ldap.c, ldapserver.c + * ldapserver.h, misc.c, misc.h, ocsp.c, ocsp.h, server.c, util.h + * validate.c, validate.h: Imported from the current SVN of the + dirmngr package (only src/). + +2010-03-13 Werner Koch + + * dirmngr.c (int_and_ptr_u): New. + (pid_suffix_callback): Trick out compiler. + (start_connection_thread): Ditto. + (handle_connections): Ditto. + +2010-03-09 Werner Koch + + * dirmngr.c (set_debug): Allow numerical values. + +2009-12-15 Werner Koch + + * dirmngr.c: Add option --ignore-cert-extension. + (parse_rereadable_options): Implement. + * dirmngr.h (opt): Add IGNORED_CERT_EXTENSIONS. + * validate.c (unknown_criticals): Handle ignored extensions. + +2009-12-08 Marcus Brinkmann + + * dirmngr-client.c (start_dirmngr): Convert posix FDs to assuan fds. + +2009-11-25 Marcus Brinkmann + + * server.c (start_command_handler): Use assuan_fd_t and + assuan_fdopen on fds. + +2009-11-05 Marcus Brinkmann + + * server.c (start_command_handler): Update use of + assuan_init_socket_server. + * dirmngr-client.c (start_dirmngr): Update use of + assuan_pipe_connect and assuan_socket_connect. + +2009-11-04 Werner Koch + + * server.c (register_commands): Add help arg to + assuan_register_command. Change all command comments to strings. + +2009-11-02 Marcus Brinkmann + + * server.c (reset_notify): Take LINE argument, return gpg_error_t. + +2009-10-16 Marcus Brinkmann + + * Makefile.am: (dirmngr_LDADD): Link to $(LIBASSUAN_LIBS) instead + of $(LIBASSUAN_PTH_LIBS). + * dirmngr.c: Invoke ASSUAN_SYSTEM_PTH_IMPL. + (main): Call assuan_set_system_hooks and assuan_sock_init. + +2009-09-22 Marcus Brinkmann + + * dirmngr.c (main): Update to new Assuan interface. + * server.c (option_handler, cmd_ldapserver, cmd_isvalid) + (cmd_checkcrl, cmd_checkocsp, cmd_lookup, cmd_loadcrl) + (cmd_listcrls, cmd_cachecert, cmd_validate): Return gpg_error_t + instead int. + (register_commands): Likewise for member HANDLER. + (start_command_handler): Allocate context with assuan_new before + starting server. Release on error. + * dirmngr-client.c (main): Update to new Assuan interface. + (start_dirmngr): Allocate context with assuan_new before + connecting to server. Release on error. + +2009-08-12 Werner Koch + + * dirmngr-client.c (squid_loop_body): Flush stdout. Suggested by + Philip Shin. + +2009-08-07 Werner Koch + + * crlfetch.c (my_es_read): Add explicit check for EOF. + + * http.c (struct http_context_s): Turn IN_DATA and IS_HTTP_0_9 to + bit fields. + (struct cookie_s): Add CONTENT_LENGTH_VALID and CONTENT_LENGTH. + (parse_response): Parse the Content-Length header. + (cookie_read): Handle content length. + (http_open): Make NEED_HEADER the semi-default. + + * http.h (HTTP_FLAG_IGNORE_CL): New. + +2009-08-04 Werner Koch + + * ldap.c (ldap_wrapper_thread): Factor some code out to ... + (read_log_data): ... new. Close the log fd on error. + (ldap_wrapper_thread): Delay cleanup until the log fd is closed. + (SAFE_PTH_CLOSE): New. Use it instead of pth_close. + +2009-07-31 Werner Koch + + * server.c (cmd_loadcrl): Add option --url. + * dirmngr-client.c (do_loadcrl): Make use of --url. + + * crlfetch.c (crl_fetch): Remove HTTP_FLAG_NO_SHUTDOWN. Add + flag HTTP_FLAG_LOG_RESP with active DBG_LOOKUP. + + * http.c: Require estream. Remove P_ES macro. + (write_server): Remove. + (my_read_line): Remove. Replace all callers by es_read_line. + (send_request): Use es_asprintf. Always store the cookie. + (http_wait_response): Remove the need to dup the socket. USe new + shutdown flag. + * http.h (HTTP_FLAG_NO_SHUTDOWN): Rename to HTTP_FLAG_SHUTDOWN. + + * estream.c, estream.h, estream-printf.c, estream-printf.h: Update + from current libestream. This is provide es_asprintf. + +2009-07-20 Werner Koch + + * dirmngr.c (pid_suffix_callback): New. + (main): Use log_set_pid_suffix_cb. + (start_connection_thread): Put the fd into the tls. + + * ldap.c (ldap_wrapper_thread): Print ldap worker stati. + (ldap_wrapper_release_context): Print a debug info. + (end_cert_fetch_ldap): Release the reader. Might fix bug#999. + +2009-06-17 Werner Koch + + * util.h: Remove unused dotlock.h. + +2009-05-26 Werner Koch + + * ldap.c (ldap_wrapper): Show reader object in diagnostics. + * crlcache.c (crl_cache_reload_crl): Ditto. Change debug messages + to regular diagnostics. + * dirmngr_ldap.c (print_ldap_entries): Add extra diagnostics. + +2009-04-03 Werner Koch + + * dirmngr.h (struct server_local_s): Move back to ... + * server.c (struct server_local_s): ... here. + (get_ldapservers_from_ctrl): New. + * ldapserver.h (ldapserver_iter_begin): Use it. + +2008-10-29 Marcus Brinkmann + + * estream.c (es_getline): Add explicit cast to silence gcc -W + warning. + * crlcache.c (finish_sig_check): Likewise. + + * dirmngr.c (opts): Add missing initializer to silence gcc + -W warning. + * server.c (register_commands): Likewise. + * dirmngr-client.c (opts): Likewise. + * dirmngr_ldap.c (opts): Likewise. + + * dirmngr-client.c (status_cb, inq_cert, data_cb): Change return + type to gpg_error_t to silence gcc warning. + +2008-10-21 Werner Koch + + * certcache.c (load_certs_from_dir): Accept ".der" files. + + * server.c (get_istrusted_from_client): New. + * validate.c (validate_cert_chain): Add new optional arg + R_TRUST_ANCHOR. Adjust all callers + * crlcache.c (crl_cache_entry_s): Add fields USER_TRUST_REQ + and CHECK_TRUST_ANCHOR. + (release_one_cache_entry): Release CHECK_TRUST_ANCHOR. + (list_one_crl_entry): Print info about the new fields. + (open_dir, write_dir_line_crl): Support the new U-flag. + (crl_parse_insert): Add arg R_TRUST_ANCHOR and set it accordingly. + (crl_cache_insert): Store trust anchor in entry object. + (cache_isvalid): Ask client for trust is needed. + + * crlcache.c (open_dir): Replace xcalloc by xtrycalloc. + (next_line_from_file): Ditt. Add arg to return the gpg error. + Change all callers. + (update_dir): Replace sprintf and malloc by estream_asprintf. + (crl_cache_insert): Ditto. + (crl_cache_isvalid): Replace xmalloc by xtrymalloc. + (get_auth_key_id): Ditto. + (crl_cache_insert): Ditto. + + * crlcache.c (start_sig_check): Remove HAVE_GCRY_MD_DEBUG test. + * validate.c (check_cert_sig): Ditto. Remove workaround for bug + in libgcrypt 1.2. + + * estream.c, estream.h, estream-printf.c, estream-printf.h: Update + from current libestream (svn rev 61). + +2008-09-30 Marcus Brinkmann + + * get-path.c (get_dirmngr_ldap_path): Revert last change. + Instead, use dirmngr_libexecdir(). + (find_program_at_standard_place): Don't define for now. + +2008-09-30 Marcus Brinkmann + + * get-path.c (dirmngr_cachedir): Make COMP a pointer to const to + silence gcc warning. + (get_dirmngr_ldap_path): Look for dirmngr_ldap in the installation + directory. + +2008-08-06 Marcus Brinkmann + + * dirmngr.c (main): Mark the ldapserverlist-file option as + read-only. + +2008-07-31 Werner Koch + + * crlcache.c (start_sig_check) [!HAVE_GCRY_MD_DEBUG]: Use + gcry_md_start_debug + +2008-06-16 Werner Koch + + * get-path.c (w32_commondir): New. + (dirmngr_sysconfdir): Use it here. + (dirmngr_datadir): Ditto. + +2008-06-12 Marcus Brinkmann + + * Makefile.am (dirmngr_SOURCES): Add ldapserver.h and ldapserver.c. + * ldapserver.h, ldapserver.c: New files. + * ldap.c: Include "ldapserver.h". + (url_fetch_ldap): Use iterator to get session servers as well. + (attr_fetch_ldap, start_default_fetch_ldap): Likewise. + * dirmngr.c: Include "ldapserver.h". + (free_ldapservers_list): Removed. Change callers to + ldapserver_list_free. + (parse_ldapserver_file): Use ldapserver_parse_one. + * server.c: Include "ldapserver.h". + (cmd_ldapserver): New command. + (register_commands): Add new command LDAPSERVER. + (reset_notify): New function. + (start_command_handler): Register reset notify handler. + Deallocate session server list. + (lookup_cert_by_pattern): Use iterator to get session servers as well. + (struct server_local_s): Move to ... + * dirmngr.h (struct server_local_s): ... here. Add new member + ldapservers. + +2008-06-10 Werner Koch + + Support PEM encoded CRLs. Fixes bug#927. + + * crlfetch.c (struct reader_cb_context_s): New. + (struct file_reader_map_s): Replace FP by new context. + (register_file_reader, get_file_reader): Adjust accordingly. + (my_es_read): Detect Base64 encoded CRL and decode if needed. + (crl_fetch): Pass new context to the callback. + (crl_close_reader): Cleanup the new context. + * b64dec.c: New. Taken from GnuPG. + * util.h (struct b64state): Add new fields STOP_SEEN and + INVALID_ENCODING. + +2008-05-26 Marcus Brinkmann + + * dirmngr.c (main) [HAVE_W32_SYSTEM]: Switch to system + configuration on gpgconf related commands, and make all options + unchangeable. + +2008-03-25 Marcus Brinkmann + + * dirmngr_ldap.c (print_ldap_entries): Add code alternative for + W32 console stdout (unused at this point). + +2008-03-21 Marcus Brinkmann + + * estream.c (ESTREAM_MUTEX_DESTROY): New macro. + (es_create, es_destroy): Use it. + +2008-02-21 Werner Koch + + * validate.c (check_cert_sig) [HAVE_GCRY_MD_DEBUG]: Use new debug + function if available. + + * crlcache.c (abort_sig_check): Mark unused arg. + + * exechelp.c (dirmngr_release_process) [!W32]: Mark unsed arg. + + * validate.c (is_root_cert): New. Taken from GnuPG. + (validate_cert_chain): Use it in place of the simple DN compare. + +2008-02-15 Marcus Brinkmann + + * dirmngr.c (main): Reinitialize assuan log stream if necessary. + + * crlcache.c (update_dir) [HAVE_W32_SYSTEM]: Remove destination + file before rename. + (crl_cache_insert) [HAVE_W32_SYSTEM]: Remove destination file + before rename. + +2008-02-14 Marcus Brinkmann + + * validate.c (check_cert_policy): Use ksba_free instead of xfree. + (validate_cert_chain): Likewise. Free SUBJECT on error. + (cert_usage_p): Likewise. + + * crlcache.c (finish_sig_check): Undo last change. + (finish_sig_check): Close md. + (abort_sig_check): New function. + (crl_parse_insert): Use abort_sig_check to clean up. + + * crlcache.c (crl_cache_insert): Clean up CDB on error. + +2008-02-13 Marcus Brinkmann + + * crlcache.c (finish_sig_check): Call gcry_md_stop_debug. + * exechelp.h (dirmngr_release_process): New prototype. + * exechelp.c (dirmngr_release_process): New function. + * ldap.c (ldap_wrapper_thread): Release pid. + (destroy_wrapper): Likewise. + + * dirmngr.c (launch_reaper_thread): Destroy tattr. + (handle_connections): Likewise. + +2008-02-12 Marcus Brinkmann + + * ldap.c (pth_close) [! HAVE_W32_SYSTEM]: New macro. + (struct wrapper_context_s): New member log_ev. + (destroy_wrapper): Check FDs for != -1 rather than != 0. Use + pth_close instead of close. Free CTX->log_ev. + (ldap_wrapper_thread): Rewritten to use pth_wait instead of + select. Also use pth_read instead of read and pth_close instead + of close. + (ldap_wrapper): Initialize CTX->log_ev. + (reader_callback): Use pth_close instead of close. + * exechelp.c (create_inheritable_pipe) [HAVE_W32_SYSTEM]: Removed. + (dirmngr_spawn_process) [HAVE_W32_SYSTEM]: Use pth_pipe instead. + * dirmngr_ldap.c [HAVE_W32_SYSTEM]: Include . + (main) [HAVE_W32_SYSTEM]: Set mode of stdout to binary. + +2008-02-01 Werner Koch + + * ldap.c: Remove all ldap headers as they are unused. + + * dirmngr_ldap.c (LDAP_DEPRECATED): New, to have OpenLDAP use the + old standard API. + +2008-01-10 Werner Koch + + * dirmngr-client.c: New option --local. + (do_lookup): Use it. + + * server.c (lookup_cert_by_pattern): Implement local lookup. + (return_one_cert): New. + * certcache.c (hexsn_to_sexp): New. + (classify_pattern, get_certs_bypattern): New. + + * misc.c (unhexify): Allow passing NULL for RESULT. + (cert_log_subject): Do not call ksba_free on an unused variable. + +2008-01-02 Marcus Brinkmann + + * Makefile.am (dirmngr_LDADD, dirmngr_ldap_LDADD) + (dirmngr_client_LDADD): Add $(LIBICONV). Reported by Michael + Nottebrock. + +2007-12-11 Werner Koch + + * server.c (option_handler): New option audit-events. + * dirmngr.h (struct server_control_s): Add member AUDIT_EVENTS. + +2007-11-26 Marcus Brinkmann + + * get-path.c (dirmngr_cachedir): Create intermediate directories. + (default_socket_name): Use CSIDL_WINDOWS. + +2007-11-21 Werner Koch + + * server.c (lookup_cert_by_pattern): Add args SINGLE and CACHE_ONLY. + (cmd_lookup): Add options --single and --cache-only. + +2007-11-16 Werner Koch + + * certcache.c (load_certs_from_dir): Also log the subject DN. + * misc.c (cert_log_subject): New. + +2007-11-14 Werner Koch + + * dirmngr-client.c: Replace --lookup-url by --url. + (main): Remove extra code for --lookup-url. + (do_lookup): Remove LOOKUP_URL arg and use the + global option OPT.URL. + + * server.c (has_leading_option): New. + (cmd_lookup): Use it. + + * crlfetch.c (fetch_cert_by_url): Use GPG_ERR_INV_CERT_OBJ. + (fetch_cert_by_url): Use gpg_error_from_syserror. + +2007-11-14 Moritz (wk) + + * dirmngr-client.c: New command: --lookup-url . + (do_lookup): New parameter: lookup_url. If TRUE, include "--url" + switch in LOOKUP transaction. + (enum): New entry: oLookupUrl. + (opts): Likewise. + (main): Handle oLookupUrl. New variable: cmd_lookup_url, set + during option parsing, pass to do_lookup() and substitute some + occurences of "cmd_lookup" with "cmd_lookup OR cmd_lookup_url". + * crlfetch.c (fetch_cert_by_url): New function, uses + url_fetch_ldap() to create a reader object and libksba functions + to read a single cert from that reader. + * server.c (lookup_cert_by_url, lookup_cert_by_pattern): New + functions. + (cmd_lookup): Moved almost complete code ... + (lookup_cert_by_pattern): ... here. + (cmd_lookup): Support new optional argument: --url. Depending on + the presence of that switch, call lookup_cert_by_url() or + lookup_cert_by_pattern(). + (lookup_cert_by_url): Heavily stripped down version of + lookup_cert_by_pattern(), using fetch_cert_by_url. + +2007-10-24 Marcus Brinkmann + + * exechelp.c (dirmngr_spawn_process): Fix child handles. + +2007-10-05 Marcus Brinkmann + + * dirmngr.h: Include assuan.h. + (start_command_handler): Change type of FD to assuan_fd_t. + * dirmngr.c: Do not include w32-afunix.h. + (socket_nonce): New global variable. + (create_server_socket): Use assuan socket wrappers. Remove W32 + specific stuff. Save the server nonce. + (check_nonce): New function. + (start_connection_thread): Call it. + (handle_connections): Change args to assuan_fd_t. + * server.c (start_command_handler): Change type of FD to assuan_fd_t. + +2007-09-12 Marcus Brinkmann + + * dirmngr.c (main): Percent escape pathnames in --gpgconf-list output. + +2007-08-27 Moritz Schulte + + * src/Makefile.am (AM_CPPFLAGS): Define DIRMNGR_SOCKETDIR based on + $(localstatedir). + * src/get-path.c (default_socket_name): Use DIRMNGR_SOCKETDIR + instead of hard-coded "/var/run/dirmngr". + +2007-08-16 Werner Koch + + * get-path.c (get_dirmngr_ldap_path): Make PATHNAME const. + + * dirmngr.c (my_ksba_hash_buffer): Mark unused arg. + (dirmngr_init_default_ctrl): Ditto. + (my_gcry_logger): Ditto. + * dirmngr-client.c (status_cb): Ditto. + * dirmngr_ldap.c (catch_alarm): Ditto. + * estream-printf.c (pr_bytes_so_far): Ditto. + * estream.c (es_func_fd_create): Ditto. + (es_func_fp_create): Ditto. + (es_write_hexstring): Ditto. + * server.c (cmd_listcrls): Ditto. + (cmd_cachecert): Ditto. + * crlcache.c (cache_isvalid): Ditto. + * ocsp.c (do_ocsp_request): Ditto. + * ldap.c (ldap_wrapper_thread): Ditto. + * http.c (http_register_tls_callback): Ditto. + (connect_server): Ditto. + (write_server) [!HTTP_USE_ESTREAM]: Don't build. + +2007-08-14 Werner Koch + + * get-path.c (dirmngr_cachedir) [W32]: Use CSIDL_LOCAL_APPDATA. + +2007-08-13 Werner Koch + + * dirmngr.c (handle_connections): Use a timeout in the accept + function. Block signals while creating a new thread. + (shutdown_pending): Needs to be volatile as also accessed bt the + service function. + (w32_service_control): Do not use the regular log fucntions here. + (handle_tick): New. + (main): With system_service in effect use aDaemon as default + command. + (main) [W32]: Only temporary redefine main for the sake of Emacs's + "C-x 4 a". + + * dirmngr-client.c (main) [W32]: Initialize sockets. + (start_dirmngr): Use default_socket_name instead of a constant. + * Makefile.am (dirmngr_client_SOURCES): Add get-path.c + +2007-08-09 Werner Koch + + * dirmngr.c (parse_ocsp_signer): New. + (parse_rereadable_options): Set opt.ocsp_signer to this. + * dirmngr.h (fingerprint_list_t): New. + * ocsp.c (ocsp_isvalid, check_signature, validate_responder_cert): + Allow for several default ocscp signers. + (ocsp_isvalid): Return GPG_ERR_NO_DATA for an unknwon status. + + * dirmngr-client.c: New option --force-default-responder. + + * server.c (has_option, skip_options): New. + (cmd_checkocsp): Add option --force-default-responder. + (cmd_isvalid): Ditto. Also add option --only-ocsp. + + * ocsp.c (ocsp_isvalid): New arg FORCE_DEFAULT_RESPONDER. + + * dirmngr.c: New option --ocsp-max-period. + * ocsp.c (ocsp_isvalid): Implement it and take care that a missing + next_update is to be ignored. + + * crlfetch.c (my_es_read): New. Use it instead of es_read. + + * estream.h, estream.c, estream-printf.c: Updated from current + libestream SVN. + +2007-08-08 Werner Koch + + * crlcache.c (crl_parse_insert): Hack to allow for a missing + nextUpdate. + + * dirmngr_ldap.c (print_ldap_entries): Strip the extension from + the want_attr. + + * exechelp.c (dirmngr_wait_process): Reworked for clear error + semantics. + * ldap.c (ldap_wrapper_thread): Adjust for new + dirmngr_wait_process semantics. + +2007-08-07 Werner Koch + + * get-path.c (default_socket_name) [!W32]: Fixed syntax error. + + * ldap.c (X509CACERT, make_url, fetch_next_cert_ldap): Support + x509caCert as used by the Bundesnetzagentur. + (ldap_wrapper): Do not pass the prgtram name as the first + argument. dirmngr_spawn_process takes care of that. + +2007-08-04 Marcus Brinkmann + + * dirmngr.h (opt): Add member system_service. + * dirmngr.c (opts) [HAVE_W32_SYSTEM]: New entry for option + --service. + (DEFAULT_SOCKET_NAME): Removed. + (service_handle, service_status, + w32_service_control) [HAVE_W32_SYSTEM]: New symbols. + (main) [HAVE_W32_SYSTEM]: New entry point for --service. Rename + old function to ... + (real_main) [HAVE_W32_SYSTEM]: ... this. Use default_socket_name + instead of DEFAULT_SOCKET_NAME, and similar for other paths. + Allow colons in Windows socket path name, and implement --service + option. + * util.h (dirmngr_sysconfdir, dirmngr_libexecdir, dirmngr_datadir, + dirmngr_cachedir, default_socket_name): New prototypes. + * get-path.c (dirmngr_sysconfdir, dirmngr_libexecdir) + (dirmngr_datadir, dirmngr_cachedir, default_socket_name): New + functions. + (DIRSEP_C, DIRSEP_S): New macros. + +2007-08-03 Marcus Brinkmann + + * get-path.c: Really add the file this time. + +2007-07-31 Marcus Brinkmann + + * crlfetch.c: Include "estream.h". + (crl_fetch): Use es_read callback instead a file handle. + (crl_close_reader): Use es_fclose instead of fclose. + (struct file_reader_map_s): Change type of FP to estream_t. + (register_file_reader, crl_fetch, crl_close_reader): Likewise. + * ocsp.c: Include "estream.h". + (read_response): Change type of FP to estream_t. + (read_response, do_ocsp_request): Use es_* variants of I/O + functions. + + * http.c: Include . + (http_wait_response) [HAVE_W32_SYSTEM]: Use DuplicateHandle. + (cookie_read): Use pth_read instead read. + (cookie_write): Use pth_write instead write. + +2007-07-30 Marcus Brinkmann + + * ldap-url.c (ldap_str2charray): Fix buglet in ldap_utf8_strchr + invocation. + +2007-07-27 Marcus Brinkmann + + * estream.h, estream.c: Update from recent GnuPG. + + * get-path.c: New file. + * Makefile.am (dirmngr_SOURCES): Add get-path.c. + * util.h (default_homedir, get_dirmngr_ldap_path): New prototypes. + * dirmngr.c (main): Use default_homedir(). + * ldap-url.h: Remove japanese white space (sorry!). + +2007-07-26 Marcus Brinkmann + + * ldap.c (pth_yield): Remove macro. + + * ldap.c (pth_yield) [HAVE_W32_SYSTEM]: Define to Sleep(0). + + * dirmngr_ldap.c [HAVE_W32_SYSTEM]: Do not include , but + , and "ldap-url.h". + * ldap.c [HAVE_W32_SYSTEM]: Do not include , but + and . + + * ldap-url.c: Do not include , but , + and "ldap-url.h". + (LDAP_P): New macro. + * ldap-url.h: New file. + * Makefile.am (ldap_url): Add ldap-url.h. + + * Makefile.am (ldap_url): New variable. + (dirmngr_ldap_SOURCES): Add $(ldap_url). + (dirmngr_ldap_LDADD): Add $(LIBOBJS). + * ldap-url.c: New file, excerpted from OpenLDAP. + * dirmngr.c (main) [HAVE_W32_SYSTEM]: Avoid the daemonization. + * dirmngr_ldap.c: Include "util.h". + (main) [HAVE_W32_SYSTEM]: Don't set up alarm. + (set_timeout) [HAVE_W32_SYSTEM]: Likewise. + * ldap.c [HAVE_W32_SYSTEM]: Add macros for setenv and pth_yield. + * no-libgcrypt.h (NO_LIBGCRYPT): Define. + * util.h [NO_LIBGCRYPT]: Don't include . + +2007-07-23 Marcus Brinkmann + + * Makefile.am (dirmngr_SOURCES): Add exechelp.h and exechelp.c. + * exechelp.h, exechelp.c: New files. + * ldap.c: Don't include but "exechelp.h". + (destroy_wrapper, ldap_wrapper_thread, + ldap_wrapper_connection_cleanup): Use dirmngr_kill_process instead + of kill. + (ldap_wrapper_thread): Use dirmngr_wait_process instead of + waitpid. + (ldap_wrapper): Use dirmngr_spawn_process. + +2007-07-20 Marcus Brinkmann + + * certcache.c (cert_cache_lock): Do not initialize statically. + (init_cache_lock): New function. + (cert_cache_init): Call init_cache_lock. + + * estream.h, estream.c, estream-printf.h, estream-printf.c: New + files. + * Makefile.am (dirmngr_SOURCES): Add estream.c, estream.h, + estream-printf.c, estream-printf.h. + + * http.c: Update to latest version from GnuPG. + + * Makefile.am (cdb_sources) + * cdblib.c: Port to windows (backport from tinycdb 0.76). + + * crlcache.c [HAVE_W32_SYSTEM]: Don't include sys/utsname.h. + [MKDIR_TAKES_ONE_ARG]: Define mkdir as a macro for such systems. + (update_dir, crl_cache_insert) [HAVE_W32_SYSTEM]: Don't get uname. + * server.c (start_command_handler) [HAVE_W32_SYSTEM]: Don't log + peer credentials. + + * dirmngr.c [HAVE_W32_SYSTEM]: Do not include sys/socket.h or + sys/un.h, but ../jnlib/w32-afunix.h. + (sleep) [HAVE_W32_SYSTEM]: New macro. + (main) [HAVE_W32_SYSTEM]: Don't mess with SIGPIPE. Use W32 socket + API. + (handle_signal) [HAVE_W32_SYSTEM]: Deactivate the bunch of the + code. + (handle_connections) [HAVE_W32_SYSTEM]: don't handle signals. + +2006-11-29 Werner Koch + + * dirmngr.c (my_strusage): Use macro for the bug report address + and the copyright line. + * dirmngr-client.c (my_strusage): Ditto. + * dirmngr_ldap.c (my_strusage): Ditto. + + * Makefile.am: Do not link against LIBICONV. + +2006-11-19 Werner Koch + + * dirmngr.c: Include i18n.h. + +2006-11-17 Werner Koch + + * Makefile.am (dirmngr_LDADD): Use LIBASSUAN_PTH_LIBS. + +2006-11-16 Werner Koch + + * server.c (start_command_handler): Replaced + assuan_init_connected_socket_server by assuan_init_socket_server_ext. + + * crlcache.c (update_dir): Put a diagnostic into DIR.txt. + (open_dir): Detect invalid and duplicate entries. + (update_dir): Fixed search for second field. + +2006-10-23 Werner Koch + + * dirmngr.c (main): New command --gpgconf-test. + +2006-09-14 Werner Koch + + * server.c (start_command_handler): In vebose mode print + information about the peer. This may later be used to restrict + certain commands. + +2006-09-12 Werner Koch + + * server.c (start_command_handler): Print a more informative hello + line. + * dirmngr.c: Moved config_filename into the opt struct. + +2006-09-11 Werner Koch + + Changed everything to use Assuan with gpg-error codes. + * maperror.c: Removed. + * server.c (map_to_assuan_status): Removed. + * dirmngr.c (main): Set assuan error source. + * dirmngr-client.c (main): Ditto. + +2006-09-04 Werner Koch + + * crlfetch.c (crl_fetch): Implement HTTP redirection. + * ocsp.c (do_ocsp_request): Ditto. + + New HTTP code version taken from gnupg svn release 4236. + * http.c (http_get_header): New. + (capitalize_header_name, store_header): New. + (parse_response): Store headers away. + (send_request): Return GPG_ERR_NOT_FOUND if connect_server failed. + * http.h: New flag HTTP_FLAG_NEED_HEADER. + +2006-09-01 Werner Koch + + * crlfetch.c (register_file_reader, get_file_reader): New. + (crl_fetch): Register the file pointer for HTTP. + (crl_close_reader): And release it. + + * http.c, http.h: Updated from GnuPG SVN trunk. Changed all users + to adopt the new API. + * dirmngr.h: Moved inclusion of jnlib header to ... + * util.h: .. here. This is required becuase http.c includes only + a file util.h but makes use of log_foo. Include gcrypt.h so that + gcry_malloc et al are declared. + +2006-08-31 Werner Koch + + * ocsp.c (check_signature): Make use of the responder id. + +2006-08-30 Werner Koch + + * validate.c (check_cert_sig): Workaround for rimemd160. + (allowed_ca): Always allow trusted CAs. + + * dirmngr.h (cert_ref_t): New. + (struct server_control_s): Add field OCSP_CERTS. + * server.c (start_command_handler): Release new field + * ocsp.c (release_ctrl_ocsp_certs): New. + (check_signature): Store certificates in OCSP_CERTS. + + * certcache.c (find_issuing_cert): Reset error if cert was found + by subject. + (put_cert): Add new arg FPR_BUFFER. Changed callers. + (cache_cert_silent): New. + + * dirmngr.c (parse_rereadable_options): New options + --ocsp-max-clock-skew and --ocsp-current-period. + * ocsp.c (ocsp_isvalid): Use them here. + + * ocsp.c (validate_responder_cert): New optional arg signer_cert. + (check_signature_core): Ditto. + (check_signature): Use the default signer certificate here. + +2006-06-27 Werner Koch + + * dirmngr-client.c (inq_cert): Take care of SENDCERT_SKI. + +2006-06-26 Werner Koch + + * crlcache.c (lock_db_file): Count open files when needed. + (find_entry): Fixed deleted case. + +2006-06-23 Werner Koch + + * misc.c (cert_log_name): New. + + * certcache.c (load_certs_from_dir): Also print certificate name. + (find_cert_bysn): Release ISSDN. + + * validate.h: New VALIDATE_MODE_CERT. + * server.c (cmd_validate): Use it here so that no policy checks + are done. Try to validated a cached copy of the target. + + * validate.c (validate_cert_chain): Implement a validation cache. + (check_revocations): Print more diagnostics. Actually use the + loop variable and not the head of the list. + (validate_cert_chain): Do not check revocations of CRL issuer + certificates in plain CRL check mode. + * ocsp.c (ocsp_isvalid): Make sure it is reset for a status of + revoked. + +2006-06-22 Werner Koch + + * validate.c (cert_use_crl_p): New. + (cert_usage_p): Add a mode 6 for CRL signing. + (validate_cert_chain): Check that the certificate may be used for + CRL signing. Print a note when not running as system daemon. + (validate_cert_chain): Reduce the maximum depth from 50 to 10. + + * certcache.c (find_cert_bysn): Minor restructuring + (find_cert_bysubject): Ditto. Use get_cert_local when called + without KEYID. + * crlcache.c (get_crlissuer_cert_bysn): Removed. + (get_crlissuer_cert): Removed. + (crl_parse_insert): Use find_cert_bysubject and find_cert_bysn + instead of the removed functions. + +2006-06-19 Werner Koch + + * certcache.c (compare_serialno): Silly me. Using 0 as true is + that hard; tsss. Fixed call cases except for the only working one + which are both numbers of the same length. + +2006-05-15 Werner Koch + + * crlfetch.c (crl_fetch): Use no-shutdown flag for HTTP. This + seems to be required for "IBM_HTTP_Server/2.0.47.1 Apache/2.0.47 + (Unix)". + + * http.c (parse_tuple): Set flag to to indicate no value. + (build_rel_path): Take care of it. + + * crlcache.c (crl_cache_reload_crl): Also iterate over all names + within a DP. + +2005-09-28 Marcus Brinkmann + + * Makefile.am (dirmngr_LDADD): Add @LIBINTL@ and @LIBICONV@. + (dirmngr_ldap_LDADD): Likewise. + (dirmngr_client_LDADD): Likewise. + +2005-09-12 Werner Koch + + * dirmngr.c: Fixed description to match the one in gpgconf. + +2005-06-15 Werner Koch + + * server.c (cmd_lookup): Take care of NO_DATA which might get + returned also by start_cert_fetch(). + +2005-04-20 Werner Koch + + * ldap.c (ldap_wrapper_wait_connections): Set a shutdown flag. + (ldap_wrapper_thread): Handle shutdown in a special way. + +2005-04-19 Werner Koch + + * server.c (get_cert_local, get_issuing_cert_local) + (get_cert_local_ski): Bail out if called without a local context. + +2005-04-18 Werner Koch + + * certcache.c (find_issuing_cert): Fixed last resort method which + should be finding by subject and not by issuer. Try to locate it + also using the keyIdentifier method. Improve error reporting. + (cmp_simple_canon_sexp): New. + (find_cert_bysubject): New. + (find_cert_bysn): Ask back to the caller before trying an extarnl + lookup. + * server.c (get_cert_local_ski): New. + * crlcache.c (crl_parse_insert): Also try to locate issuer + certificate using the keyIdentifier. Improved error reporting. + +2005-04-14 Werner Koch + + * ldap.c (start_cert_fetch_ldap): Really return ERR. + +2005-03-17 Werner Koch + + * http.c (parse_response): Changed MAXLEN and LEN to size_t to + match the requirement of read_line. + * http.h (http_context_s): Ditto for BUFFER_SIZE. + +2005-03-15 Werner Koch + + * ldap.c: Included time.h. Reported by Bernhard Herzog. + +2005-03-09 Werner Koch + + * dirmngr.c: Add a note to the help listing check the man page for + other options. + +2005-02-01 Werner Koch + + * crlcache.c (crl_parse_insert): Renamed a few variables and + changed diagnostic strings for clarity. + (get_issuer_cert): Renamed to get_crlissuer_cert. Try to locate + the certificate from the cache using the subject name. Use new + fetch function. + (get_crlissuer_cert_bysn): New. + (crl_parse_insert): Use it here. + * crlfetch.c (ca_cert_fetch): Changed interface. + (fetch_next_ksba_cert): New. + * ldap.c (run_ldap_wrapper): Add arg MULTI_MODE. Changed all + callers. + (start_default_fetch_ldap): New + * certcache.c (get_cert_bysubject): New. + (clean_cache_slot, put_cert): Store the subject DN if available. + (MAX_EXTRA_CACHED_CERTS): Increase limit of cachable certificates + to 1000. + (find_cert_bysn): Loop until a certificate with a matching S/N has + been found. + + * dirmngr.c (main): Add honor-http-proxy to the gpgconf list. + +2005-01-31 Werner Koch + + * ldap.c: Started to work on support for userSMIMECertificates. + + * dirmngr.c (main): Make sure to always pass a server control + structure to the caching functions. Reported by Neil Dunbar. + +2005-01-05 Werner Koch + + * dirmngr-client.c (read_pem_certificate): Skip trailing percent + escaped linefeeds. + +2005-01-03 Werner Koch + + * dirmngr-client.c (read_pem_certificate): New. + (read_certificate): Divert to it depending on pem option. + (squid_loop_body): New. + (main): New options --pem and --squid-mode. + +2004-12-17 Werner Koch + + * dirmngr.c (launch_ripper_thread): Renamed to launch_reaper_thread. + (shutdown_reaper): New. Use it for --server and --daemon. + * ldap.c (ldap_wrapper_wait_connections): New. + +2004-12-17 Werner Koch + + * Makefile.am (dirmngr_ldap_LDADD): Adjusted for new LDAP checks. + +2004-12-16 Werner Koch + + * ldap.c (ldap_wrapper): Peek on the output to detect empty output + early. + +2004-12-15 Werner Koch + + * ldap.c (ldap_wrapper): Print a diagnostic after forking for the + ldap wrapper. + * certcache.h (find_cert_bysn): Add this prototype. + * crlcache.c (start_sig_check): Write CRL hash debug file. + (finish_sig_check): Dump the signer's certificate. + (crl_parse_insert): Try to get the issuing cert by authKeyId. + Moved certificate retrieval after item processing. + +2004-12-13 Werner Koch + + * dirmngr_ldap.c (catch_alarm, set_timeout): new. + (main): Install alarm handler. Add new option --only-search-timeout. + (print_ldap_entries, fetch_ldap): Use set_timeout (); + * dirmngr.h: Make LDAPTIMEOUT a simple unsigned int. Change all + initializations. + * ldap.c (start_cert_fetch_ldap, run_ldap_wrapper): Pass timeout + option to the wrapper. + (INACTIVITY_TIMEOUT): Depend on LDAPTIMEOUT. + (run_ldap_wrapper): Add arg IGNORE_TIMEOUT. + (ldap_wrapper_thread): Check for special timeout exit code. + + * dirmngr.c: Workaround a typo in gpgconf for + ignore-ocsp-service-url. + +2004-12-10 Werner Koch + + * ldap.c (url_fetch_ldap): Use TMP and not a HOST which is always + NULL. + * misc.c (host_and_port_from_url): Fixed bad encoding detection. + +2004-12-03 Werner Koch + + * crlcache.c (crl_cache_load): Re-implement it. + + * dirmngr-client.c: New command --load-crl + (do_loadcrl): New. + + * dirmngr.c (parse_rereadable_options, main): Make --allow-ocsp, + --ocsp-responder, --ocsp-signer and --max-replies re-readable. + + * ocsp.c (check_signature): try to get the cert from the cache + first. + (ocsp_isvalid): Print the next and this update times on time + conflict. + + * certcache.c (load_certs_from_dir): Print the fingerprint for + trusted certificates. + (get_cert_byhexfpr): New. + * misc.c (get_fingerprint_hexstring_colon): New. + +2004-12-01 Werner Koch + + * Makefile.am (dirmngr_LDADD): Don't use LDAP_LIBS. + + * validate.c (validate_cert_chain): Fixed test; as written in the + comment we want to do this only in daemon mode. For clarity + reworked by using a linked list of certificates and include root + and tragte certificate. + (check_revocations): Likewise. Introduced a recursion sentinel. + +2004-11-30 Werner Koch + + * crlfetch.c (ca_cert_fetch, crl_fetch_default): Do not use the + binary prefix as this will be handled in the driver. + + * dirmngr_ldap.c: New option --log-with-pid. + (fetch_ldap): Handle LDAP_NO_SUCH_OBJECT. + * ldap.c (run_ldap_wrapper, start_cert_fetch_ldap): Use new log + option. + + +2004-11-25 Werner Koch + + * Makefile.am (dirmngr_ldap_CFLAGS): Added GPG_ERROR_CFLAGS. + Noted by Bernhard Herzog. + +2004-11-24 Werner Koch + + * ldap.c (ldap_wrapper): Fixed default name of the ldap wrapper. + + * b64enc.c (b64enc_start, b64enc_finish): Use standard strdup/free + to manage memory. + + * dirmngr.c: New options --ignore-http-dp, --ignore-ldap-dp and + --ignore-ocsp-service-url. + * crlcache.c (crl_cache_reload_crl): Implement them. + * ocsp.c (ocsp_isvalid): Ditto. + +2004-11-23 Werner Koch + + * ldap.c (ldap_wrapper_thread, reader_callback, ldap_wrapper): + Keep a timestamp and terminate the wrapper after some time of + inactivity. + + * dirmngr-client.c (do_lookup): New. + (main): New option --lookup. + (data_cb): New. + * b64enc.c: New. Taken from GnuPG 1.9. + * no-libgcrypt.c (gcry_strdup): Added. + + * ocsp.c (ocsp_isvalid): New arg CERT and lookup the issuer + certificate using the standard methods. + + * server.c (cmd_lookup): Truncation is now also an indication for + error. + (cmd_checkocsp): Implemented. + + * dirmngr_ldap.c (fetch_ldap): Write an error marker for a + truncated search. + * ldap.c (add_server_to_servers): Reactivated. + (url_fetch_ldap): Call it here and try all configured servers in + case of a a failed lookup. + (fetch_next_cert_ldap): Detect the truncation error flag. + * misc.c (host_and_port_from_url, remove_percent_escapes): New. + +2004-11-22 Werner Koch + + * dirmngr_ldap.c (main): New option --proxy. + * ocsp.c (do_ocsp_request): Take care of opt.disable_http. + * crlfetch.c (crl_fetch): Honor the --honor-http-proxy variable. + (crl_fetch): Take care of opt.disable_http and disable_ldap. + (crl_fetch_default, ca_cert_fetch, start_cert_fetch): + * ldap.c (run_ldap_wrapper): New arg PROXY. + (url_fetch_ldap, attr_fetch_ldap, start_cert_fetch_ldap): Pass it. + + * http.c (http_open_document): Add arg PROXY. + (http_open): Ditto. + (send_request): Ditto and implement it as an override. + + * ocsp.c (validate_responder_cert): Use validate_cert_chain. + + * Makefile.am (AM_CPPFLAGS): Add macros for a few system + directories. + * dirmngr.h (opt): New members homedir_data, homedir_cache, + ldap_wrapper_program, system_daemon, honor_http_proxy, http_proxy, + ldap_proxy, only_ldap_proxy, disable_ldap, disable_http. + * dirmngr.c (main): Initialize new opt members HOMEDIR_DATA and + HOMEDIR_CACHE. + (parse_rereadable_options): New options --ldap-wrapper-program, + --http-wrapper-program, --disable-ldap, --disable-http, + --honor-http-proxy, --http-proxy, --ldap-proxy, --only-ldap-proxy. + (reread_configuration): New. + + * ldap.c (ldap_wrapper): Use the correct name for the wrapper. + + * crlcache.c (DBDIR_D): Make it depend on opt.SYSTEM_DAEMON. + (cleanup_cache_dir, open_dir, update_dir, make_db_file_name) + (crl_cache_insert, create_directory_if_needed): Use opt.HOMEDIR_CACHE + + * validate.c (check_revocations): New. + * crlcache.c (crl_cache_isvalid): Factored most code out to + (cache_isvalid): .. new. + (crl_cache_cert_isvalid): New. + * server.c (cmd_checkcrl): Cleaned up by using this new function. + (reload_crl): Moved to .. + * crlcache.c (crl_cache_reload_crl): .. here and made global. + + * certcache.c (cert_compute_fpr): Renamed from computer_fpr and + made global. + (find_cert_bysn): Try to lookup missing certs. + (cert_cache_init): Intialize using opt.HOMEDIR_DATA. + + +2004-11-19 Werner Koch + + * dirmngr-client.c (status_cb): New. Use it in very verbose mode. + + * server.c (start_command_handler): Malloc the control structure + and properly release it. Removed the primary_connection + hack. Cleanup running wrappers. + (dirmngr_status): Return an error code. + (dirmngr_tick): Return an error code and detect a + cancellation. Use wall time and not CPU time. + * validate.c (validate_cert_chain): Add CTRL arg and changed callers. + * crlcache.c (crl_cache_isvalid): + * crlfetch.c (ca_cert_fetch, start_cert_fetch, crl_fetch_default) + (crl_fetch): Ditto. + * ldap.c (ldap_wrapper, run_ldap_wrapper, url_fetch_ldap) + (attr_fetch_ldap, start_cert_fetch_ldap): Ditto. + (ldap_wrapper_release_context): Reset the stored CTRL. + (reader_callback): Periodically call dirmngr_tick. + (ldap_wrapper_release_context): Print an error message for read + errors. + (ldap_wrapper_connection_cleanup): New. + +2004-11-18 Werner Koch + + * dirmngr.c (main): Do not cd / if not running detached. + + * dirmngr-client.c: New options --cache-cert and --validate. + (do_cache, do_validate): New. + * server.c (cmd_cachecert, cmd_validate): New. + + * crlcache.c (get_issuer_cert): Make use of the certificate cache. + (crl_parse_insert): Validate the issuer certificate. + + * dirmngr.c (handle_signal): Reinitialize the certificate cache on + a HUP. + (struct opts): Add --homedir to enable the already implemented code. + (handle_signal): Print stats on SIGUSR1. + + * certcache.c (clean_cache_slot, cert_cache_init) + (cert_cache_deinit): New. + (acquire_cache_read_lock, acquire_cache_write_lock) + (release_cache_lock): New. Use them where needed. + (put_cert): Renamed from put_loaded_cert. + (cache_cert): New. + (cert_cache_print_stats): New. + (compare_serialno): Fixed. + +2004-11-16 Werner Koch + + * Makefile.am (AM_CPPFLAGS): Define DIRMNGR_SYSCONFDIR and + DIRMNGR_LIBEXECDIR. + + * misc.c (dump_isotime, dump_string, dump_cert): New. Taken from + gnupg 1.9. + (dump_serial): New. + +2004-11-15 Werner Koch + + * validate.c: New. Based on gnupg's certchain.c + + * ldap.c (get_cert_ldap): Removed. + (read_buffer): New. + (start_cert_fetch_ldap, fetch_next_cert_ldap) + (end_cert_fetch_ldap): Rewritten to make use of the ldap wrapper. + +2004-11-12 Werner Koch + + * http.c (insert_escapes): Print the percent sign too. + + * dirmngr-client.c (inq_cert): Ignore "SENDCERT" and + "SENDISSUERCERT". + + * server.c (do_get_cert_local): Limit the length of a retruned + certificate. Return NULL without an error if an empry value has + been received. + + * crlfetch.c (ca_cert_fetch): Use the ksba_reader_object. + (setup_funopen, fun_reader, fun_closer): Removed. + + * crlcache.c (get_issuer_cert): Adjust accordingly. + + * ldap.c (attr_fetch_ldap_internal, attr_fetch_fun_closer) + (attr_fetch_fun_reader, url_fetch_ldap_internal) + (get_attr_from_result_ldap): Removed. + (destroy_wrapper, print_log_line, ldap_wrapper_thread) + (ldap_wrapper_release_context, reader_callback, ldap_wrapper) + (run_ldap_wrapper): New. + (url_fetch_ldap): Make use of the new ldap wrapper and return a + ksba reader object instead of a stdio stream. + (attr_fetch_ldap): Ditto. + (make_url, escape4url): New. + +2004-11-11 Werner Koch + + * dirmngr.c (launch_ripper_thread): New. + (main): Start it wheere appropriate. Always ignore SIGPIPE. + (start_connection_thread): Maintain a connection count. + (handle_signal, handle_connections): Use it here instead of the + thread count. + + * crlcache.c (crl_cache_insert): Changed to use ksba reader + object. Changed all callers to pass this argument. + +2004-11-08 Werner Koch + + * dirmngr_ldap.c: New. + + * crlcache.c (crl_cache_init): Don't return a cache object but + keep it module local. We only need one. + (crl_cache_deinit): Don't take cache object but work on existing + one. + (get_current_cache): New. + (crl_cache_insert, crl_cache_list, crl_cache_load): Use the global + cache object and removed the cache arg. Changed all callers. + + * dirmngr-client.c: New option --ping. + + * dirmngr.c (main): New option --daemon. Initialize PTH. + (handle_connections, start_connection_thread): New. + (handle_signal): New. + (parse_rereadable_options): New. Changed main to make use of it. + (set_debug): Don't bail out on invalid debug levels. + (main): Init the crl_chache for server and daemon mode. + + * server.c (start_command_handler): New arg FD. Changed callers. + +2004-11-06 Werner Koch + + * server.c (map_assuan_err): Factored out to .. + * maperror.c: .. new file. + * util.h: Add prototype + +2004-11-05 Werner Koch + + * no-libgcrypt.c: New, used as helper for dirmngr-client which + does not need libgcrypt proper but jnlib references the memory + functions. Taken from gnupg 1.9.12. + + * dirmngr.h: Factored i18n and xmalloc code out to .. + * i18n.h, util.h: .. New. + + * dirmngr-client.c: New. Some code taken from gnupg 1.9.12. + * Makefile.am (bin_PROGRAMS) Add dirmngr-client. + +2004-11-04 Werner Koch + + * src/server.c (get_fingerprint_from_line, cmd_checkcrl) + (cmd_checkocsp): New. + (register_commands): Register new commands. + (inquire_cert_and_load_crl): Factored most code out to .. + (reload_crl): .. new function. + * src/certcache.h, src/certcache.c: New. + * src/Makefile.am (dirmngr_SOURCES): Add new files. + +2004-11-04 Werner Koch + + Please note that earlier entries are found in the top level + ChangeLog. + [Update after merge with GnuPG: These old ChangeLog entries are + found below up to ==END OLDEST CHANGELOG==] + +==BEGIN OLDEST CHANGELOG== + +2004-10-04 Werner Koch + + * src/dirmngr.c: Changed an help entry description. + +2004-09-30 Werner Koch + + * src/dirmngr.c (i18n_init): Always use LC_ALL. + +2004-09-28 Werner Koch + + Released 0.5.6. + + * config.guess, config.sub: Updated. + +2004-06-21 Werner Koch + + * src/crlfetch.c (crl_fetch): Bad hack to use the right attribute. + +2004-05-13 Werner Koch + + Released 0.5.5. + + * src/ldap.c (start_cert_fetch_ldap, start_cert_fetch_ldap): More + detailed error messages. + + * src/crlcache.c (update_dir): Handle i-records properly. + +2004-04-29 Werner Koch + + Released 0.5.4. + + * src/crlcache.h (crl_cache_result_t): Add CRL_CACHE_CANTUSE. + * src/server.c (cmd_isvalid): Handle it here. + * src/crlcache.c (crl_cache_isvalid): Issue this code if the CRL + cant be used. + (open_dir): Parse new fields 8,9 and 10 as well as the invalid flag. + (write_dir_line_crl): Write new fields. + (get_crl_number, get_auth_key_id): New. + (crl_cache_insert): Fill new fields. Mark the entry invalid if + the CRL is too old after an update or an unknown critical + extension was seen. + (list_one_crl_entry): Print the new fields. + +2004-04-28 Werner Koch + + * configure.ac: Requires libksba 0.9.6. + + * src/dirmngr.c: New option --ocsp-signer. + * src/dirmngr.h (opt): Renamed member OCSP_REPONDERS to + OCSP_RESPONDER and made ist a simple string. Add OCSP_SIGNER. + * src/ocsp.c (ocsp_isvalid): Changed it accordingly. + (ocsp_isvalid): Pass the ocsp_signer to check_signature. + (check_signature): New arg SIGNER_FPR. Use it to retrieve the + certificate. Factored out common code to .. + (check_signature_core): .. New. + +2004-04-27 Werner Koch + + * src/server.c (start_command_handler): Keep track of the first + connection. + (dirmngr_tick): New. + * src/ldap.c (attr_fetch_fun_reader): Call it from time to time. + +2004-04-23 Werner Koch + + * src/dirmngr.c (main): Removed the add-servers option from the + gpgconf list. It is not really useful. + +2004-04-02 Thomas Schwinge + + * autogen.sh: Added ACLOCAL_FLAGS. + +2004-04-13 Werner Koch + + * src/crlcache.c (update_dir): Do not double close FPOUT. + +2004-04-09 Werner Koch + + * src/cdblib.c (cdb_make_start): Wipeout the entire buffer to + shutup valgrind. + (ewrite): Fixed writing bad data on EINTR. + + * src/ldap.c (get_attr_from_result_ldap): Fixed bad copy and + terminate of a string. + + * src/crlfetch.c (crl_fetch): Fixed freeing of VALUE on error. + +2004-04-07 Werner Koch + + * src/dirmngr.h (server_control_s): Add member force_crl_refresh. + * src/server.c (option_handler): New. + (start_command_handler): Register option handler + * src/crlcache.c (crl_cache_isvalid): Add arg FORCE_REFRESH. + (crl_cache_insert): Record last refresh in memory. + + * src/server.c (inquire_cert_and_load_crl): Renamed from + inquire_cert. + +2004-04-06 Werner Koch + + Released 0.5.3 + + * doc/dirmngr.texi: Updated. + * doc/texinfo.tex: Updated. + +2004-04-05 Werner Koch + + * src/ocsp.c (ocsp_isvalid): Check THIS_UPDATE. + + * src/misc.c (add_isotime): New. + (date2jd, jd2date, days_per_month, days_per_year): New. Taken from + my ancient (1988) code used in Wedit (time2.c). + +2004-04-02 Werner Koch + + * autogen.sh: Check gettext version. + * configure.ac: Add AM_GNU_GETTEXT. + +2004-04-02 gettextize + + * Makefile.am (SUBDIRS): Add intl. + (EXTRA_DIST): Add config.rpath. + * configure.ac (AC_CONFIG_FILES): Add intl/Makefile, + +2004-04-02 Werner Koch + + Add i18n at most places. + + * src/dirmngr.c (i18n_init): New. + (main): Call it. + * src/dirmngr.h: Add i18n stuff. + +2004-04-01 Werner Koch + + * src/misc.c (get_fingerprint_hexstring): New. + + * src/server.c (dirmngr_status): New. + +2004-03-26 Werner Koch + + * configure.ac: Add AC_SYS_LARGEFILE. + + * doc/dirmngr.texi: Changed the license to the GPL as per message + by Mathhias Kalle Dalheimer of Klaralvdalens-Datakonsult dated + Jan 7, 2004. + * doc/fdl.texi: Removed. + +2004-03-25 Werner Koch + + * src/dirmngr.c (main): New command --fetch-crl. + +2004-03-23 Werner Koch + + * src/dirmngr.c: New option --allow-ocsp. + * src/server.c (cmd_isvalid): Make use of allow_ocsp. + +2004-03-17 Werner Koch + + * src/dirmngr.c (main) : Fixed default value quoting. + +2004-03-16 Werner Koch + + * src/dirmngr.c (main): Add ocsp-responder to the gpgconf list. + Add option --debug-level. + (set_debug): New. + +2004-03-15 Werner Koch + + * src/misc.c (canon_sexp_to_grcy): New. + +2004-03-12 Werner Koch + + * src/crlfetch.c (crl_fetch): Hack to substitute http for https. + +2004-03-10 Werner Koch + + * src/dirmngr.c (parse_ldapserver_file): Don't skip the entire + file on errors. + +2004-03-09 Werner Koch + + * src/dirmngr.c (my_ksba_hash_buffer): New. + (main): Initialize the internal libksba hashing. + + * src/server.c (get_issuer_cert_local): Renamed to ... + (get_cert_local): ... this. Changed all callers. Allow NULL for + ISSUER to return the current target cert. + (get_issuing_cert_local): New. + (do_get_cert_local): Moved common code to here. + +2004-03-06 Werner Koch + + Released 0.5.2. + + * configure.ac: Fixed last change to check the API version of + libgcrypt. + +2004-03-05 Werner Koch + + * configure.ac: Also check the SONAME of libgcrypt. + +2004-03-03 Werner Koch + + * src/dirmngr.c: New option --ocsp-responder. + * src/dirmngr.h (opt): Add member OCSP_RESPONDERS. + +2004-02-26 Steffen Hansen + + * src/server.c (start_command_handler): Corrected typo and made + dirmngr output it's version in the greeting message. + +2004-02-24 Marcus Brinkmann + + * src/dirmngr.c (DEFAULT_ADD_SERVERS): Removed. If this were + true, there'd be no way to disable it. + (main): Dump options in new gpgconf format. + +2004-02-11 Werner Koch + + * autogen.sh (check_version): Removed bashism and simplified. + +2004-02-06 Moritz Schulte + + * src/crlfetch.c (crl_fetch_default): Do not dereference VALUE, + when checking for non-zero. + +2004-02-01 Marcus Brinkmann + + * src/dirmngr.c (DEFAULT_ADD_SERVERS, DEFAULT_MAX_REPLIES) + (DEFAULT_LDAP_TIMEOUT): New macros. + (main): Use them. + (enum cmd_and_opt_values): New command aGPGConfList. + (main): Add handler here. + +2004-01-17 Werner Koch + + * configure.ac: Added AC_CHECK_FUNCS tests again, because the + other test occurrences belong to the jnlib tests block. + +2004-01-15 Moritz Schulte + + * configure.ac: Fixed funopen replacement mechanism; removed + unnecessary AC_CHECK_FUNCS calls. + +2004-01-14 Werner Koch + + * src/crlcache.c (list_one_crl_entry): Don't use putchar. + + * src/server.c (cmd_listcrls): New. + +2003-12-23 Werner Koch + + Released 0.5.1. + +2003-12-17 Werner Koch + + * configure.ac (CFLAGS): Add -Wformat-noliteral in gcc + + maintainer mode. + (NEED_LIBASSUAN_VERSION): Bump up to 0.6.2. + +2003-12-16 Werner Koch + + * configure.ac: Update the tests for jnlib. + * src/dirmngr.c (main): Ignore SIGPIPE in server mode. + +2003-12-12 Werner Koch + + * src/crlcache.c (hash_dbfile): Also hash version info of the + cache file format. + + * src/Makefile.am (dirmngr_SOURCES): Add http.h. + + * configure.ac: Removed checking for DB2. Add checking for mmap. + * src/cdb.h, src/cdblib.h: New. Add a few comments from the + original man page and fixed typos. + * src/cdblib.c (cdb_findinit, cdb_findnext): Modified to allow + walking over all entries. + * src/crlcache.h: Removed DB2/4 cruft. + (release_one_cache_entry, lock_db_file, crl_parse_insert) + (crl_cache_insert, crl_cache_isvalid, list_one_crl_entry): Use the + new CDB interface. + + * src/dirmngr.c: Beautified the help messages. + (wrong_args): New. + (main): new option --force. Revamped the command handling code. + Allow to pass multiple CRLS as well as stdin to --local-crl. + * src/crlcache.c (crl_cache_insert): Make --force work. + +2003-12-11 Werner Koch + + * src/crlfetch.c (crl_fetch): Enhanced to allow fetching binary + data using HTTP. + * src/http.c, src/http.h: Replaced by the code from gnupg 1.3 and + modified acording to our needs. + (read_line): New. Based on the code from GnuPG's iobuf_read_line. + * configure.ac: Check for getaddrinfo. + + * src/dirmngr.c (parse_ldapserver_file): Close the stream. + (main): Free ldapfile. + + * src/ocsp.c, src/ocsp.h: New. Albeit not functionality. + + * src/server.c (inquire_cert): Catch EOF when reading dist points. + + * src/crlcache.c (hash_dbfile, check_dbfile): New. + (lock_db_file, crl_cache_insert): Use them here to detect + corrupted CRL files. + (open_dir): Read the new dbfile hash field. + + * src/crlfetch.c (crl_fetch, crl_fetch_default): Changed to retrun + a stream. + (fun_reader, fun_closer, setup_funopen): New. + * src/server.c (inquire_cert): Changed to use the new stream interface + of crlfetch.c. + +2003-12-10 Werner Koch + + * src/funopen.c: New. + * configure.ac (funopen): Add test. + * src/Makefile.am (dirmngr_LDADD): Add LIBOBJS. + + * src/crlcache.c (next_line_from_file): Remove the limit on the + line length. + (crl_cache_new): Removed. + (open_dbcontent): New. + (crl_cache_init): Use it here. + (crl_cache_flush): The DB content fie is now in the cache + directory, so we can simplify it. + (make_db_file_name, lock_db_file, unlock_db_file): New. + (release_cache): Close the cached DB files. + (crl_cache_isvalid): Make use of the new lock_db_file. + (crl_cache_insert): Changed to take a stream as argument. + (crl_parse_insert): Rewritten to use a temporary DB and to avoid + using up large amounts of memory. + (db_entry_new): Removed. + (release_cache,release_one_cache_entry): Splitted up. + (find_entry): Take care of the new deleted flag. + (crl_cache_load): Simplified becuase we can now pass a FP to the + insert code. + (save_contents): Removed. + (update_dir): New. + (open_dbcontent_file): Renamed to open_dir_file. + (check_dbcontent_version): Renamed to check_dir_version. + (open_dbcontent): Renamed to open_dir. + + * src/dirmngr.c: New option --faked-system-time. + * src/misc.c (faked_time_p, set_time, get_time): New. Taken from GnuPG. + (check_isotime): New. + (unpercent_string): New. + +2003-12-09 Werner Koch + + * src/crlcache.h (DBDIR,DBCONTENTFILE): Changed value. + + * autogen.sh: Reworked. + * README.CVS: New. + * configure.ac: Added min_automake_version. + +2003-12-03 Werner Koch + + * src/server.c (cmd_lookup): Send an END line after each + certificate. + +2003-11-28 Werner Koch + + * src/Makefile.am (dirmngr_LDADD): Remove DB_LIBS + because it never got defined and -ldb{2,4} is implictly set + by the AC_CHECK_LIB test in configure. + + * src/crlcache.c (mydbopen): DB4 needs an extra parameter; I + wonder who ever tested DB4 support. Add an error statement in + case no DB support is configured. + + * tests/Makefile.am: Don't use AM_CPPFLAGS but AM_CFLAGS, replaced + variables by configure templates. + * src/Makefile.am: Ditto. + +2003-11-19 Werner Koch + + * src/crlcache.c (list_one_crl_entry): Define X to nothing for non + DB4 systems. Thanks to Luca M. G. Centamore. + +2003-11-17 Werner Koch + + Released 0.5.0 + + * src/crlcache.c (crl_cache_new): Fixed eof detection. + + * src/server.c (cmd_loadcrl): Do the unescaping. + + * doc/dirmngr.texi: Added a history section for this modified + version. + +2003-11-14 Werner Koch + + * tests/asschk.c: New. Taken from GnuPG. + * tests/Makefile.am: Added asschk. + +2003-11-13 Werner Koch + + * src/ldap.c (fetch_next_cert_ldap): Get the pattern switching + right. + + * tests/test-dirmngr.c: Replaced a couple of deprecated types. + + * configure.ac (GPG_ERR_SOURCE_DEFAULT): Added. + (fopencookie, asprintf): Removed unneeded test. + (PRINTABLE_OS_NAME): Updated the test from gnupg. + (CFLAGS): Do full warnings only in maintainer mode. Add flag + --enable gcc-warnings to override it and to enable even more + warnings. + * acinclude.m4: Removed the libgcrypt test. + + * src/ldap.c (get_attr_from_result_ldap): Simplified the binary + hack and return a proper gpg error. + (attr_fetch_ldap_internal): Changed error handling. + (attr_fetch_ldap): Reworked. Return configuration error if no + servers are configured. + (url_fetch_ldap, add_server_to_servers) + (url_fetch_ldap_internal): Reworked. + (struct cert_fetch_context_s): New to get rid of a global state. + (start_cert_fetch_ldap): Allocate context and do a bind with a + timeout. Parse pattern. + (end_cert_fetch_ldap): Take context and don't return anything. + (find_next_pattern): Removed. + (parse_one_pattern): Redone. + (get_cert_ldap): Redone. + * src/server.c (cmd_lookup): Changed for changed fetch functions. + + * doc/dirmngr.texi: Reworked a bit to get rid of tex errors. + + * configure.ac: Enable makeinfo test. + + * src/crlcache.c (crl_cache_insert): Fixed for latest KSBA API + changes. + * tests/test-dirmngr.c (main): Ditto. Also added some more error + checking. + +2003-11-11 Werner Koch + + * src/cert.c (hashify_data, hexify_data, serial_hex) + (serial_to_buffer): Moved all to ... + * src/misc.c: .. here. + * src/Makefile.am (cert.c, cert.h): Removed. + * cert.c, cert.h: Removed. + + * m4/: New. + * configure.ac, Makefile.am: Include m4 directory support, updated + required library versions. + + * src/cert.c (make_cert): Removed. + + * src/ldap.c (fetch_next_cert_ldap): Return a gpg style error. + + * src/misc.h (copy_time): New. + * src/misc.c (get_isotime): New. + (iso_string2time, iso_time2string): Removed. + (unhexify): New. + + * src/crlcache.h (DBCONTENTSVERSION): Bumbed to 0.6. + * src/crlcache.c (finish_sig_check): New. Factored out from + crl_parse_insert and entirely redone. + (do_encode_md): Removed. + (print_time): Removed + (crl_cache_isvalid): Reworked. + +2003-11-10 Werner Koch + + * src/crlcache.c (make_db_val, parse_db_val): Removed. + + * src/cert.c (serial_to_buffer): New. + + * src/server.c (get_issuer_cert_local): Rewritten. + + * src/crlcache.c (crl_parse_insert): Rewritten. Takes now a CTRL + instead of the Assuan context. Changed caller accordingly. + (get_issuer_cert): Cleaned up. + + * src/crlfetch.c (crl_fetch): Changed VALUE to unsigned char* for + documentation reasons. Make sure that VALUE is released on error. + (crl_fetch_default, ca_cert_fetch): Ditto. + + * src/crlcache.c (release_cache): New. + (crl_cache_deinit): Use it here. + (crl_cache_flush): Redone. + (save_contents): Redone. + (crl_cache_list, list_one_crl_entry): Print error messages. + +2003-11-06 Werner Koch + + * src/crlcache.c (create_directory_if_needed, cleanup_cache_dir): + New. Factored out from crl_cache_new and mostly rewritten. + (crl_cache_new): Rewritten. + (next_line_from_file): New. + (find_entry): Cleaned up. + (crl_cache_deinit): Cleaned up. + + * src/dirmngr.c (dirmngr_init_default_ctrl): New stub. + * src/dirmngr.h (ctrl_t): New. + (DBG_ASSUAN,...): Added the usual debug test macros. + * src/server.c: Removed the GET_PTR cruft, replaced it by ctrl_t. + Removed the recursion flag. + (get_issuer_cert_local): Allow for arbitary large + certificates. 4096 is definitely too small. + (inquire_cert): Ditto. + (start_command_handler): Set a hello line and call the default + init function. + (cmd_isvalid): Rewritten. + (inquire_cert): Removed unused arg LINE. General cleanup. + (map_assuan_err,map_to_assuan_status): New. Taken from gnupg 1.9. + (cmd_lookup): Rewritten. + (cmd_loadcrl): Started to rewrite it. + +2003-10-29 Werner Koch + + * src/dirmngr.c (parse_ldapserver_file): Entirely rewritten. + (cleanup): New. + (main): Cleaned up. + +2003-10-28 Werner Koch + + * src/dirmngr.h: Renamed dirmngr_opt to opt. + + * src/dirmngr.c (parse_ldapserver_file, free_ldapservers_list): + Moved with this file. Cleaned up. Replaced too deep recursion in + the free function. + +2003-10-21 Werner Koch + + Changed all occurrences of assuan.h to use use the system provided + one. + * src/server.c (register_commands): Adjusted for Assuan API change. + +2003-08-14 Werner Koch + + * src/Makefile.am: s/LIBKSBA_/KSBA_/. Changed for external Assuan lib. + * tests/Makefile.am: Ditto. + + * configure.ac: Partly restructured, add standard checks for + required libraries, removed included libassuan. + * Makefile.am (SUBDIRS): Removed assuan becuase we now use the + libassuan package. + + * src/dirmngr.c (main): Properly initialize Libgcrypt and libksba. + +2003-08-13 Werner Koch + + * src/server.c (get_issuer_cert_local): Print error using + assuan_strerror. + + * src/crlcache.c (do_encode_md, start_sig_check): Adjust for + changed Libgcrypt API. + +2003-06-19 Steffen Hansen + + * configure.ac: Upped version to 0.4.7-cvs. + +2003-06-19 Steffen Hansen + + * configure.ac: Release 0.4.6. + +2003-06-17 Bernhard Reiter + + * src/ldap.c (url_fetch_ldap()): + try other default servers when an url with hostname failed + * AUTHORS: added Steffen and Werner + * THANKS: Thanked people in the ChangeLog and the Ägypten-Team + + +2003-06-16 Steffen Hansen + + * configure.ac, src/crlcache.h, src/crlcache.c: Added db4 support. + * src/Makefile.am, tests/Makefile.am: Removed automake warning. + * tests/test-dirmngr.c: Removed a warning. + +2003-05-12 Steffen Hansen + + * doc/Makefile.am: Added dirmngr.ops to DISTCLEANFILES. + * ChangeLog, doc/ChangeLog, src/ChangeLog: Merged dirmngr ChangeLogs + into one toplevel file. + * acinclude.m4, configure.ac: Renamed PFX to PATH for consistency. + +2003-05-12 Steffen Hansen + + * src/ldap.c: Fixed end-of-certificates-list indication. + +2003-05-08 Steffen Hansen + + * src/server.c: Fixed iteration over server list + +2003-02-23 Steffen Hansen + + * src/crlcache.h, src/crlcache.c, src/dirmngr.c: Implemented --flush command. + +2003-02-07 Marcus Brinkmann + + * configure.ac: Release 0.4.4. + +2003-02-05 Steffen Hansen + + * src/ldap.c: Try harder with and without ";binary" in the + attribute name when fetching certificates. + * src/ldap.c, src/server.c: Support multiple userCertificate attributes + per entry. + +2003-02-04 Steffen Hansen + + * src/ldap.c: Include the sn attribute in the search filter. + Better log messages. + +2002-11-20 Steffen Hansen + + * Doc updates (fixes #1373) + * Fix for #1419 (crash in free_ldapservers_list()) + * Fix for #1375. Dirmngr now asks back with an INQUIRE SENDCERT before + querying the LDAP servers for an issuer certificate to validate a CRL + +2002-11-12 Werner Koch + + * config.sub, config.guess: Updated from ftp.gnu.org/gnu/config + to version 2002-11-08. + +2002-11-12 Werner Koch + + * dirmngr.c (main) : Better pass NULL instead + of an unitialized Assuan context. Let's hope that the other + functions can cope with this. + +2002-10-25 Bernhard Reiter + + * src/ldap.c (get_attr_from_result_ldap()): + added value extraction retry for CRLs and Certs without ";binary" + * changed version number to reflect cvs status to "0.4.3-cvs" + +2002-08-21 Werner Koch + + * dirmngr.c (main): Changed default homedir to .gnupg. + +2002-08-07 Steffen Hansen + + * Added configure check to examine whether db2 cursor() uses 3 or + 4 parameters. + +2002-07-31 Werner Koch + + * doc/dirmngr.texi: Fixed the structure and added menu entries + for the other nodes. + +2002-07-30 Steffen Hansen + + * Added doc dir and first steps towards manual. + +2002-07-29 Steffen Hansen + + * Got rid of the default server for CRL lookup. We now use the + same list of servers that we use for cert. lookup. + +2002-07-29 Steffen Hansen + + * New option --add-servers to allow dirmngr to add LDAP servers + found in CRL distribution points to the list of servers it + searches. NOTE: The added servers are only active in the currently + running dirmngr -- the info isn't written to persistens storage. + +2002-07-26 Steffen Hansen + + * Default LDAP timeout is 100 seconds now. + + * Use DB2 instead of DB1. Check for libresolv, fixed bug when + libldap was found in the default search path. + +2002-07-22 Steffen Hansen + + * Implemented --load-crl option. Also available as + LOADCRL assuan command when in server mode. + +2002-07-22 Steffen Hansen + + * Implemented new option --ldaptimeout to specify the number of seconds to + wait for an LDAP request before timeout. + + * Added --list-crls option to print the contents of the CRL cache + * Added some items to the dbcontents file to make printout nicer + and updated it's version number + +2002-07-02 Werner Koch + + * crlcache.c (crl_parse_insert): Fixed log_debug format string. + +2002-07-02 Steffen Hansen + + * configure.ac: Use DB->get() return value correctly. + +2002-06-28 Werner Koch + + * crlcache.c (crl_parse_insert): Keep track of newly allocated + ENTRY so that we don't free existing errors after a bad signature. + + * dirmngr.h: Include prototype for start_command_handler. + + * crlfetch.c, crlcache.c, http.c, cert.c, ldap.c: Include + config.h. + + * crlcache.c (crl_parse_insert): Fixed format type specifiers for + time_t variables in log_debug. + + * error.h: Use log_debug instead of dirmngr_debug. Changed all + callers. + * Makefile.am (dirmngr_SOURCES): Removed error.c + + * dirmngr.c (main): Register gcrypt malloc functions with ksba so + that we don't run into problems by using the wrong free function. + The gcrypt malloc function have the additional benefit of a + providing allocation sanity checks when compiled with that + feature. + + * crlcache.c (get_issuer_cert): Use xfree instead of ksba_free. + + +2002-06-27 Steffen Hansen + + * ldap.c: Look for both userCertificate and caCertificate + +2002-06-26 Steffen Hansen + + * configure.ac: Upped version number to 0.3.1 + +2002-06-25 Werner Koch + + * server.c (cmd_lookup): Use assuan_write_status which ensures a + correct syntax. + +2002-06-20 Werner Koch + + * crlcache.c (crl_cache_isvalid): Started with some nicer logging. + However, this will need a lot more work. + (get_issuer_cert): Ditto. + + * dirmngr.c (main): Changed required libgcrypt version and don't + print the prefix when using a logfile. + +2002-06-20 Werner Koch + + * tests/Makefile.am (TESTS): Removed test-dirmngr because it + is not a proper test program. + (EXTRA_DIST): Removed the non-existent test certificate. + +2002-05-21 Werner Koch + + * server.c (start_command_handler): Enable assuan debugging. + +2002-05-08 Steffen Hansen + + * Replaced gdbm check with db1 check + +2002-05-08 Steffen Hansen + + * Replaced gdbm with db1, updated file format version + +2002-03-01 Steffen Hansen + + * Added gdbm configure check + +2002-01-23 Steffen Hansen + + * Return ASSUAN_CRL_Too_Old if the CRL is too old + + +2002-01-17 Steffen Hansen + + Added commandline options --ldapserver --ldapport + --ldapuser --ldappassword . + + Cleaned up CRL parsing, signature evaluation a bit, changed + datetime format in config file to ISO, added version string to + contents format and cache file clean up code in case of mismatch. + +2002-01-14 Steffen Hansen + + * Use dirmngr_opt.homedir for storing the db. Added Makefile.am to + tests, bugfixes. + + * First code. + Things that work: + Loading/saving database (paths hardcoded) + Fetching CRL from hardcoded server, parsing and inserting in database + Answer ISVALID xxx.yyy requests + + Things that are missing: + Some error-checking/handling + Proper autoconf handling of gdbm and OpenLDAP + Signature checking downloaded CRLs + Answer LOOKUP requests + ... + + How to test: + cd tests + ldapsearch -v -x -h www.trustcenter.de -b '' userCertificate -t + cp /tmp/ testcert.der + ./test-dirmngr + +==END OLDEST CHANGELOG== + + Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am new file mode 100644 index 0000000..d3f89bc --- /dev/null +++ b/dirmngr/Makefile.am @@ -0,0 +1,161 @@ +# Makefile.am - dirmngr +# Copyright (C) 2002 Klarälvdalens Datakonsult AB +# Copyright (C) 2004, 2007, 2010 g10 Code GmbH +# +# 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 3 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, see . + +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = OAUTHORS ONEWS ChangeLog-2011 tls-ca.pem +dist_pkgdata_DATA = sks-keyservers.netCA.pem + +bin_PROGRAMS = dirmngr dirmngr-client + +if USE_LDAPWRAPPER +libexec_PROGRAMS = dirmngr_ldap +endif + +noinst_PROGRAMS = $(module_tests) $(module_net_tests) $(module_maint_tests) +TESTS = $(module_tests) $(module_net_tests) + +AM_CPPFLAGS = -I$(top_srcdir)/common + +include $(top_srcdir)/am/cmacros.am + +AM_CFLAGS = $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \ + $(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(NTBTLS_CFLAGS) \ + $(LIBGNUTLS_CFLAGS) + + +if HAVE_W32_SYSTEM +ldap_url = ldap-url.h ldap-url.c +else +ldap_url = +endif + +if USE_LDAPWRAPPER +extraldap_src = ldap-wrapper.c +else +extraldap_src = ldap-wrapper-ce.c dirmngr_ldap.c +endif + +noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h + +dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ + certcache.c certcache.h \ + loadswdb.c \ + cdb.h cdblib.c misc.c dirmngr-err.h \ + ocsp.c ocsp.h validate.c validate.h \ + dns-stuff.c dns-stuff.h \ + http.c http.h \ + ks-action.c ks-action.h ks-engine.h \ + ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c + +if USE_LIBDNS +dirmngr_SOURCES += dns.c dns.h +endif + +if USE_LDAP +dirmngr_SOURCES += ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \ + ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \ + ks-engine-ldap.c $(ldap_url) $(extraldap_src) +ldaplibs = $(LDAPLIBS) +else +ldaplibs = +endif + + +dirmngr_LDADD = $(libcommonpth) \ + $(DNSLIBS) $(LIBASSUAN_LIBS) \ + $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(NPTH_LIBS) \ + $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(LIBINTL) $(LIBICONV) +if USE_LDAP +dirmngr_LDADD += $(ldaplibs) +endif +if !USE_LDAPWRAPPER +dirmngr_LDADD += $(ldaplibs) +endif +dirmngr_LDFLAGS = $(extra_bin_ldflags) + +if USE_LDAPWRAPPER +dirmngr_ldap_SOURCES = dirmngr_ldap.c $(ldap_url) +dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) +dirmngr_ldap_LDFLAGS = +dirmngr_ldap_LDADD = $(libcommon) \ + $(GPG_ERROR_LIBS) $(LIBGCRYPT_LIBS) $(LDAPLIBS) \ + $(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS) +endif + +dirmngr_client_SOURCES = dirmngr-client.c +dirmngr_client_LDADD = $(libcommon) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV) +dirmngr_client_LDFLAGS = $(extra_bin_ldflags) + + +t_common_src = t-support.h +if USE_LIBDNS +t_common_src += dns.c dns.h +endif +t_common_ldadd = $(libcommon) $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \ + $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \ + $(DNSLIBS) $(LIBINTL) $(LIBICONV) + +module_tests = + +if USE_LDAP +module_tests += t-ldap-parse-uri +endif + +# Test which need a network connections are only used in maintainer mode. +if MAINTAINER_MODE +module_net_tests = t-dns-stuff +else +module_net_tests = +endif + +# Tests which are only for manually testing are only build in maintainer-mode. +if MAINTAINER_MODE +module_maint_tests = t-http +else +module_maint_tests = +endif + + +# http tests +t_http_SOURCES = $(t_common_src) t-http.c http.c dns-stuff.c +t_http_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \ + $(GPG_ERROR_CFLAGS) +t_http_LDADD = $(t_common_ldadd) \ + $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS) + +t_ldap_parse_uri_SOURCES = \ + t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \ + http.c dns-stuff.c \ + $(ldap_url) $(t_common_src) +t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) +t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS) + +t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) +t_dns_stuff_SOURCES = $(t_common_src) t-dns-stuff.c dns-stuff.c +t_dns_stuff_LDADD = $(t_common_ldadd) $(DNSLIBS) + +$(PROGRAMS) : $(libcommon) $(libcommonpth) diff --git a/dirmngr/Makefile.in b/dirmngr/Makefile.in new file mode 100644 index 0000000..2b9085e --- /dev/null +++ b/dirmngr/Makefile.in @@ -0,0 +1,1439 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# 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. + +@SET_MAKE@ + +# Makefile.am - dirmngr +# Copyright (C) 2002 Klarälvdalens Datakonsult AB +# Copyright (C) 2004, 2007, 2010 g10 Code GmbH +# +# 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 3 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, see . + +# cmacros.am - C macro definitions +# 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 3 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, see . + + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = dirmngr$(EXEEXT) dirmngr-client$(EXEEXT) +@USE_LDAPWRAPPER_TRUE@libexec_PROGRAMS = dirmngr_ldap$(EXEEXT) +noinst_PROGRAMS = $(am__EXEEXT_2) $(am__EXEEXT_3) $(am__EXEEXT_4) +TESTS = $(am__EXEEXT_2) $(am__EXEEXT_3) +DIST_COMMON = $(top_srcdir)/am/cmacros.am $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/build-aux/mkinstalldirs \ + $(top_srcdir)/build-aux/depcomp $(dist_pkgdata_DATA) \ + $(noinst_HEADERS) +@HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_BINDIR="\"$(bindir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\"" + + +# If a specific protect tool program has been defined, pass its name +# to cc. Note that these macros should not be used directly but via +# the gnupg_module_name function. +@GNUPG_AGENT_PGM_TRUE@am__append_2 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\"" +@GNUPG_PINENTRY_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\"" +@GNUPG_SCDAEMON_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\"" +@GNUPG_DIRMNGR_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\"" +@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\"" +@GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\"" +@USE_LIBDNS_TRUE@am__append_8 = dns.c dns.h +@USE_LDAP_TRUE@am__append_9 = ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \ +@USE_LDAP_TRUE@ ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \ +@USE_LDAP_TRUE@ ks-engine-ldap.c $(ldap_url) $(extraldap_src) + +@USE_LDAP_TRUE@am__append_10 = $(ldaplibs) +@USE_LDAPWRAPPER_FALSE@am__append_11 = $(ldaplibs) +@USE_LIBDNS_TRUE@am__append_12 = dns.c dns.h +@USE_LDAP_TRUE@am__append_13 = t-ldap-parse-uri +subdir = dirmngr +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ + $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \ + "$(DESTDIR)$(pkgdatadir)" +@USE_LDAP_TRUE@am__EXEEXT_1 = t-ldap-parse-uri$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +@MAINTAINER_MODE_TRUE@am__EXEEXT_3 = t-dns-stuff$(EXEEXT) +@MAINTAINER_MODE_TRUE@am__EXEEXT_4 = t-http$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(noinst_PROGRAMS) +am__dirmngr_SOURCES_DIST = dirmngr.c dirmngr.h server.c crlcache.c \ + crlfetch.c certcache.c certcache.h loadswdb.c cdb.h cdblib.c \ + misc.c dirmngr-err.h ocsp.c ocsp.h validate.c validate.h \ + dns-stuff.c dns-stuff.h http.c http.h ks-action.c ks-action.h \ + ks-engine.h ks-engine-hkp.c ks-engine-http.c \ + ks-engine-finger.c ks-engine-kdns.c dns.c dns.h ldapserver.h \ + ldapserver.c ldap.c w32-ldap-help.h ldap-wrapper.h \ + ldap-parse-uri.c ldap-parse-uri.h ks-engine-ldap.c ldap-url.h \ + ldap-url.c ldap-wrapper-ce.c dirmngr_ldap.c ldap-wrapper.c +@USE_LIBDNS_TRUE@am__objects_1 = dns.$(OBJEXT) +@HAVE_W32_SYSTEM_TRUE@am__objects_2 = ldap-url.$(OBJEXT) +@USE_LDAPWRAPPER_FALSE@am__objects_3 = ldap-wrapper-ce.$(OBJEXT) \ +@USE_LDAPWRAPPER_FALSE@ dirmngr_ldap.$(OBJEXT) +@USE_LDAPWRAPPER_TRUE@am__objects_3 = ldap-wrapper.$(OBJEXT) +@USE_LDAP_TRUE@am__objects_4 = ldapserver.$(OBJEXT) ldap.$(OBJEXT) \ +@USE_LDAP_TRUE@ ldap-parse-uri.$(OBJEXT) \ +@USE_LDAP_TRUE@ ks-engine-ldap.$(OBJEXT) $(am__objects_2) \ +@USE_LDAP_TRUE@ $(am__objects_3) +am_dirmngr_OBJECTS = dirmngr.$(OBJEXT) server.$(OBJEXT) \ + crlcache.$(OBJEXT) crlfetch.$(OBJEXT) certcache.$(OBJEXT) \ + loadswdb.$(OBJEXT) cdblib.$(OBJEXT) misc.$(OBJEXT) \ + ocsp.$(OBJEXT) validate.$(OBJEXT) dns-stuff.$(OBJEXT) \ + http.$(OBJEXT) ks-action.$(OBJEXT) ks-engine-hkp.$(OBJEXT) \ + ks-engine-http.$(OBJEXT) ks-engine-finger.$(OBJEXT) \ + ks-engine-kdns.$(OBJEXT) $(am__objects_1) $(am__objects_4) +dirmngr_OBJECTS = $(am_dirmngr_OBJECTS) +am__DEPENDENCIES_1 = +@USE_LDAP_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +@USE_LDAP_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) +@USE_LDAPWRAPPER_FALSE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_2) +dirmngr_DEPENDENCIES = $(libcommonpth) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4) +dirmngr_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(dirmngr_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_dirmngr_client_OBJECTS = dirmngr-client.$(OBJEXT) +dirmngr_client_OBJECTS = $(am_dirmngr_client_OBJECTS) +dirmngr_client_DEPENDENCIES = $(libcommon) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +dirmngr_client_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(dirmngr_client_LDFLAGS) $(LDFLAGS) -o $@ +am__dirmngr_ldap_SOURCES_DIST = dirmngr_ldap.c ldap-url.h ldap-url.c +@HAVE_W32_SYSTEM_TRUE@am__objects_5 = dirmngr_ldap-ldap-url.$(OBJEXT) +@USE_LDAPWRAPPER_TRUE@am_dirmngr_ldap_OBJECTS = \ +@USE_LDAPWRAPPER_TRUE@ dirmngr_ldap-dirmngr_ldap.$(OBJEXT) \ +@USE_LDAPWRAPPER_TRUE@ $(am__objects_5) +dirmngr_ldap_OBJECTS = $(am_dirmngr_ldap_OBJECTS) +@USE_LDAPWRAPPER_TRUE@dirmngr_ldap_DEPENDENCIES = $(libcommon) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) \ +@USE_LDAPWRAPPER_TRUE@ $(am__DEPENDENCIES_1) +dirmngr_ldap_LINK = $(CCLD) $(dirmngr_ldap_CFLAGS) $(CFLAGS) \ + $(dirmngr_ldap_LDFLAGS) $(LDFLAGS) -o $@ +am__t_dns_stuff_SOURCES_DIST = t-support.h dns.c dns.h t-dns-stuff.c \ + dns-stuff.c +@USE_LIBDNS_TRUE@am__objects_6 = t_dns_stuff-dns.$(OBJEXT) +am__objects_7 = $(am__objects_6) +am_t_dns_stuff_OBJECTS = $(am__objects_7) \ + t_dns_stuff-t-dns-stuff.$(OBJEXT) \ + t_dns_stuff-dns-stuff.$(OBJEXT) +t_dns_stuff_OBJECTS = $(am_t_dns_stuff_OBJECTS) +am__DEPENDENCIES_5 = $(libcommon) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +t_dns_stuff_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) +t_dns_stuff_LINK = $(CCLD) $(t_dns_stuff_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__t_http_SOURCES_DIST = t-support.h dns.c dns.h t-http.c http.c \ + dns-stuff.c +@USE_LIBDNS_TRUE@am__objects_8 = t_http-dns.$(OBJEXT) +am__objects_9 = $(am__objects_8) +am_t_http_OBJECTS = $(am__objects_9) t_http-t-http.$(OBJEXT) \ + t_http-http.$(OBJEXT) t_http-dns-stuff.$(OBJEXT) +t_http_OBJECTS = $(am_t_http_OBJECTS) +t_http_DEPENDENCIES = $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +t_http_LINK = $(CCLD) $(t_http_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__t_ldap_parse_uri_SOURCES_DIST = t-ldap-parse-uri.c \ + ldap-parse-uri.c ldap-parse-uri.h http.c dns-stuff.c \ + ldap-url.h ldap-url.c t-support.h dns.c dns.h +@HAVE_W32_SYSTEM_TRUE@am__objects_10 = \ +@HAVE_W32_SYSTEM_TRUE@ t_ldap_parse_uri-ldap-url.$(OBJEXT) +@USE_LIBDNS_TRUE@am__objects_11 = t_ldap_parse_uri-dns.$(OBJEXT) +am__objects_12 = $(am__objects_11) +am_t_ldap_parse_uri_OBJECTS = \ + t_ldap_parse_uri-t-ldap-parse-uri.$(OBJEXT) \ + t_ldap_parse_uri-ldap-parse-uri.$(OBJEXT) \ + t_ldap_parse_uri-http.$(OBJEXT) \ + t_ldap_parse_uri-dns-stuff.$(OBJEXT) $(am__objects_10) \ + $(am__objects_12) +t_ldap_parse_uri_OBJECTS = $(am_t_ldap_parse_uri_OBJECTS) +t_ldap_parse_uri_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_5) $(am__DEPENDENCIES_1) +t_ldap_parse_uri_LINK = $(CCLD) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(dirmngr_SOURCES) $(dirmngr_client_SOURCES) \ + $(dirmngr_ldap_SOURCES) $(t_dns_stuff_SOURCES) \ + $(t_http_SOURCES) $(t_ldap_parse_uri_SOURCES) +DIST_SOURCES = $(am__dirmngr_SOURCES_DIST) $(dirmngr_client_SOURCES) \ + $(am__dirmngr_ldap_SOURCES_DIST) \ + $(am__t_dns_stuff_SOURCES_DIST) $(am__t_http_SOURCES_DIST) \ + $(am__t_ldap_parse_uri_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +DATA = $(dist_pkgdata_DATA) +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_FILEVERSION = @BUILD_FILEVERSION@ +BUILD_HOSTNAME = @BUILD_HOSTNAME@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BUILD_REVISION = @BUILD_REVISION@ +BUILD_TIMESTAMP = @BUILD_TIMESTAMP@ +BUILD_VERSION = @BUILD_VERSION@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DL_LIBS = @DL_LIBS@ +DNSLIBS = @DNSLIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENCFS = @ENCFS@ +EXEEXT = @EXEEXT@ +FUSERMOUNT = @FUSERMOUNT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@ +GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@ +GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@ +GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ +GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ +GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ +GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ +GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ +GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ +GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@ +GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KSBA_CFLAGS = @KSBA_CFLAGS@ +KSBA_CONFIG = @KSBA_CONFIG@ +KSBA_LIBS = @KSBA_LIBS@ +LBER_LIBS = @LBER_LIBS@ +LDAPLIBS = @LDAPLIBS@ +LDAP_CPPFLAGS = @LDAP_CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@ +LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@ +LIBASSUAN_LIBS = @LIBASSUAN_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ +LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBREADLINE = @LIBREADLINE@ +LIBS = @LIBS@ +LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBUTIL_LIBS = @LIBUTIL_LIBS@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NETLIBS = @NETLIBS@ +NPTH_CFLAGS = @NPTH_CFLAGS@ +NPTH_CONFIG = @NPTH_CONFIG@ +NPTH_LIBS = @NPTH_LIBS@ +NTBTLS_CFLAGS = @NTBTLS_CFLAGS@ +NTBTLS_CONFIG = @NTBTLS_CONFIG@ +NTBTLS_LIBS = @NTBTLS_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_GT = @PACKAGE_GT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHRED = @SHRED@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ +STRIP = @STRIP@ +SYSROOT = @SYSROOT@ +SYS_SOCKET_H = @SYS_SOCKET_H@ +TAR = @TAR@ +USE_C99_CFLAGS = @USE_C99_CFLAGS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +W32SOCKLIBS = @W32SOCKLIBS@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +ZLIBS = @ZLIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = $(datadir)/locale +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = OAUTHORS ONEWS ChangeLog-2011 tls-ca.pem +dist_pkgdata_DATA = sks-keyservers.netCA.pem + +# NB: AM_CFLAGS may also be used by tools running on the build +# platform to create source files. +AM_CPPFLAGS = -I$(top_srcdir)/common -DLOCALEDIR=\"$(localedir)\" \ + $(am__append_1) $(am__append_2) $(am__append_3) \ + $(am__append_4) $(am__append_5) $(am__append_6) \ + $(am__append_7) +@HAVE_W32CE_SYSTEM_FALSE@extra_sys_libs = + +# Under Windows we use LockFileEx. WindowsCE provides this only on +# the WindowsMobile 6 platform and thus we need to use the coredll6 +# import library. We also want to use a stacksize of 256k instead of +# the 2MB which is the default with cegcc. 256k is the largest stack +# we use with pth. +@HAVE_W32CE_SYSTEM_TRUE@extra_sys_libs = -lcoredll6 +@HAVE_W32CE_SYSTEM_FALSE@extra_bin_ldflags = +@HAVE_W32CE_SYSTEM_TRUE@extra_bin_ldflags = -Wl,--stack=0x40000 +resource_objs = + +# Convenience macros +libcommon = ../common/libcommon.a +libcommonpth = ../common/libcommonpth.a +libcommontls = ../common/libcommontls.a +libcommontlsnpth = ../common/libcommontlsnpth.a +AM_CFLAGS = $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \ + $(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(NTBTLS_CFLAGS) \ + $(LIBGNUTLS_CFLAGS) + +@HAVE_W32_SYSTEM_FALSE@ldap_url = +@HAVE_W32_SYSTEM_TRUE@ldap_url = ldap-url.h ldap-url.c +@USE_LDAPWRAPPER_FALSE@extraldap_src = ldap-wrapper-ce.c dirmngr_ldap.c +@USE_LDAPWRAPPER_TRUE@extraldap_src = ldap-wrapper.c +noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h +dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ + certcache.c certcache.h loadswdb.c cdb.h cdblib.c misc.c \ + dirmngr-err.h ocsp.c ocsp.h validate.c validate.h dns-stuff.c \ + dns-stuff.h http.c http.h ks-action.c ks-action.h ks-engine.h \ + ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c \ + ks-engine-kdns.c $(am__append_8) $(am__append_9) +@USE_LDAP_FALSE@ldaplibs = +@USE_LDAP_TRUE@ldaplibs = $(LDAPLIBS) +dirmngr_LDADD = $(libcommonpth) $(DNSLIBS) $(LIBASSUAN_LIBS) \ + $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(NPTH_LIBS) $(NTBTLS_LIBS) \ + $(LIBGNUTLS_LIBS) $(LIBINTL) $(LIBICONV) $(am__append_10) \ + $(am__append_11) +dirmngr_LDFLAGS = $(extra_bin_ldflags) +@USE_LDAPWRAPPER_TRUE@dirmngr_ldap_SOURCES = dirmngr_ldap.c $(ldap_url) +@USE_LDAPWRAPPER_TRUE@dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) +@USE_LDAPWRAPPER_TRUE@dirmngr_ldap_LDFLAGS = +@USE_LDAPWRAPPER_TRUE@dirmngr_ldap_LDADD = $(libcommon) \ +@USE_LDAPWRAPPER_TRUE@ $(GPG_ERROR_LIBS) $(LIBGCRYPT_LIBS) $(LDAPLIBS) \ +@USE_LDAPWRAPPER_TRUE@ $(LBER_LIBS) $(LIBINTL) $(LIBICONV) $(NETLIBS) + +dirmngr_client_SOURCES = dirmngr-client.c +dirmngr_client_LDADD = $(libcommon) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV) + +dirmngr_client_LDFLAGS = $(extra_bin_ldflags) +t_common_src = t-support.h $(am__append_12) +t_common_ldadd = $(libcommon) $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \ + $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \ + $(DNSLIBS) $(LIBINTL) $(LIBICONV) + +module_tests = $(am__append_13) +@MAINTAINER_MODE_FALSE@module_net_tests = + +# Test which need a network connections are only used in maintainer mode. +@MAINTAINER_MODE_TRUE@module_net_tests = t-dns-stuff +@MAINTAINER_MODE_FALSE@module_maint_tests = + +# Tests which are only for manually testing are only build in maintainer-mode. +@MAINTAINER_MODE_TRUE@module_maint_tests = t-http + +# http tests +t_http_SOURCES = $(t_common_src) t-http.c http.c dns-stuff.c +t_http_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \ + $(GPG_ERROR_CFLAGS) + +t_http_LDADD = $(t_common_ldadd) \ + $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS) + +t_ldap_parse_uri_SOURCES = \ + t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \ + http.c dns-stuff.c \ + $(ldap_url) $(t_common_src) + +t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) + +t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS) +t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ + $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) + +t_dns_stuff_SOURCES = $(t_common_src) t-dns-stuff.c dns-stuff.c +t_dns_stuff_LDADD = $(t_common_ldadd) $(DNSLIBS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj .rc +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dirmngr/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu dirmngr/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(top_srcdir)/am/cmacros.am: + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + -test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS) + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + +dirmngr$(EXEEXT): $(dirmngr_OBJECTS) $(dirmngr_DEPENDENCIES) $(EXTRA_dirmngr_DEPENDENCIES) + @rm -f dirmngr$(EXEEXT) + $(AM_V_CCLD)$(dirmngr_LINK) $(dirmngr_OBJECTS) $(dirmngr_LDADD) $(LIBS) + +dirmngr-client$(EXEEXT): $(dirmngr_client_OBJECTS) $(dirmngr_client_DEPENDENCIES) $(EXTRA_dirmngr_client_DEPENDENCIES) + @rm -f dirmngr-client$(EXEEXT) + $(AM_V_CCLD)$(dirmngr_client_LINK) $(dirmngr_client_OBJECTS) $(dirmngr_client_LDADD) $(LIBS) + +dirmngr_ldap$(EXEEXT): $(dirmngr_ldap_OBJECTS) $(dirmngr_ldap_DEPENDENCIES) $(EXTRA_dirmngr_ldap_DEPENDENCIES) + @rm -f dirmngr_ldap$(EXEEXT) + $(AM_V_CCLD)$(dirmngr_ldap_LINK) $(dirmngr_ldap_OBJECTS) $(dirmngr_ldap_LDADD) $(LIBS) + +t-dns-stuff$(EXEEXT): $(t_dns_stuff_OBJECTS) $(t_dns_stuff_DEPENDENCIES) $(EXTRA_t_dns_stuff_DEPENDENCIES) + @rm -f t-dns-stuff$(EXEEXT) + $(AM_V_CCLD)$(t_dns_stuff_LINK) $(t_dns_stuff_OBJECTS) $(t_dns_stuff_LDADD) $(LIBS) + +t-http$(EXEEXT): $(t_http_OBJECTS) $(t_http_DEPENDENCIES) $(EXTRA_t_http_DEPENDENCIES) + @rm -f t-http$(EXEEXT) + $(AM_V_CCLD)$(t_http_LINK) $(t_http_OBJECTS) $(t_http_LDADD) $(LIBS) + +t-ldap-parse-uri$(EXEEXT): $(t_ldap_parse_uri_OBJECTS) $(t_ldap_parse_uri_DEPENDENCIES) $(EXTRA_t_ldap_parse_uri_DEPENDENCIES) + @rm -f t-ldap-parse-uri$(EXEEXT) + $(AM_V_CCLD)$(t_ldap_parse_uri_LINK) $(t_ldap_parse_uri_OBJECTS) $(t_ldap_parse_uri_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdblib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certcache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crlcache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crlfetch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirmngr-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirmngr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirmngr_ldap-ldap-url.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirmngr_ldap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns-stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks-action.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks-engine-finger.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks-engine-hkp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks-engine-http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks-engine-kdns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks-engine-ldap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-parse-uri.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-url.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-wrapper-ce.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap-wrapper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldapserver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loadswdb.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_dns_stuff-dns-stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_dns_stuff-dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_dns_stuff-t-dns-stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_http-dns-stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_http-dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_http-http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_http-t-http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_ldap_parse_uri-dns-stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_ldap_parse_uri-dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_ldap_parse_uri-http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_ldap_parse_uri-ldap-url.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/validate.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +dirmngr_ldap-dirmngr_ldap.o: dirmngr_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -MT dirmngr_ldap-dirmngr_ldap.o -MD -MP -MF $(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Tpo -c -o dirmngr_ldap-dirmngr_ldap.o `test -f 'dirmngr_ldap.c' || echo '$(srcdir)/'`dirmngr_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Tpo $(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dirmngr_ldap.c' object='dirmngr_ldap-dirmngr_ldap.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -c -o dirmngr_ldap-dirmngr_ldap.o `test -f 'dirmngr_ldap.c' || echo '$(srcdir)/'`dirmngr_ldap.c + +dirmngr_ldap-dirmngr_ldap.obj: dirmngr_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -MT dirmngr_ldap-dirmngr_ldap.obj -MD -MP -MF $(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Tpo -c -o dirmngr_ldap-dirmngr_ldap.obj `if test -f 'dirmngr_ldap.c'; then $(CYGPATH_W) 'dirmngr_ldap.c'; else $(CYGPATH_W) '$(srcdir)/dirmngr_ldap.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Tpo $(DEPDIR)/dirmngr_ldap-dirmngr_ldap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dirmngr_ldap.c' object='dirmngr_ldap-dirmngr_ldap.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -c -o dirmngr_ldap-dirmngr_ldap.obj `if test -f 'dirmngr_ldap.c'; then $(CYGPATH_W) 'dirmngr_ldap.c'; else $(CYGPATH_W) '$(srcdir)/dirmngr_ldap.c'; fi` + +dirmngr_ldap-ldap-url.o: ldap-url.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -MT dirmngr_ldap-ldap-url.o -MD -MP -MF $(DEPDIR)/dirmngr_ldap-ldap-url.Tpo -c -o dirmngr_ldap-ldap-url.o `test -f 'ldap-url.c' || echo '$(srcdir)/'`ldap-url.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dirmngr_ldap-ldap-url.Tpo $(DEPDIR)/dirmngr_ldap-ldap-url.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap-url.c' object='dirmngr_ldap-ldap-url.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -c -o dirmngr_ldap-ldap-url.o `test -f 'ldap-url.c' || echo '$(srcdir)/'`ldap-url.c + +dirmngr_ldap-ldap-url.obj: ldap-url.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -MT dirmngr_ldap-ldap-url.obj -MD -MP -MF $(DEPDIR)/dirmngr_ldap-ldap-url.Tpo -c -o dirmngr_ldap-ldap-url.obj `if test -f 'ldap-url.c'; then $(CYGPATH_W) 'ldap-url.c'; else $(CYGPATH_W) '$(srcdir)/ldap-url.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dirmngr_ldap-ldap-url.Tpo $(DEPDIR)/dirmngr_ldap-ldap-url.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap-url.c' object='dirmngr_ldap-ldap-url.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dirmngr_ldap_CFLAGS) $(CFLAGS) -c -o dirmngr_ldap-ldap-url.obj `if test -f 'ldap-url.c'; then $(CYGPATH_W) 'ldap-url.c'; else $(CYGPATH_W) '$(srcdir)/ldap-url.c'; fi` + +t_dns_stuff-dns.o: dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -MT t_dns_stuff-dns.o -MD -MP -MF $(DEPDIR)/t_dns_stuff-dns.Tpo -c -o t_dns_stuff-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_dns_stuff-dns.Tpo $(DEPDIR)/t_dns_stuff-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns.c' object='t_dns_stuff-dns.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -c -o t_dns_stuff-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c + +t_dns_stuff-dns.obj: dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -MT t_dns_stuff-dns.obj -MD -MP -MF $(DEPDIR)/t_dns_stuff-dns.Tpo -c -o t_dns_stuff-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_dns_stuff-dns.Tpo $(DEPDIR)/t_dns_stuff-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns.c' object='t_dns_stuff-dns.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -c -o t_dns_stuff-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` + +t_dns_stuff-t-dns-stuff.o: t-dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -MT t_dns_stuff-t-dns-stuff.o -MD -MP -MF $(DEPDIR)/t_dns_stuff-t-dns-stuff.Tpo -c -o t_dns_stuff-t-dns-stuff.o `test -f 't-dns-stuff.c' || echo '$(srcdir)/'`t-dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_dns_stuff-t-dns-stuff.Tpo $(DEPDIR)/t_dns_stuff-t-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-dns-stuff.c' object='t_dns_stuff-t-dns-stuff.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -c -o t_dns_stuff-t-dns-stuff.o `test -f 't-dns-stuff.c' || echo '$(srcdir)/'`t-dns-stuff.c + +t_dns_stuff-t-dns-stuff.obj: t-dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -MT t_dns_stuff-t-dns-stuff.obj -MD -MP -MF $(DEPDIR)/t_dns_stuff-t-dns-stuff.Tpo -c -o t_dns_stuff-t-dns-stuff.obj `if test -f 't-dns-stuff.c'; then $(CYGPATH_W) 't-dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/t-dns-stuff.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_dns_stuff-t-dns-stuff.Tpo $(DEPDIR)/t_dns_stuff-t-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-dns-stuff.c' object='t_dns_stuff-t-dns-stuff.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -c -o t_dns_stuff-t-dns-stuff.obj `if test -f 't-dns-stuff.c'; then $(CYGPATH_W) 't-dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/t-dns-stuff.c'; fi` + +t_dns_stuff-dns-stuff.o: dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -MT t_dns_stuff-dns-stuff.o -MD -MP -MF $(DEPDIR)/t_dns_stuff-dns-stuff.Tpo -c -o t_dns_stuff-dns-stuff.o `test -f 'dns-stuff.c' || echo '$(srcdir)/'`dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_dns_stuff-dns-stuff.Tpo $(DEPDIR)/t_dns_stuff-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns-stuff.c' object='t_dns_stuff-dns-stuff.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -c -o t_dns_stuff-dns-stuff.o `test -f 'dns-stuff.c' || echo '$(srcdir)/'`dns-stuff.c + +t_dns_stuff-dns-stuff.obj: dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -MT t_dns_stuff-dns-stuff.obj -MD -MP -MF $(DEPDIR)/t_dns_stuff-dns-stuff.Tpo -c -o t_dns_stuff-dns-stuff.obj `if test -f 'dns-stuff.c'; then $(CYGPATH_W) 'dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/dns-stuff.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_dns_stuff-dns-stuff.Tpo $(DEPDIR)/t_dns_stuff-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns-stuff.c' object='t_dns_stuff-dns-stuff.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_dns_stuff_CFLAGS) $(CFLAGS) -c -o t_dns_stuff-dns-stuff.obj `if test -f 'dns-stuff.c'; then $(CYGPATH_W) 'dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/dns-stuff.c'; fi` + +t_http-dns.o: dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-dns.o -MD -MP -MF $(DEPDIR)/t_http-dns.Tpo -c -o t_http-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-dns.Tpo $(DEPDIR)/t_http-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns.c' object='t_http-dns.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c + +t_http-dns.obj: dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-dns.obj -MD -MP -MF $(DEPDIR)/t_http-dns.Tpo -c -o t_http-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-dns.Tpo $(DEPDIR)/t_http-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns.c' object='t_http-dns.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` + +t_http-t-http.o: t-http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-t-http.o -MD -MP -MF $(DEPDIR)/t_http-t-http.Tpo -c -o t_http-t-http.o `test -f 't-http.c' || echo '$(srcdir)/'`t-http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-t-http.Tpo $(DEPDIR)/t_http-t-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-http.c' object='t_http-t-http.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-t-http.o `test -f 't-http.c' || echo '$(srcdir)/'`t-http.c + +t_http-t-http.obj: t-http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-t-http.obj -MD -MP -MF $(DEPDIR)/t_http-t-http.Tpo -c -o t_http-t-http.obj `if test -f 't-http.c'; then $(CYGPATH_W) 't-http.c'; else $(CYGPATH_W) '$(srcdir)/t-http.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-t-http.Tpo $(DEPDIR)/t_http-t-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-http.c' object='t_http-t-http.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-t-http.obj `if test -f 't-http.c'; then $(CYGPATH_W) 't-http.c'; else $(CYGPATH_W) '$(srcdir)/t-http.c'; fi` + +t_http-http.o: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-http.o -MD -MP -MF $(DEPDIR)/t_http-http.Tpo -c -o t_http-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-http.Tpo $(DEPDIR)/t_http-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='t_http-http.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c + +t_http-http.obj: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-http.obj -MD -MP -MF $(DEPDIR)/t_http-http.Tpo -c -o t_http-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-http.Tpo $(DEPDIR)/t_http-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='t_http-http.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` + +t_http-dns-stuff.o: dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-dns-stuff.o -MD -MP -MF $(DEPDIR)/t_http-dns-stuff.Tpo -c -o t_http-dns-stuff.o `test -f 'dns-stuff.c' || echo '$(srcdir)/'`dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-dns-stuff.Tpo $(DEPDIR)/t_http-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns-stuff.c' object='t_http-dns-stuff.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-dns-stuff.o `test -f 'dns-stuff.c' || echo '$(srcdir)/'`dns-stuff.c + +t_http-dns-stuff.obj: dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -MT t_http-dns-stuff.obj -MD -MP -MF $(DEPDIR)/t_http-dns-stuff.Tpo -c -o t_http-dns-stuff.obj `if test -f 'dns-stuff.c'; then $(CYGPATH_W) 'dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/dns-stuff.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_http-dns-stuff.Tpo $(DEPDIR)/t_http-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns-stuff.c' object='t_http-dns-stuff.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_http_CFLAGS) $(CFLAGS) -c -o t_http-dns-stuff.obj `if test -f 'dns-stuff.c'; then $(CYGPATH_W) 'dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/dns-stuff.c'; fi` + +t_ldap_parse_uri-t-ldap-parse-uri.o: t-ldap-parse-uri.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-t-ldap-parse-uri.o -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Tpo -c -o t_ldap_parse_uri-t-ldap-parse-uri.o `test -f 't-ldap-parse-uri.c' || echo '$(srcdir)/'`t-ldap-parse-uri.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Tpo $(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-ldap-parse-uri.c' object='t_ldap_parse_uri-t-ldap-parse-uri.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-t-ldap-parse-uri.o `test -f 't-ldap-parse-uri.c' || echo '$(srcdir)/'`t-ldap-parse-uri.c + +t_ldap_parse_uri-t-ldap-parse-uri.obj: t-ldap-parse-uri.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-t-ldap-parse-uri.obj -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Tpo -c -o t_ldap_parse_uri-t-ldap-parse-uri.obj `if test -f 't-ldap-parse-uri.c'; then $(CYGPATH_W) 't-ldap-parse-uri.c'; else $(CYGPATH_W) '$(srcdir)/t-ldap-parse-uri.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Tpo $(DEPDIR)/t_ldap_parse_uri-t-ldap-parse-uri.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-ldap-parse-uri.c' object='t_ldap_parse_uri-t-ldap-parse-uri.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-t-ldap-parse-uri.obj `if test -f 't-ldap-parse-uri.c'; then $(CYGPATH_W) 't-ldap-parse-uri.c'; else $(CYGPATH_W) '$(srcdir)/t-ldap-parse-uri.c'; fi` + +t_ldap_parse_uri-ldap-parse-uri.o: ldap-parse-uri.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-ldap-parse-uri.o -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Tpo -c -o t_ldap_parse_uri-ldap-parse-uri.o `test -f 'ldap-parse-uri.c' || echo '$(srcdir)/'`ldap-parse-uri.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Tpo $(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap-parse-uri.c' object='t_ldap_parse_uri-ldap-parse-uri.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-ldap-parse-uri.o `test -f 'ldap-parse-uri.c' || echo '$(srcdir)/'`ldap-parse-uri.c + +t_ldap_parse_uri-ldap-parse-uri.obj: ldap-parse-uri.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-ldap-parse-uri.obj -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Tpo -c -o t_ldap_parse_uri-ldap-parse-uri.obj `if test -f 'ldap-parse-uri.c'; then $(CYGPATH_W) 'ldap-parse-uri.c'; else $(CYGPATH_W) '$(srcdir)/ldap-parse-uri.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Tpo $(DEPDIR)/t_ldap_parse_uri-ldap-parse-uri.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap-parse-uri.c' object='t_ldap_parse_uri-ldap-parse-uri.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-ldap-parse-uri.obj `if test -f 'ldap-parse-uri.c'; then $(CYGPATH_W) 'ldap-parse-uri.c'; else $(CYGPATH_W) '$(srcdir)/ldap-parse-uri.c'; fi` + +t_ldap_parse_uri-http.o: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-http.o -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-http.Tpo -c -o t_ldap_parse_uri-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-http.Tpo $(DEPDIR)/t_ldap_parse_uri-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='t_ldap_parse_uri-http.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c + +t_ldap_parse_uri-http.obj: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-http.obj -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-http.Tpo -c -o t_ldap_parse_uri-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-http.Tpo $(DEPDIR)/t_ldap_parse_uri-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='t_ldap_parse_uri-http.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` + +t_ldap_parse_uri-dns-stuff.o: dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-dns-stuff.o -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-dns-stuff.Tpo -c -o t_ldap_parse_uri-dns-stuff.o `test -f 'dns-stuff.c' || echo '$(srcdir)/'`dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-dns-stuff.Tpo $(DEPDIR)/t_ldap_parse_uri-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns-stuff.c' object='t_ldap_parse_uri-dns-stuff.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-dns-stuff.o `test -f 'dns-stuff.c' || echo '$(srcdir)/'`dns-stuff.c + +t_ldap_parse_uri-dns-stuff.obj: dns-stuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-dns-stuff.obj -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-dns-stuff.Tpo -c -o t_ldap_parse_uri-dns-stuff.obj `if test -f 'dns-stuff.c'; then $(CYGPATH_W) 'dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/dns-stuff.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-dns-stuff.Tpo $(DEPDIR)/t_ldap_parse_uri-dns-stuff.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns-stuff.c' object='t_ldap_parse_uri-dns-stuff.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-dns-stuff.obj `if test -f 'dns-stuff.c'; then $(CYGPATH_W) 'dns-stuff.c'; else $(CYGPATH_W) '$(srcdir)/dns-stuff.c'; fi` + +t_ldap_parse_uri-ldap-url.o: ldap-url.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-ldap-url.o -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-ldap-url.Tpo -c -o t_ldap_parse_uri-ldap-url.o `test -f 'ldap-url.c' || echo '$(srcdir)/'`ldap-url.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-ldap-url.Tpo $(DEPDIR)/t_ldap_parse_uri-ldap-url.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap-url.c' object='t_ldap_parse_uri-ldap-url.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-ldap-url.o `test -f 'ldap-url.c' || echo '$(srcdir)/'`ldap-url.c + +t_ldap_parse_uri-ldap-url.obj: ldap-url.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-ldap-url.obj -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-ldap-url.Tpo -c -o t_ldap_parse_uri-ldap-url.obj `if test -f 'ldap-url.c'; then $(CYGPATH_W) 'ldap-url.c'; else $(CYGPATH_W) '$(srcdir)/ldap-url.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-ldap-url.Tpo $(DEPDIR)/t_ldap_parse_uri-ldap-url.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap-url.c' object='t_ldap_parse_uri-ldap-url.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-ldap-url.obj `if test -f 'ldap-url.c'; then $(CYGPATH_W) 'ldap-url.c'; else $(CYGPATH_W) '$(srcdir)/ldap-url.c'; fi` + +t_ldap_parse_uri-dns.o: dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-dns.o -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-dns.Tpo -c -o t_ldap_parse_uri-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-dns.Tpo $(DEPDIR)/t_ldap_parse_uri-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns.c' object='t_ldap_parse_uri-dns.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-dns.o `test -f 'dns.c' || echo '$(srcdir)/'`dns.c + +t_ldap_parse_uri-dns.obj: dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -MT t_ldap_parse_uri-dns.obj -MD -MP -MF $(DEPDIR)/t_ldap_parse_uri-dns.Tpo -c -o t_ldap_parse_uri-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_ldap_parse_uri-dns.Tpo $(DEPDIR)/t_ldap_parse_uri-dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dns.c' object='t_ldap_parse_uri-dns.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_ldap_parse_uri_CFLAGS) $(CFLAGS) -c -o t_ldap_parse_uri-dns.obj `if test -f 'dns.c'; then $(CYGPATH_W) 'dns.c'; else $(CYGPATH_W) '$(srcdir)/dns.c'; fi` +install-dist_pkgdataDATA: $(dist_pkgdata_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgdatadir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgdatadir)" || exit $$?; \ + done + +uninstall-dist_pkgdataDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(PROGRAMS) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(pkgdatadir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libexecPROGRAMS \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_pkgdataDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libexecPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-dist_pkgdataDATA \ + uninstall-libexecPROGRAMS + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ + clean-binPROGRAMS clean-generic clean-libexecPROGRAMS \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am \ + install-dist_pkgdataDATA install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libexecPROGRAMS \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-dist_pkgdataDATA \ + uninstall-libexecPROGRAMS + + +@HAVE_W32_SYSTEM_TRUE@.rc.o: +@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" + +$(PROGRAMS) : $(libcommon) $(libcommonpth) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/dirmngr/OAUTHORS b/dirmngr/OAUTHORS new file mode 100644 index 0000000..7832449 --- /dev/null +++ b/dirmngr/OAUTHORS @@ -0,0 +1,38 @@ +The old AUTHORS file from the separate dirmngr package. + + Package: dirmngr + Maintainer: Werner Koch + Bug reports: bug-dirmngr@gnupg.org + Security related bug reports: security@gnupg.org + License: GPLv2+ + + +Steffen Hansen + - Initial code + +g10 Code GmbH + - All stuff written since October 2003. + +Werner Koch , + - Help with initial code. + +Free Software Foundation + - Code taken from GnuPG. + +Michael Tokarev + - src/cdb.h and src/cdblib.c from the public domain tinycdb 0.73. + + +The actual code is under the GNU GPL, except for src/cdb.h and +src/cdblib.h which are in the public domain. + + + Copyright 2003, 2004, 2006, 2007, 2008, 2010 g10 Code GmbH + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/dirmngr/ONEWS b/dirmngr/ONEWS new file mode 100644 index 0000000..cb20507 --- /dev/null +++ b/dirmngr/ONEWS @@ -0,0 +1,240 @@ +These are NEWS entries from the old separate dirmngr package + +Noteworthy changes in version 1.1.0 (unreleased) +------------------------------------------------ + + * Fixed a resource problem with LDAP CRLs. + + * Fixed a bad EOF detection with HTTP CRLs. + + * Made "dirmngr-client --url --load-crl URL" work. + + * New option --ignore-cert-extension. + + * Make use of libassuan 2.0 which is available as a DSO. + + +Noteworthy changes in version 1.0.3 (2009-06-17) +------------------------------------------------ + + * Client based trust anchors are now supported. + + * Configured certificates with the suffix ".der" are now also used. + + * Libgcrypt 1.4 is now required. + + +Noteworthy changes in version 1.0.2 (2008-07-31) +------------------------------------------------ + + * New option --url for the LOOKUP command and dirmngr-client. + + * The LOOKUP command does now also consults the local cache. New + option --cache-only for it and --local for dirmngr-client. + + * Port to Windows completed. + + * Improved certificate chain construction. + + * Support loading of PEM encoded CRLs via HTTP. + + +Noteworthy changes in version 1.0.1 (2007-08-16) +------------------------------------------------ + + * The option --ocsp-signer may now take a filename to allow several + certificates to be valid signers for the default responder. + + * New option --ocsp-max-period and improved the OCSP time checks. + + * New option --force-default-signer for dirmngr-client. + + * Ported to Windows. + + +Noteworthy changes in version 1.0.0 (2006-11-29) +------------------------------------------------ + + * Bumbed the version number. + + * Removed included gettext. We now require the system to provide a + suitable installation. + + +Noteworthy changes in version 0.9.7 (2006-11-17) +------------------------------------------------ + + * Internal cleanups. + + * Fixed updating of DIR.txt. Add additional diagnostics. + + * Updated gettext package. + + +Noteworthy changes in version 0.9.6 (2006-09-04) +------------------------------------------------ + + * A couple of bug fixes for OCSP. + + * OCSP does now make use of the responder ID and optionally included + certificates in the response to locate certificates. + + * No more lost file descriptors when loading CRLs via HTTP. + + * HTTP redirection for CRL and OCSP has been implemented. + + * Man pages are now build and installed from the texinfo source. + + +Noteworthy changes in version 0.9.5 (2006-06-27) +------------------------------------------------ + + * Fixed a problems with the CRL caching and CRL certificate + validation. + + * Improved diagnostics. + + +Noteworthy changes in version 0.9.4 (2006-05-16) +------------------------------------------------ + + * Try all names of each crlDP. + + * Don't shutdown the socket after sending the HTTP request. + + +Noteworthy changes in version 0.9.3 (2005-10-26) +------------------------------------------------ + + * Minor bug fixes. + + +Noteworthy changes in version 0.9.2 (2005-04-21) +------------------------------------------------ + + * Make use of authorityKeyidentifier.keyIdentifier. + + * Fixed a possible hang on exit. + + +Noteworthy changes in version 0.9.1 (2005-02-08) +------------------------------------------------ + + * New option --pem for dirmngr-client to allow requesting service + using a PEM encoded certificate. + + * New option --squid-mode to allow using dirmngr-client directly as a + Squid helper. + + * Bug fixes. + + +Noteworthy changes in version 0.9.0 (2004-12-17) +------------------------------------------------ + + * New option --daemon to start dirmngr as a system daemon. This + switches to the use of different directories and also does + CRL signing certificate validation on its own. + + * New tool dirmngr-client. + + * New options: --ldap-wrapper-program, --http-wrapper-program, + --disable-ldap, --disable-http, --honor-http-proxy, --http-proxy, + --ldap-proxy, --only-ldap-proxy, --ignore-ldap-dp and + --ignore-http-dp. + + * Uses an external ldap wrapper to cope with timeouts and general + LDAP problems. + + * SIGHUP may be used to reread the configuration and to flush the + certificate cache. + + * An authorithyKeyIdentifier in a CRL is now handled correctly. + + +Noteworthy changes in version 0.5.6 (2004-09-28) +------------------------------------------------ + + * LDAP fix. + + * Logging fixes. + + * Updated some configuration files. + + +Noteworthy changes in version 0.5.5 (2004-05-13) +------------------------------------------------ + + * Fixed the growing-dir.txt bug. + + * Better LDAP error logging. + + +Noteworthy changes in version 0.5.4 (2004-04-29) +------------------------------------------------ + + * New commands --ocsp-responder and --ocsp-signer to define a default + OCSP reponder if a certificate does not contain an assigned OCSP + responder. + + +Noteworthy changes in version 0.5.3 (2004-04-06) +------------------------------------------------ + + * Basic OCSP support. + + +Noteworthy changes in version 0.5.2 (2004-03-06) +------------------------------------------------ + + * New Assuan command LISTCRLS. + + * A couple of minor bug fixes. + + +Noteworthy changes in version 0.5.1 (2003-12-23) +------------------------------------------------ + +* New options --faked-system-time and --force. + +* Changed the name of the cache directory to $HOMEDIR/dirmngr-cache.d + and renamed the dbcontents file. You may delete the now obsolete + cache/ directory and the dbcontents file. + +* Dropped DB2 or DB4 use. There is no need for it because a constant + database fits our needs far better. + +* Experimental support for retrieving CRLs via http. + +* The --log-file option may now be used to print logs to a socket. + Prefix the socket name with "socket://" to enable this. This does + not work on all systems and falls back to stderr if there is a + problem with the socket. + + +Noteworthy changes in version 0.5.0 (2003-11-17) +------------------------------------------------ + +* Revamped the entire thing. + +* Does now require Libgcrypt 1.1.90 or higher, as well as the latest + libksba and libassuan. + +* Fixed a bug in the assuan inquire processing. + + +Noteworthy changes as of 2002-08-21 +------------------------------------ + +* The default home directory is now .gnupg + + + Copyright 2003, 2004, 2005 g10 Code GmbH + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/dirmngr/cdb.h b/dirmngr/cdb.h new file mode 100644 index 0000000..0c0d270 --- /dev/null +++ b/dirmngr/cdb.h @@ -0,0 +1,94 @@ +/* $Id: cdb.h 106 2003-12-12 17:36:49Z werner $ + * public cdb include file + * + * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru. + * Public domain. + * + * Taken from tinycdb-0.73. By Werner Koch 2003-12-12. + */ + +#ifndef TINYCDB_VERSION +#define TINYCDB_VERSION 0.73 + +typedef unsigned int cdbi_t; /*XXX should be at least 32 bits long */ + +/* common routines */ +cdbi_t cdb_hash(const void *buf, cdbi_t len); +cdbi_t cdb_unpack(const unsigned char buf[4]); +void cdb_pack(cdbi_t num, unsigned char buf[4]); + +struct cdb { + int cdb_fd; /* file descriptor */ + /* private members */ +#ifdef HAVE_W32_SYSTEM + void *cdb_mapping; /* Mapping handle. */ +#endif + cdbi_t cdb_fsize; /* datafile size */ + const unsigned char *cdb_mem; /* mmap'ed file memory */ + cdbi_t cdb_vpos, cdb_vlen; /* found data */ + cdbi_t cdb_kpos, cdb_klen; /* found key (only set if cdb_findinit + was called with KEY set to NULL). */ +}; + +#define cdb_datapos(c) ((c)->cdb_vpos) +#define cdb_datalen(c) ((c)->cdb_vlen) +#define cdb_keypos(c) ((c)->cdb_kpos) +#define cdb_keylen(c) ((c)->cdb_klen) +#define cdb_fileno(c) ((c)->cdb_fd) + +int cdb_init(struct cdb *cdbp, int fd); +void cdb_free(struct cdb *cdbp); + +int cdb_read(const struct cdb *cdbp, + void *buf, unsigned len, cdbi_t pos); +int cdb_find(struct cdb *cdbp, const void *key, unsigned klen); + +struct cdb_find { + struct cdb *cdb_cdbp; + cdbi_t cdb_hval; + const unsigned char *cdb_htp, *cdb_htab, *cdb_htend; + cdbi_t cdb_httodo; + const void *cdb_key; + cdbi_t cdb_klen; +}; + +int cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp, + const void *key, cdbi_t klen); +int cdb_findnext(struct cdb_find *cdbfp); + +/* old simple interface */ +/* open file using standard routine, then: */ +int cdb_seek(int fd, const void *key, unsigned klen, cdbi_t *dlenp); +int cdb_bread(int fd, void *buf, int len); + +/* cdb_make */ + +struct cdb_make { + int cdb_fd; /* file descriptor */ + /* private */ + cdbi_t cdb_dpos; /* data position so far */ + cdbi_t cdb_rcnt; /* record count so far */ + char cdb_buf[4096]; /* write buffer */ + char *cdb_bpos; /* current buf position */ + struct cdb_rl *cdb_rec[256]; /* list of arrays of record infos */ +}; + + + +int cdb_make_start(struct cdb_make *cdbmp, int fd); +int cdb_make_add(struct cdb_make *cdbmp, + const void *key, cdbi_t klen, + const void *val, cdbi_t vlen); +int cdb_make_exists(struct cdb_make *cdbmp, + const void *key, cdbi_t klen); +int cdb_make_put(struct cdb_make *cdbmp, + const void *key, cdbi_t klen, + const void *val, cdbi_t vlen, + int flag); +#define CDB_PUT_ADD 0 /* add unconditionnaly, like cdb_make_add() */ +#define CDB_PUT_REPLACE 1 /* replace: do not place to index OLD record */ +#define CDB_PUT_INSERT 2 /* add only if not already exists */ +#define CDB_PUT_WARN 3 /* add unconditionally but ret. 1 if exists */ +int cdb_make_finish(struct cdb_make *cdbmp); + +#endif /* include guard */ diff --git a/dirmngr/cdblib.c b/dirmngr/cdblib.c new file mode 100644 index 0000000..c04690f --- /dev/null +++ b/dirmngr/cdblib.c @@ -0,0 +1,928 @@ +/* cdblib.c - all CDB library functions. + * + * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru. + * Public domain. + * + * Taken from tinycdb-0.73 and merged into one file for easier + * inclusion into Dirmngr. By Werner Koch 2003-12-12. + */ + +/* A cdb database is a single file used to map 'keys' to 'values', + having records of (key,value) pairs. File consists of 3 parts: toc + (table of contents), data and index (hash tables). + + Toc has fixed length of 2048 bytes, containing 256 pointers to hash + tables inside index sections. Every pointer consists of position + of a hash table in bytes from the beginning of a file, and a size + of a hash table in entries, both are 4-bytes (32 bits) unsigned + integers in little-endian form. Hash table length may have zero + length, meaning that corresponding hash table is empty. + + Right after toc section, data section follows without any + alingment. It consists of series of records, each is a key length, + value (data) length, key and value. Again, key and value length + are 4-byte unsigned integers. Each next record follows previous + without any special alignment. + + After data section, index (hash tables) section follows. It should + be looked to in conjunction with toc section, where each of max 256 + hash tables are defined. Index section consists of series of hash + tables, with starting position and length defined in toc section. + Every hash table is a sequence of records each holds two numbers: + key's hash value and record position inside data section (bytes + from the beginning of a file to first byte of key length starting + data record). If record position is zero, then this is an empty + hash table slot, pointed to nowhere. + + CDB hash function is + hv = ((hv << 5) + hv) ^ c + for every single c byte of a key, starting with hv = 5381. + + Toc section indexed by (hv % 256), i.e. hash value modulo 256 + (number of entries in toc section). + + In order to find a record, one should: first, compute the hash + value (hv) of a key. Second, look to hash table number hv modulo + 256. If it is empty, then there is no such key exists. If it is + not empty, then third, loop by slots inside that hash table, + starting from slot with number hv divided by 256 modulo length of + that table, or ((hv / 256) % htlen), searching for this hv in hash + table. Stop search on empty slot (if record position is zero) or + when all slots was probed (note cyclic search, jumping from end to + beginning of a table). When hash value in question is found in + hash table, look to key of corresponding record, comparing it with + key in question. If them of the same length and equals to each + other, then record is found, overwise, repeat with next hash table + slot. Note that there may be several records with the same key. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef _WIN32 +# include +#else +# include +# ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +# endif +#endif +#include + +#include "dirmngr-err.h" +#include "cdb.h" + +#ifndef EPROTO +# define EPROTO EINVAL +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + + +struct cdb_rec { + cdbi_t hval; + cdbi_t rpos; +}; + +struct cdb_rl { + struct cdb_rl *next; + cdbi_t cnt; + struct cdb_rec rec[254]; +}; + +static int make_find(struct cdb_make *cdbmp, + const void *key, cdbi_t klen, cdbi_t hval, + struct cdb_rl **rlp); +static int make_write(struct cdb_make *cdbmp, + const char *ptr, cdbi_t len); + + + +/* Initializes structure given by CDBP pointer and associates it with + the open file descriptor FD. Allocate memory for the structure + itself if needed and file open operation should be done by + application. File FD should be opened at least read-only, and + should be seekable. Routine returns 0 on success or negative value + on error. */ +int +cdb_init(struct cdb *cdbp, int fd) +{ + struct stat st; + unsigned char *mem; +#ifdef _WIN32 + HANDLE hFile, hMapping; +#else + unsigned int fsize; +#endif + + /* get file size */ + if (fstat(fd, &st) < 0) + return -1; + /* trivial sanity check: at least toc should be here */ + if (st.st_size < 2048) { + gpg_err_set_errno (EPROTO); + return -1; + } + /* memory-map file */ +#ifdef _WIN32 +# ifdef __MINGW32CE__ + hFile = fd; +# else + hFile = (HANDLE) _get_osfhandle(fd); +# endif + if (hFile == (HANDLE) -1) + return -1; + hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (!hMapping) + return -1; + mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); + if (!mem) + return -1; + cdbp->cdb_mapping = hMapping; +#else /*!_WIN32*/ + fsize = (unsigned int)(st.st_size & 0xffffffffu); + mem = (unsigned char*)mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) + return -1; +#endif /*!_WIN32*/ + + cdbp->cdb_fd = fd; + cdbp->cdb_fsize = st.st_size; + cdbp->cdb_mem = mem; + +#if 0 + /* XXX don't know well about madvise syscall -- is it legal + to set different options for parts of one mmap() region? + There is also posix_madvise() exist, with POSIX_MADV_RANDOM etc... + */ +#ifdef MADV_RANDOM + /* set madvise() parameters. Ignore errors for now if system + doesn't support it */ + madvise(mem, 2048, MADV_WILLNEED); + madvise(mem + 2048, cdbp->cdb_fsize - 2048, MADV_RANDOM); +#endif +#endif + + cdbp->cdb_vpos = cdbp->cdb_vlen = 0; + + return 0; +} + + +/* Frees the internal resources held by structure. Note that this + routine does not close the file. */ +void +cdb_free(struct cdb *cdbp) +{ + if (cdbp->cdb_mem) { +#ifdef _WIN32 + UnmapViewOfFile ((void*) cdbp->cdb_mem); + CloseHandle (cdbp->cdb_mapping); + cdbp->cdb_mapping = NULL; +#else + munmap((void*)cdbp->cdb_mem, cdbp->cdb_fsize); +#endif /* _WIN32 */ + cdbp->cdb_mem = NULL; + } + cdbp->cdb_fsize = 0; +} + + +/* Read data from cdb file, starting at position pos of length len, + placing result to buf. This routine may be used to get actual + value found by cdb_find() or other routines that returns position + and length of a data. Returns 0 on success or negative value on + error. */ +int +cdb_read(const struct cdb *cdbp, void *buf, unsigned len, cdbi_t pos) +{ + if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) { + gpg_err_set_errno (EPROTO); + return -1; + } + memcpy(buf, cdbp->cdb_mem + pos, len); + return 0; +} + + +/* Attempts to find a key given by (key,klen) parameters. If key + exists in database, routine returns 1 and places position and + length of value associated with this key to internal fields inside + cdbp structure, to be accessible by cdb_datapos() and + cdb_datalen(). If key is not in database, routines returns 0. On + error, negative value is returned. Note that using cdb_find() it + is possible to lookup only first record with a given key. */ +int +cdb_find(struct cdb *cdbp, const void *key, cdbi_t klen) +{ + const unsigned char *htp; /* hash table pointer */ + const unsigned char *htab; /* hash table */ + const unsigned char *htend; /* end of hash table */ + cdbi_t httodo; /* ht bytes left to look */ + cdbi_t pos, n; + + cdbi_t hval; + + if (klen > cdbp->cdb_fsize) /* if key size is larger than file */ + return 0; + + hval = cdb_hash(key, klen); + + /* find (pos,n) hash table to use */ + /* first 2048 bytes (toc) are always available */ + /* (hval % 256) * 8 */ + htp = cdbp->cdb_mem + ((hval << 3) & 2047); /* index in toc (256x8) */ + n = cdb_unpack(htp + 4); /* table size */ + if (!n) /* empty table */ + return 0; /* not found */ + httodo = n << 3; /* bytes of htab to lookup */ + pos = cdb_unpack(htp); /* htab position */ + if (n > (cdbp->cdb_fsize >> 3) /* overflow of httodo ? */ + || pos > cdbp->cdb_fsize /* htab start within file ? */ + || httodo > cdbp->cdb_fsize - pos) /* entrie htab within file ? */ + { + gpg_err_set_errno (EPROTO); + return -1; + } + + htab = cdbp->cdb_mem + pos; /* htab pointer */ + htend = htab + httodo; /* after end of htab */ + /* htab starting position: rest of hval modulo htsize, 8bytes per elt */ + htp = htab + (((hval >> 8) % n) << 3); + + for(;;) { + pos = cdb_unpack(htp + 4); /* record position */ + if (!pos) + return 0; + if (cdb_unpack(htp) == hval) { + if (pos > cdbp->cdb_fsize - 8) { /* key+val lengths */ + gpg_err_set_errno (EPROTO); + return -1; + } + if (cdb_unpack(cdbp->cdb_mem + pos) == klen) { + if (cdbp->cdb_fsize - klen < pos + 8) { + gpg_err_set_errno (EPROTO); + return -1; + } + if (memcmp(key, cdbp->cdb_mem + pos + 8, klen) == 0) { + n = cdb_unpack(cdbp->cdb_mem + pos + 4); + pos += 8 + klen; + if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos) { + gpg_err_set_errno (EPROTO); + return -1; + } + cdbp->cdb_vpos = pos; + cdbp->cdb_vlen = n; + return 1; + } + } + } + httodo -= 8; + if (!httodo) + return 0; + if ((htp += 8) >= htend) + htp = htab; + } + +} + + + +/* Sequential-find routines that used separate structure. It is + possible to have many than one record with the same key in a + database, and these routines allow enumeration of all of them. + cdb_findinit() initializes search structure pointed to by cdbfp. + It will return negative value on error or 0 on success. + cdb_findnext() attempts to find next matching key, setting value + position and length in cdbfp structure. It will return positive + value if given key was found, 0 if there is no more such key(s), or + negative value on error. To access value position and length after + successeful call to cdb_findnext() (when it returned positive + result), use cdb_datapos() and cdb_datalen() macros with cdbp + pointer. It is error to use cdb_findnext() after it returned 0 or + error condition. These routines is a bit slower than cdb_find(). + + Setting KEY to NULL will start a sequential search through the + entire DB. +*/ +int +cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp, + const void *key, cdbi_t klen) +{ + cdbi_t n, pos; + + cdbfp->cdb_cdbp = cdbp; + cdbfp->cdb_key = key; + cdbfp->cdb_klen = klen; + cdbfp->cdb_hval = key? cdb_hash(key, klen) : 0; + + if (key) + { + cdbfp->cdb_htp = cdbp->cdb_mem + ((cdbfp->cdb_hval << 3) & 2047); + n = cdb_unpack(cdbfp->cdb_htp + 4); + cdbfp->cdb_httodo = n << 3; /* Set to size of hash table. */ + if (!n) + return 0; /* The hash table is empry. */ + pos = cdb_unpack(cdbfp->cdb_htp); + if (n > (cdbp->cdb_fsize >> 3) + || pos > cdbp->cdb_fsize + || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos) + { + gpg_err_set_errno (EPROTO); + return -1; + } + + cdbfp->cdb_htab = cdbp->cdb_mem + pos; + cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo; + cdbfp->cdb_htp = cdbfp->cdb_htab + (((cdbfp->cdb_hval >> 8) % n) << 3); + } + else /* Walk over all entries. */ + { + cdbfp->cdb_hval = 0; + /* Force stepping in findnext. */ + cdbfp->cdb_htp = cdbfp->cdb_htend = cdbp->cdb_mem; + } + return 0; +} + + +/* See cdb_findinit. */ +int +cdb_findnext(struct cdb_find *cdbfp) +{ + cdbi_t pos, n; + struct cdb *cdbp = cdbfp->cdb_cdbp; + + if (cdbfp->cdb_key) + { + while(cdbfp->cdb_httodo) { + pos = cdb_unpack(cdbfp->cdb_htp + 4); + if (!pos) + return 0; + n = cdb_unpack(cdbfp->cdb_htp) == cdbfp->cdb_hval; + if ((cdbfp->cdb_htp += 8) >= cdbfp->cdb_htend) + cdbfp->cdb_htp = cdbfp->cdb_htab; + cdbfp->cdb_httodo -= 8; + if (n) { + if (pos > cdbp->cdb_fsize - 8) { + gpg_err_set_errno (EPROTO); + return -1; + } + if (cdb_unpack(cdbp->cdb_mem + pos) == cdbfp->cdb_klen) { + if (cdbp->cdb_fsize - cdbfp->cdb_klen < pos + 8) { + gpg_err_set_errno (EPROTO); + return -1; + } + if (memcmp(cdbfp->cdb_key, + cdbp->cdb_mem + pos + 8, cdbfp->cdb_klen) == 0) { + n = cdb_unpack(cdbp->cdb_mem + pos + 4); + pos += 8 + cdbfp->cdb_klen; + if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos) { + gpg_err_set_errno (EPROTO); + return -1; + } + cdbp->cdb_vpos = pos; + cdbp->cdb_vlen = n; + return 1; + } + } + } + } + } + else /* Walk over all entries. */ + { + do + { + while (cdbfp->cdb_htp >= cdbfp->cdb_htend) + { + if (cdbfp->cdb_hval > 255) + return 0; /* No more items. */ + + cdbfp->cdb_htp = cdbp->cdb_mem + cdbfp->cdb_hval * 8; + cdbfp->cdb_hval++; /* Advance for next round. */ + pos = cdb_unpack (cdbfp->cdb_htp); /* Offset of table. */ + n = cdb_unpack (cdbfp->cdb_htp + 4); /* Number of entries. */ + cdbfp->cdb_httodo = n * 8; /* Size of table. */ + if (n > (cdbp->cdb_fsize / 8) + || pos > cdbp->cdb_fsize + || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos) + { + gpg_err_set_errno (EPROTO); + return -1; + } + + cdbfp->cdb_htab = cdbp->cdb_mem + pos; + cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo; + cdbfp->cdb_htp = cdbfp->cdb_htab; + } + + pos = cdb_unpack (cdbfp->cdb_htp + 4); /* Offset of record. */ + cdbfp->cdb_htp += 8; + } + while (!pos); + if (pos > cdbp->cdb_fsize - 8) + { + gpg_err_set_errno (EPROTO); + return -1; + } + + cdbp->cdb_kpos = pos + 8; + cdbp->cdb_klen = cdb_unpack(cdbp->cdb_mem + pos); + cdbp->cdb_vpos = pos + 8 + cdbp->cdb_klen; + cdbp->cdb_vlen = cdb_unpack(cdbp->cdb_mem + pos + 4); + n = 8 + cdbp->cdb_klen + cdbp->cdb_vlen; + if ( pos > cdbp->cdb_fsize || pos > cdbp->cdb_fsize - n) + { + gpg_err_set_errno (EPROTO); + return -1; + } + return 1; /* Found. */ + } + return 0; +} + +/* Read a chunk from file, ignoring interrupts (EINTR) */ +int +cdb_bread(int fd, void *buf, int len) +{ + int l; + while(len > 0) { + do l = read(fd, buf, len); + while(l < 0 && errno == EINTR); + if (l <= 0) { + if (!l) + gpg_err_set_errno (EIO); + return -1; + } + buf = (char*)buf + l; + len -= l; + } + return 0; +} + +/* Find a given key in cdb file, seek a file pointer to it's value and + place data length to *dlenp. */ +int +cdb_seek(int fd, const void *key, unsigned klen, cdbi_t *dlenp) +{ + cdbi_t htstart; /* hash table start position */ + cdbi_t htsize; /* number of elements in a hash table */ + cdbi_t httodo; /* hash table elements left to look */ + cdbi_t hti; /* hash table index */ + cdbi_t pos; /* position in a file */ + cdbi_t hval; /* key's hash value */ + unsigned char rbuf[64]; /* read buffer */ + int needseek = 1; /* if we should seek to a hash slot */ + + hval = cdb_hash(key, klen); + pos = (hval & 0xff) << 3; /* position in TOC */ + /* read the hash table parameters */ + if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0) + return -1; + if ((htsize = cdb_unpack(rbuf + 4)) == 0) + return 0; + hti = (hval >> 8) % htsize; /* start position in hash table */ + httodo = htsize; + htstart = cdb_unpack(rbuf); + + for(;;) { + if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0) + return -1; + if (cdb_bread(fd, rbuf, 8) < 0) + return -1; + if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */ + return 0; + + if (cdb_unpack(rbuf) != hval) /* hash value not matched */ + needseek = 0; + else { /* hash value matched */ + if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0) + return -1; + if (cdb_unpack(rbuf) == klen) { /* key length matches */ + /* read the key from file and compare with wanted */ + cdbi_t l = klen, c; + const char *k = (const char*)key; + if (*dlenp) + *dlenp = cdb_unpack(rbuf + 4); /* save value length */ + for(;;) { + if (!l) /* the whole key read and matches, return */ + return 1; + c = l > sizeof(rbuf) ? sizeof(rbuf) : l; + if (cdb_bread(fd, rbuf, c) < 0) + return -1; + if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */ + break; + k += c; l -= c; + } + } + needseek = 1; /* we're looked to other place, should seek back */ + } + if (!--httodo) + return 0; + if (++hti == htsize) { + hti = htstart; + needseek = 1; + } + } +} + +cdbi_t +cdb_unpack(const unsigned char buf[4]) +{ + cdbi_t n = buf[3]; + n <<= 8; n |= buf[2]; + n <<= 8; n |= buf[1]; + n <<= 8; n |= buf[0]; + return n; +} + +/* Add record with key (KEY,KLEN) and value (VAL,VLEN) to a database. + Returns 0 on success or negative value on error. Note that this + routine does not checks if given key already exists, but cdb_find() + will not see second record with the same key. It is not possible + to continue building a database if cdb_make_add() returned an error + indicator. */ +int +cdb_make_add(struct cdb_make *cdbmp, + const void *key, cdbi_t klen, + const void *val, cdbi_t vlen) +{ + unsigned char rlen[8]; + cdbi_t hval; + struct cdb_rl *rl; + if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) || + vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) { + gpg_err_set_errno (ENOMEM); + return -1; + } + hval = cdb_hash(key, klen); + rl = cdbmp->cdb_rec[hval&255]; + if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) { + rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl)); + if (!rl) { + gpg_err_set_errno (ENOMEM); + return -1; + } + rl->cnt = 0; + rl->next = cdbmp->cdb_rec[hval&255]; + cdbmp->cdb_rec[hval&255] = rl; + } + rl->rec[rl->cnt].hval = hval; + rl->rec[rl->cnt].rpos = cdbmp->cdb_dpos; + ++rl->cnt; + ++cdbmp->cdb_rcnt; + cdb_pack(klen, rlen); + cdb_pack(vlen, rlen + 4); + if (make_write(cdbmp, rlen, 8) < 0 || + make_write(cdbmp, key, klen) < 0 || + make_write(cdbmp, val, vlen) < 0) + return -1; + return 0; +} + +int +cdb_make_put(struct cdb_make *cdbmp, + const void *key, cdbi_t klen, + const void *val, cdbi_t vlen, + int flags) +{ + unsigned char rlen[8]; + cdbi_t hval = cdb_hash(key, klen); + struct cdb_rl *rl; + int c, r; + + switch(flags) { + case CDB_PUT_REPLACE: + case CDB_PUT_INSERT: + case CDB_PUT_WARN: + c = make_find(cdbmp, key, klen, hval, &rl); + if (c < 0) + return -1; + if (c) { + if (flags == CDB_PUT_INSERT) { + gpg_err_set_errno (EEXIST); + return 1; + } + else if (flags == CDB_PUT_REPLACE) { + --c; + r = 1; + break; + } + else + r = 1; + } + /* fall */ + + case CDB_PUT_ADD: + rl = cdbmp->cdb_rec[hval&255]; + if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) { + rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl)); + if (!rl) { + gpg_err_set_errno (ENOMEM); + return -1; + } + rl->cnt = 0; + rl->next = cdbmp->cdb_rec[hval&255]; + cdbmp->cdb_rec[hval&255] = rl; + } + c = rl->cnt; + r = 0; + break; + + default: + gpg_err_set_errno (EINVAL); + return -1; + } + + if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) || + vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) { + gpg_err_set_errno (ENOMEM); + return -1; + } + rl->rec[c].hval = hval; + rl->rec[c].rpos = cdbmp->cdb_dpos; + if (c == rl->cnt) { + ++rl->cnt; + ++cdbmp->cdb_rcnt; + } + cdb_pack(klen, rlen); + cdb_pack(vlen, rlen + 4); + if (make_write(cdbmp, rlen, 8) < 0 || + make_write(cdbmp, key, klen) < 0 || + make_write(cdbmp, val, vlen) < 0) + return -1; + return r; +} + + +static int +match(int fd, cdbi_t pos, const char *key, cdbi_t klen) +{ + unsigned char buf[64]; /*XXX cdb_buf may be used here instead */ + if (lseek(fd, pos, SEEK_SET) < 0 || read(fd, buf, 8) != 8) + return -1; + if (cdb_unpack(buf) != klen) + return 0; + + while(klen > sizeof(buf)) { + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + return -1; + if (memcmp(buf, key, sizeof(buf)) != 0) + return 0; + key += sizeof(buf); + klen -= sizeof(buf); + } + if (klen) { + if (read(fd, buf, klen) != klen) + return -1; + if (memcmp(buf, key, klen) != 0) + return 0; + } + return 1; +} + + +static int +make_find (struct cdb_make *cdbmp, + const void *key, cdbi_t klen, cdbi_t hval, + struct cdb_rl **rlp) +{ + struct cdb_rl *rl = cdbmp->cdb_rec[hval&255]; + int r, i; + int sought = 0; + while(rl) { + for(i = rl->cnt - 1; i >= 0; --i) { /* search backward */ + if (rl->rec[i].hval != hval) + continue; + /*XXX this explicit flush may be unnecessary having + * smarter match() that looks to cdb_buf too, but + * most of a time here spent in finding hash values + * (above), not keys */ + if (cdbmp->cdb_bpos != cdbmp->cdb_buf) { + if (write(cdbmp->cdb_fd, cdbmp->cdb_buf, + cdbmp->cdb_bpos - cdbmp->cdb_buf) < 0) + return -1; + cdbmp->cdb_bpos = cdbmp->cdb_buf; + } + sought = 1; + r = match(cdbmp->cdb_fd, rl->rec[i].rpos, key, klen); + if (!r) + continue; + if (r < 0) + return -1; + if (lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0) + return -1; + if (rlp) + *rlp = rl; + return i + 1; + } + rl = rl->next; + } + if (sought && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0) + return -1; + return 0; +} + +int +cdb_make_exists(struct cdb_make *cdbmp, + const void *key, cdbi_t klen) +{ + return make_find(cdbmp, key, klen, cdb_hash(key, klen), NULL); +} + + +void +cdb_pack(cdbi_t num, unsigned char buf[4]) +{ + buf[0] = num & 255; num >>= 8; + buf[1] = num & 255; num >>= 8; + buf[2] = num & 255; + buf[3] = num >> 8; +} + + +/* Initializes structure to create a database. File FD should be + opened read-write and should be seekable. Returns 0 on success or + negative value on error. */ +int +cdb_make_start(struct cdb_make *cdbmp, int fd) +{ + memset (cdbmp, 0, sizeof *cdbmp); + cdbmp->cdb_fd = fd; + cdbmp->cdb_dpos = 2048; + cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048; + return 0; +} + + +static int +ewrite(int fd, const char *buf, int len) +{ + while(len) { + int l = write(fd, buf, len); + if (l < 0 && errno != EINTR) + return -1; + if (l > 0) + { + len -= l; + buf += l; + } + } + return 0; +} + +static int +make_write(struct cdb_make *cdbmp, const char *ptr, cdbi_t len) +{ + cdbi_t l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf); + cdbmp->cdb_dpos += len; + if (len > l) { + memcpy(cdbmp->cdb_bpos, ptr, l); + if (ewrite(cdbmp->cdb_fd, cdbmp->cdb_buf, sizeof(cdbmp->cdb_buf)) < 0) + return -1; + ptr += l; len -= l; + l = len / sizeof(cdbmp->cdb_buf); + if (l) { + l *= sizeof(cdbmp->cdb_buf); + if (ewrite(cdbmp->cdb_fd, ptr, l) < 0) + return -1; + ptr += l; len -= l; + } + cdbmp->cdb_bpos = cdbmp->cdb_buf; + } + if (len) { + memcpy(cdbmp->cdb_bpos, ptr, len); + cdbmp->cdb_bpos += len; + } + return 0; +} + +static int +cdb_make_finish_internal(struct cdb_make *cdbmp) +{ + cdbi_t hcnt[256]; /* hash table counts */ + cdbi_t hpos[256]; /* hash table positions */ + struct cdb_rec *htab; + unsigned char *p; + struct cdb_rl *rl; + cdbi_t hsize; + unsigned t, i; + + if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt) { + gpg_err_set_errno (ENOMEM); + return -1; + } + + /* count htab sizes and reorder reclists */ + hsize = 0; + for (t = 0; t < 256; ++t) { + struct cdb_rl *rlt = NULL; + i = 0; + rl = cdbmp->cdb_rec[t]; + while(rl) { + struct cdb_rl *rln = rl->next; + rl->next = rlt; + rlt = rl; + i += rl->cnt; + rl = rln; + } + cdbmp->cdb_rec[t] = rlt; + if (hsize < (hcnt[t] = i << 1)) + hsize = hcnt[t]; + } + + /* allocate memory to hold max htable */ + htab = (struct cdb_rec*)malloc((hsize + 2) * sizeof(struct cdb_rec)); + if (!htab) { + gpg_err_set_errno (ENOENT); + return -1; + } + p = (unsigned char *)htab; + htab += 2; + + /* build hash tables */ + for (t = 0; t < 256; ++t) { + cdbi_t len, hi; + hpos[t] = cdbmp->cdb_dpos; + if ((len = hcnt[t]) == 0) + continue; + for (i = 0; i < len; ++i) + htab[i].hval = htab[i].rpos = 0; + for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next) + for (i = 0; i < rl->cnt; ++i) { + hi = (rl->rec[i].hval >> 8) % len; + while(htab[hi].rpos) + if (++hi == len) + hi = 0; + htab[hi] = rl->rec[i]; + } + for (i = 0; i < len; ++i) { + cdb_pack(htab[i].hval, p + (i << 3)); + cdb_pack(htab[i].rpos, p + (i << 3) + 4); + } + if (make_write(cdbmp, p, len << 3) < 0) { + free(p); + return -1; + } + } + free(p); + if (cdbmp->cdb_bpos != cdbmp->cdb_buf && + ewrite(cdbmp->cdb_fd, cdbmp->cdb_buf, + cdbmp->cdb_bpos - cdbmp->cdb_buf) != 0) + return -1; + p = cdbmp->cdb_buf; + for (t = 0; t < 256; ++t) { + cdb_pack(hpos[t], p + (t << 3)); + cdb_pack(hcnt[t], p + (t << 3) + 4); + } + if (lseek(cdbmp->cdb_fd, 0, 0) != 0 || + ewrite(cdbmp->cdb_fd, p, 2048) != 0) + return -1; + + return 0; +} + +static void +cdb_make_free(struct cdb_make *cdbmp) +{ + unsigned t; + for(t = 0; t < 256; ++t) { + struct cdb_rl *rl = cdbmp->cdb_rec[t]; + while(rl) { + struct cdb_rl *tm = rl; + rl = rl->next; + free(tm); + } + } +} + + + +/* Finalizes database file, constructing all needed indexes, and frees + memory structures. It does not close the file descriptor. Returns + 0 on success or a negative value on error. */ +int +cdb_make_finish(struct cdb_make *cdbmp) +{ + int r = cdb_make_finish_internal(cdbmp); + cdb_make_free(cdbmp); + return r; +} + + +cdbi_t +cdb_hash(const void *buf, cdbi_t len) +{ + register const unsigned char *p = (const unsigned char *)buf; + register const unsigned char *end = p + len; + register cdbi_t hash = 5381; /* start value */ + while (p < end) + hash = (hash + (hash << 5)) ^ *p++; + return hash; +} diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c new file mode 100644 index 0000000..ad85d99 --- /dev/null +++ b/dirmngr/certcache.c @@ -0,0 +1,1391 @@ +/* certcache.c - Certificate caching + * Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "crlfetch.h" +#include "certcache.h" + + +#define MAX_EXTRA_CACHED_CERTS 1000 + +/* Constants used to classify search patterns. */ +enum pattern_class + { + PATTERN_UNKNOWN = 0, + PATTERN_EMAIL, + PATTERN_EMAIL_SUBSTR, + PATTERN_FINGERPRINT16, + PATTERN_FINGERPRINT20, + PATTERN_SHORT_KEYID, + PATTERN_LONG_KEYID, + PATTERN_SUBJECT, + PATTERN_SERIALNO, + PATTERN_SERIALNO_ISSUER, + PATTERN_ISSUER, + PATTERN_SUBSTR + }; + + +/* A certificate cache item. This consists of a the KSBA cert object + and some meta data for easier lookup. We use a hash table to keep + track of all items and use the (randomly distributed) first byte of + the fingerprint directly as the hash which makes it pretty easy. */ +struct cert_item_s +{ + struct cert_item_s *next; /* Next item with the same hash value. */ + ksba_cert_t cert; /* The KSBA cert object or NULL is this is + not a valid item. */ + unsigned char fpr[20]; /* The fingerprint of this object. */ + char *issuer_dn; /* The malloced issuer DN. */ + ksba_sexp_t sn; /* The malloced serial number */ + char *subject_dn; /* The malloced subject DN - maybe NULL. */ + struct + { + unsigned int loaded:1; /* It has been explicitly loaded. */ + unsigned int trusted:1; /* This is a trusted root certificate. */ + } flags; +}; +typedef struct cert_item_s *cert_item_t; + +/* The actual cert cache consisting of 256 slots for items indexed by + the first byte of the fingerprint. */ +static cert_item_t cert_cache[256]; + +/* This is the global cache_lock variable. In general locking is not + needed but it would take extra efforts to make sure that no + indirect use of npth functions is done, so we simply lock it + always. Note: We can't use static initialization, as that is not + available through w32-pth. */ +static npth_rwlock_t cert_cache_lock; + +/* Flag to track whether the cache has been initialized. */ +static int initialization_done; + +/* Total number of certificates loaded during initialization and + cached during operation. */ +static unsigned int total_loaded_certificates; +static unsigned int total_extra_certificates; + + + +/* Helper to do the cache locking. */ +static void +init_cache_lock (void) +{ + int err; + + err = npth_rwlock_init (&cert_cache_lock, NULL); + if (err) + log_fatal (_("can't initialize certificate cache lock: %s\n"), + strerror (err)); +} + +static void +acquire_cache_read_lock (void) +{ + int err; + + err = npth_rwlock_rdlock (&cert_cache_lock); + if (err) + log_fatal (_("can't acquire read lock on the certificate cache: %s\n"), + strerror (err)); +} + +static void +acquire_cache_write_lock (void) +{ + int err; + + err = npth_rwlock_wrlock (&cert_cache_lock); + if (err) + log_fatal (_("can't acquire write lock on the certificate cache: %s\n"), + strerror (err)); +} + +static void +release_cache_lock (void) +{ + int err; + + err = npth_rwlock_unlock (&cert_cache_lock); + if (err) + log_fatal (_("can't release lock on the certificate cache: %s\n"), + strerror (err)); +} + + +/* Return false if both serial numbers match. Can't be used for + sorting. */ +static int +compare_serialno (ksba_sexp_t serial1, ksba_sexp_t serial2 ) +{ + unsigned char *a = serial1; + unsigned char *b = serial2; + return cmp_simple_canon_sexp (a, b); +} + + + +/* Return a malloced canonical S-Expression with the serial number + converted from the hex string HEXSN. Return NULL on memory + error. */ +ksba_sexp_t +hexsn_to_sexp (const char *hexsn) +{ + char *buffer, *p; + size_t len; + char numbuf[40]; + + len = unhexify (NULL, hexsn); + snprintf (numbuf, sizeof numbuf, "(%u:", (unsigned int)len); + buffer = xtrymalloc (strlen (numbuf) + len + 2 ); + if (!buffer) + return NULL; + p = stpcpy (buffer, numbuf); + len = unhexify (p, hexsn); + p[len] = ')'; + p[len+1] = 0; + + return buffer; +} + + +/* Compute the fingerprint of the certificate CERT and put it into + the 20 bytes large buffer DIGEST. Return address of this buffer. */ +unsigned char * +cert_compute_fpr (ksba_cert_t cert, unsigned char *digest) +{ + gpg_error_t err; + gcry_md_hd_t md; + + err = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (err) + log_fatal ("gcry_md_open failed: %s\n", gpg_strerror (err)); + + err = ksba_cert_hash (cert, 0, HASH_FNC, md); + if (err) + { + log_error ("oops: ksba_cert_hash failed: %s\n", gpg_strerror (err)); + memset (digest, 0xff, 20); /* Use a dummy value. */ + } + else + { + gcry_md_final (md); + memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20); + } + gcry_md_close (md); + return digest; +} + + +/* Cleanup one slot. This releases all resourses but keeps the actual + slot in the cache marked for reuse. */ +static void +clean_cache_slot (cert_item_t ci) +{ + ksba_cert_t cert; + + if (!ci->cert) + return; /* Already cleaned. */ + + ksba_free (ci->sn); + ci->sn = NULL; + ksba_free (ci->issuer_dn); + ci->issuer_dn = NULL; + ksba_free (ci->subject_dn); + ci->subject_dn = NULL; + cert = ci->cert; + ci->cert = NULL; + + ksba_cert_release (cert); +} + + +/* Put the certificate CERT into the cache. It is assumed that the + cache is locked while this function is called. If FPR_BUFFER is not + NULL the fingerprint of the certificate will be stored there. + FPR_BUFFER neds to point to a buffer of at least 20 bytes. The + fingerprint will be stored on success or when the function returns + gpg_err_code(GPG_ERR_DUP_VALUE). */ +static gpg_error_t +put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) +{ + unsigned char help_fpr_buffer[20], *fpr; + cert_item_t ci; + + fpr = fpr_buffer? fpr_buffer : &help_fpr_buffer; + + /* If we already reached the caching limit, drop a couple of certs + from the cache. Our dropping strategy is simple: We keep a + static index counter and use this to start looking for + certificates, then we drop 5 percent of the oldest certificates + starting at that index. For a large cache this is a fair way of + removing items. An LRU strategy would be better of course. + Because we append new entries to the head of the list and we want + to remove old ones first, we need to do this from the tail. The + implementation is not very efficient but compared to the long + time it takes to retrieve a certifciate from an external resource + it seems to be reasonable. */ + if (!is_loaded && total_extra_certificates >= MAX_EXTRA_CACHED_CERTS) + { + static int idx; + cert_item_t ci_mark; + int i; + unsigned int drop_count; + + drop_count = MAX_EXTRA_CACHED_CERTS / 20; + if (drop_count < 2) + drop_count = 2; + + log_info (_("dropping %u certificates from the cache\n"), drop_count); + assert (idx < 256); + for (i=idx; drop_count; i = ((i+1)%256)) + { + ci_mark = NULL; + for (ci = cert_cache[i]; ci; ci = ci->next) + if (ci->cert && !ci->flags.loaded) + ci_mark = ci; + if (ci_mark) + { + clean_cache_slot (ci_mark); + drop_count--; + total_extra_certificates--; + } + } + if (i==idx) + idx++; + else + idx = i; + idx %= 256; + } + + cert_compute_fpr (cert, fpr); + for (ci=cert_cache[*fpr]; ci; ci = ci->next) + if (ci->cert && !memcmp (ci->fpr, fpr, 20)) + return gpg_error (GPG_ERR_DUP_VALUE); + /* Try to reuse an existing entry. */ + for (ci=cert_cache[*fpr]; ci; ci = ci->next) + if (!ci->cert) + break; + if (!ci) + { /* No: Create a new entry. */ + ci = xtrycalloc (1, sizeof *ci); + if (!ci) + return gpg_error_from_errno (errno); + ci->next = cert_cache[*fpr]; + cert_cache[*fpr] = ci; + } + else + memset (&ci->flags, 0, sizeof ci->flags); + + ksba_cert_ref (cert); + ci->cert = cert; + memcpy (ci->fpr, fpr, 20); + ci->sn = ksba_cert_get_serial (cert); + ci->issuer_dn = ksba_cert_get_issuer (cert, 0); + if (!ci->issuer_dn || !ci->sn) + { + clean_cache_slot (ci); + return gpg_error (GPG_ERR_INV_CERT_OBJ); + } + ci->subject_dn = ksba_cert_get_subject (cert, 0); + ci->flags.loaded = !!is_loaded; + ci->flags.trusted = !!is_trusted; + + if (is_loaded) + total_loaded_certificates++; + else + total_extra_certificates++; + + return 0; +} + + +/* Load certificates from the directory DIRNAME. All certificates + matching the pattern "*.crt" or "*.der" are loaded. We assume that + certificates are DER encoded and not PEM encapsulated. The cache + should be in a locked state when calling this function. */ +static gpg_error_t +load_certs_from_dir (const char *dirname, int are_trusted) +{ + gpg_error_t err; + DIR *dir; + struct dirent *ep; + char *p; + size_t n; + estream_t fp; + ksba_reader_t reader; + ksba_cert_t cert; + char *fname = NULL; + + dir = opendir (dirname); + if (!dir) + { + return 0; /* We do not consider this a severe error. */ + } + + while ( (ep=readdir (dir)) ) + { + p = ep->d_name; + if (*p == '.' || !*p) + continue; /* Skip any hidden files and invalid entries. */ + n = strlen (p); + if ( n < 5 || (strcmp (p+n-4,".crt") && strcmp (p+n-4,".der"))) + continue; /* Not the desired "*.crt" or "*.der" pattern. */ + + xfree (fname); + fname = make_filename (dirname, p, NULL); + fp = es_fopen (fname, "rb"); + if (!fp) + { + log_error (_("can't open '%s': %s\n"), + fname, strerror (errno)); + continue; + } + + err = create_estream_ksba_reader (&reader, fp); + if (err) + { + es_fclose (fp); + continue; + } + + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_read_der (cert, reader); + ksba_reader_release (reader); + es_fclose (fp); + if (err) + { + log_error (_("can't parse certificate '%s': %s\n"), + fname, gpg_strerror (err)); + ksba_cert_release (cert); + continue; + } + + err = put_cert (cert, 1, are_trusted, NULL); + if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) + log_info (_("certificate '%s' already cached\n"), fname); + else if (!err) + { + if (are_trusted) + log_info (_("trusted certificate '%s' loaded\n"), fname); + else + log_info (_("certificate '%s' loaded\n"), fname); + if (opt.verbose) + { + p = get_fingerprint_hexstring_colon (cert); + log_info (_(" SHA1 fingerprint = %s\n"), p); + xfree (p); + + cert_log_name (_(" issuer ="), cert); + cert_log_subject (_(" subject ="), cert); + } + } + else + log_error (_("error loading certificate '%s': %s\n"), + fname, gpg_strerror (err)); + ksba_cert_release (cert); + } + + xfree (fname); + closedir (dir); + return 0; +} + + +/* Initialize the certificate cache if not yet done. */ +void +cert_cache_init (void) +{ + char *dname; + + if (initialization_done) + return; + init_cache_lock (); + acquire_cache_write_lock (); + + dname = make_filename (gnupg_sysconfdir (), "trusted-certs", NULL); + load_certs_from_dir (dname, 1); + xfree (dname); + + dname = make_filename (gnupg_sysconfdir (), "extra-certs", NULL); + load_certs_from_dir (dname, 0); + xfree (dname); + + initialization_done = 1; + release_cache_lock (); + + cert_cache_print_stats (); +} + +/* Deinitialize the certificate cache. With FULL set to true even the + unused certificate slots are released. */ +void +cert_cache_deinit (int full) +{ + cert_item_t ci, ci2; + int i; + + if (!initialization_done) + return; + + acquire_cache_write_lock (); + + for (i=0; i < 256; i++) + for (ci=cert_cache[i]; ci; ci = ci->next) + clean_cache_slot (ci); + + if (full) + { + for (i=0; i < 256; i++) + { + for (ci=cert_cache[i]; ci; ci = ci2) + { + ci2 = ci->next; + xfree (ci); + } + cert_cache[i] = NULL; + } + } + + total_loaded_certificates = 0; + total_extra_certificates = 0; + initialization_done = 0; + release_cache_lock (); +} + +/* Print some statistics to the log file. */ +void +cert_cache_print_stats (void) +{ + log_info (_("permanently loaded certificates: %u\n"), + total_loaded_certificates); + log_info (_(" runtime cached certificates: %u\n"), + total_extra_certificates); +} + + +/* Put CERT into the certificate cache. */ +gpg_error_t +cache_cert (ksba_cert_t cert) +{ + gpg_error_t err; + + acquire_cache_write_lock (); + err = put_cert (cert, 0, 0, NULL); + release_cache_lock (); + if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) + log_info (_("certificate already cached\n")); + else if (!err) + log_info (_("certificate cached\n")); + else + log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); + return err; +} + + +/* Put CERT into the certificate cache and store the fingerprint of + the certificate into FPR_BUFFER. If the certificate is already in + the cache do not print a warning; just store the + fingerprint. FPR_BUFFER needs to be at least 20 bytes. */ +gpg_error_t +cache_cert_silent (ksba_cert_t cert, void *fpr_buffer) +{ + gpg_error_t err; + + acquire_cache_write_lock (); + err = put_cert (cert, 0, 0, fpr_buffer); + release_cache_lock (); + if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) + err = 0; + if (err) + log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); + return err; +} + + + +/* Return a certificate object for the given fingerprint. FPR is + expected to be a 20 byte binary SHA-1 fingerprint. If no matching + certificate is available in the cache NULL is returned. The caller + must release a returned certificate. Note that although we are + using reference counting the caller should not just compare the + pointers to check for identical certificates. */ +ksba_cert_t +get_cert_byfpr (const unsigned char *fpr) +{ + cert_item_t ci; + + acquire_cache_read_lock (); + for (ci=cert_cache[*fpr]; ci; ci = ci->next) + if (ci->cert && !memcmp (ci->fpr, fpr, 20)) + { + ksba_cert_ref (ci->cert); + release_cache_lock (); + return ci->cert; + } + + release_cache_lock (); + return NULL; +} + +/* Return a certificate object for the given fingerprint. STRING is + expected to be a SHA-1 fingerprint in standard hex notation with or + without colons. If no matching certificate is available in the + cache NULL is returned. The caller must release a returned + certificate. Note that although we are using reference counting + the caller should not just compare the pointers to check for + identical certificates. */ +ksba_cert_t +get_cert_byhexfpr (const char *string) +{ + unsigned char fpr[20]; + const char *s; + int i; + + if (strchr (string, ':')) + { + for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1);) + { + if (s[2] && s[2] != ':') + break; /* Invalid string. */ + fpr[i++] = xtoi_2 (s); + s += 2; + if (i!= 20 && *s == ':') + s++; + } + } + else + { + for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1); s+=2 ) + fpr[i++] = xtoi_2 (s); + } + if (i!=20 || *s) + { + log_error (_("invalid SHA1 fingerprint string '%s'\n"), string); + return NULL; + } + + return get_cert_byfpr (fpr); +} + + + +/* Return the certificate matching ISSUER_DN and SERIALNO. */ +ksba_cert_t +get_cert_bysn (const char *issuer_dn, ksba_sexp_t serialno) +{ + /* Simple and inefficient implementation. fixme! */ + cert_item_t ci; + int i; + + acquire_cache_read_lock (); + for (i=0; i < 256; i++) + { + for (ci=cert_cache[i]; ci; ci = ci->next) + if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn) + && !compare_serialno (ci->sn, serialno)) + { + ksba_cert_ref (ci->cert); + release_cache_lock (); + return ci->cert; + } + } + + release_cache_lock (); + return NULL; +} + + +/* Return the certificate matching ISSUER_DN. SEQ should initially be + set to 0 and bumped up to get the next issuer with that DN. */ +ksba_cert_t +get_cert_byissuer (const char *issuer_dn, unsigned int seq) +{ + /* Simple and very inefficient implementation and API. fixme! */ + cert_item_t ci; + int i; + + acquire_cache_read_lock (); + for (i=0; i < 256; i++) + { + for (ci=cert_cache[i]; ci; ci = ci->next) + if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn)) + if (!seq--) + { + ksba_cert_ref (ci->cert); + release_cache_lock (); + return ci->cert; + } + } + + release_cache_lock (); + return NULL; +} + + +/* Return the certificate matching SUBJECT_DN. SEQ should initially be + set to 0 and bumped up to get the next subject with that DN. */ +ksba_cert_t +get_cert_bysubject (const char *subject_dn, unsigned int seq) +{ + /* Simple and very inefficient implementation and API. fixme! */ + cert_item_t ci; + int i; + + if (!subject_dn) + return NULL; + + acquire_cache_read_lock (); + for (i=0; i < 256; i++) + { + for (ci=cert_cache[i]; ci; ci = ci->next) + if (ci->cert && ci->subject_dn + && !strcmp (ci->subject_dn, subject_dn)) + if (!seq--) + { + ksba_cert_ref (ci->cert); + release_cache_lock (); + return ci->cert; + } + } + + release_cache_lock (); + return NULL; +} + + + +/* Return a value describing the the class of PATTERN. The offset of + the actual string to be used for the comparison is stored at + R_OFFSET. The offset of the serialnumer is stored at R_SN_OFFSET. */ +static enum pattern_class +classify_pattern (const char *pattern, size_t *r_offset, size_t *r_sn_offset) +{ + enum pattern_class result; + const char *s; + int hexprefix = 0; + int hexlength; + + *r_offset = *r_sn_offset = 0; + + /* Skip leading spaces. */ + for(s = pattern; *s && spacep (s); s++ ) + ; + + switch (*s) + { + case 0: /* Empty string is an error. */ + result = PATTERN_UNKNOWN; + break; + + case '.': /* An email address, compare from end. */ + result = PATTERN_UNKNOWN; /* Not implemented. */ + break; + + case '<': /* An email address. */ + result = PATTERN_EMAIL; + s++; + break; + + case '@': /* Part of an email address. */ + result = PATTERN_EMAIL_SUBSTR; + s++; + break; + + case '=': /* Exact compare. */ + result = PATTERN_UNKNOWN; /* Does not make sense for X.509. */ + break; + + case '*': /* Case insensitive substring search. */ + result = PATTERN_SUBSTR; + s++; + break; + + case '+': /* Compare individual words. */ + result = PATTERN_UNKNOWN; /* Not implemented. */ + break; + + case '/': /* Subject's DN. */ + s++; + if (!*s || spacep (s)) + result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */ + else + result = PATTERN_SUBJECT; + break; + + case '#': /* Serial number or issuer DN. */ + { + const char *si; + + s++; + if ( *s == '/') + { + /* An issuer's DN is indicated by "#/" */ + s++; + if (!*s || spacep (s)) + result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */ + else + result = PATTERN_ISSUER; + } + else + { /* Serialnumber + optional issuer ID. */ + for (si=s; *si && *si != '/'; si++) + if (!strchr("01234567890abcdefABCDEF", *si)) + break; + if (*si && *si != '/') + result = PATTERN_UNKNOWN; /* Invalid digit in serial number. */ + else + { + *r_sn_offset = s - pattern; + if (!*si) + result = PATTERN_SERIALNO; + else + { + s = si+1; + if (!*s || spacep (s)) + result = PATTERN_UNKNOWN; /* No DN or prefixed + with a space. */ + else + result = PATTERN_SERIALNO_ISSUER; + } + } + } + } + break; + + case ':': /* Unified fingerprint. */ + { + const char *se, *si; + int i; + + se = strchr (++s, ':'); + if (!se) + result = PATTERN_UNKNOWN; + else + { + for (i=0, si=s; si < se; si++, i++ ) + if (!strchr("01234567890abcdefABCDEF", *si)) + break; + if ( si < se ) + result = PATTERN_UNKNOWN; /* Invalid digit. */ + else if (i == 32) + result = PATTERN_FINGERPRINT16; + else if (i == 40) + result = PATTERN_FINGERPRINT20; + else + result = PATTERN_UNKNOWN; /* Invalid length for a fingerprint. */ + } + } + break; + + case '&': /* Keygrip. */ + result = PATTERN_UNKNOWN; /* Not implemented. */ + break; + + default: + if (s[0] == '0' && s[1] == 'x') + { + hexprefix = 1; + s += 2; + } + + hexlength = strspn(s, "0123456789abcdefABCDEF"); + + /* Check if a hexadecimal number is terminated by EOS or blank. */ + if (hexlength && s[hexlength] && !spacep (s+hexlength)) + { + /* If the "0x" prefix is used a correct termination is required. */ + if (hexprefix) + { + result = PATTERN_UNKNOWN; + break; /* switch */ + } + hexlength = 0; /* Not a hex number. */ + } + + if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')) + { + if (hexlength == 9) + s++; + result = PATTERN_SHORT_KEYID; + } + else if (hexlength == 16 || (!hexprefix && hexlength == 17 && *s == '0')) + { + if (hexlength == 17) + s++; + result = PATTERN_LONG_KEYID; + } + else if (hexlength == 32 || (!hexprefix && hexlength == 33 && *s == '0')) + { + if (hexlength == 33) + s++; + result = PATTERN_FINGERPRINT16; + } + else if (hexlength == 40 || (!hexprefix && hexlength == 41 && *s == '0')) + { + if (hexlength == 41) + s++; + result = PATTERN_FINGERPRINT20; + } + else if (!hexprefix) + { + /* The fingerprints used with X.509 are often delimited by + colons, so we try to single this case out. */ + result = PATTERN_UNKNOWN; + hexlength = strspn (s, ":0123456789abcdefABCDEF"); + if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) + { + int i, c; + + for (i=0; i < 20; i++, s += 3) + { + c = hextobyte(s); + if (c == -1 || (i < 19 && s[2] != ':')) + break; + } + if (i == 20) + result = PATTERN_FINGERPRINT20; + } + if (result == PATTERN_UNKNOWN) /* Default to substring match. */ + { + result = PATTERN_SUBSTR; + } + } + else /* A hex number with a prefix but with a wrong length. */ + result = PATTERN_UNKNOWN; + } + + if (result != PATTERN_UNKNOWN) + *r_offset = s - pattern; + return result; +} + + + +/* Given PATTERN, which is a string as used by GnuPG to specify a + certificate, return all matching certificates by calling the + supplied function RETFNC. */ +gpg_error_t +get_certs_bypattern (const char *pattern, + gpg_error_t (*retfnc)(void*,ksba_cert_t), + void *retfnc_data) +{ + gpg_error_t err = GPG_ERR_BUG; + enum pattern_class class; + size_t offset, sn_offset; + const char *hexserialno; + ksba_sexp_t serialno = NULL; + ksba_cert_t cert = NULL; + unsigned int seq; + + if (!pattern || !retfnc) + return gpg_error (GPG_ERR_INV_ARG); + + class = classify_pattern (pattern, &offset, &sn_offset); + hexserialno = pattern + sn_offset; + pattern += offset; + switch (class) + { + case PATTERN_UNKNOWN: + err = gpg_error (GPG_ERR_INV_NAME); + break; + + case PATTERN_FINGERPRINT20: + cert = get_cert_byhexfpr (pattern); + err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND); + break; + + case PATTERN_SERIALNO_ISSUER: + serialno = hexsn_to_sexp (hexserialno); + if (!serialno) + err = gpg_error_from_syserror (); + else + { + cert = get_cert_bysn (pattern, serialno); + err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND); + } + break; + + case PATTERN_ISSUER: + for (seq=0,err=0; !err && (cert = get_cert_byissuer (pattern, seq)); seq++) + { + err = retfnc (retfnc_data, cert); + ksba_cert_release (cert); + cert = NULL; + } + if (!err && !seq) + err = gpg_error (GPG_ERR_NOT_FOUND); + break; + + case PATTERN_SUBJECT: + for (seq=0,err=0; !err && (cert = get_cert_bysubject (pattern, seq));seq++) + { + err = retfnc (retfnc_data, cert); + ksba_cert_release (cert); + cert = NULL; + } + if (!err && !seq) + err = gpg_error (GPG_ERR_NOT_FOUND); + break; + + case PATTERN_EMAIL: + case PATTERN_EMAIL_SUBSTR: + case PATTERN_FINGERPRINT16: + case PATTERN_SHORT_KEYID: + case PATTERN_LONG_KEYID: + case PATTERN_SUBSTR: + case PATTERN_SERIALNO: + /* Not supported. */ + err = gpg_error (GPG_ERR_INV_NAME); + } + + + if (!err && cert) + err = retfnc (retfnc_data, cert); + ksba_cert_release (cert); + xfree (serialno); + return err; +} + + + + + +/* Return the certificate matching ISSUER_DN and SERIALNO; if it is + not already in the cache, try to find it from other resources. */ +ksba_cert_t +find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno) +{ + gpg_error_t err; + ksba_cert_t cert; + cert_fetch_context_t context = NULL; + char *hexsn, *buf; + + /* First check whether it has already been cached. */ + cert = get_cert_bysn (issuer_dn, serialno); + if (cert) + return cert; + + /* Ask back to the service requester to return the certificate. + This is because we can assume that he already used the + certificate while checking for the CRL. */ + hexsn = serial_hex (serialno); + if (!hexsn) + { + log_error ("serial_hex() failed\n"); + return NULL; + } + buf = xtrymalloc (1 + strlen (hexsn) + 1 + strlen (issuer_dn) + 1); + if (!buf) + { + log_error ("can't allocate enough memory: %s\n", strerror (errno)); + xfree (hexsn); + return NULL; + } + strcpy (stpcpy (stpcpy (stpcpy (buf, "#"), hexsn),"/"), issuer_dn); + xfree (hexsn); + cert = get_cert_local (ctrl, buf); + xfree (buf); + if (cert) + { + cache_cert (cert); + return cert; /* Done. */ + } + + if (DBG_LOOKUP) + log_debug ("find_cert_bysn: certificate not returned by caller" + " - doing lookup\n"); + + /* Retrieve the certificate from external resources. */ + while (!cert) + { + ksba_sexp_t sn; + char *issdn; + + if (!context) + { + err = ca_cert_fetch (ctrl, &context, issuer_dn); + if (err) + { + log_error (_("error fetching certificate by S/N: %s\n"), + gpg_strerror (err)); + break; + } + } + + err = fetch_next_ksba_cert (context, &cert); + if (err) + { + log_error (_("error fetching certificate by S/N: %s\n"), + gpg_strerror (err) ); + break; + } + + issdn = ksba_cert_get_issuer (cert, 0); + if (strcmp (issuer_dn, issdn)) + { + log_debug ("find_cert_bysn: Ooops: issuer DN does not match\n"); + ksba_cert_release (cert); + cert = NULL; + ksba_free (issdn); + break; + } + + sn = ksba_cert_get_serial (cert); + + if (DBG_LOOKUP) + { + log_debug (" considering certificate (#"); + dump_serial (sn); + log_printf ("/"); + dump_string (issdn); + log_printf (")\n"); + } + + if (!compare_serialno (serialno, sn)) + { + ksba_free (sn); + ksba_free (issdn); + cache_cert (cert); + if (DBG_LOOKUP) + log_debug (" found\n"); + break; /* Ready. */ + } + + ksba_free (sn); + ksba_free (issdn); + ksba_cert_release (cert); + cert = NULL; + } + + end_cert_fetch (context); + return cert; +} + + +/* Return the certificate matching SUBJECT_DN and (if not NULL) + KEYID. If it is not already in the cache, try to find it from other + resources. Note, that the external search does not work for user + certificates because the LDAP lookup is on the caCertificate + attribute. For our purposes this is just fine. */ +ksba_cert_t +find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) +{ + gpg_error_t err; + int seq; + ksba_cert_t cert = NULL; + cert_fetch_context_t context = NULL; + ksba_sexp_t subj; + + /* If we have certificates from an OCSP request we first try to use + them. This is because these certificates will really be the + required ones and thus even in the case that they can't be + uniquely located by the following code we can use them. This is + for example required by Telesec certificates where a keyId is + used but the issuer certificate comes without a subject keyId! */ + if (ctrl->ocsp_certs && subject_dn) + { + cert_item_t ci; + cert_ref_t cr; + int i; + + /* For efficiency reasons we won't use get_cert_bysubject here. */ + acquire_cache_read_lock (); + for (i=0; i < 256; i++) + for (ci=cert_cache[i]; ci; ci = ci->next) + if (ci->cert && ci->subject_dn + && !strcmp (ci->subject_dn, subject_dn)) + for (cr=ctrl->ocsp_certs; cr; cr = cr->next) + if (!memcmp (ci->fpr, cr->fpr, 20)) + { + ksba_cert_ref (ci->cert); + release_cache_lock (); + return ci->cert; /* We use this certificate. */ + } + release_cache_lock (); + if (DBG_LOOKUP) + log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n"); + } + + + /* First we check whether the certificate is cached. */ + for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++) + { + if (!keyid) + break; /* No keyid requested, so return the first one found. */ + if (!ksba_cert_get_subj_key_id (cert, NULL, &subj) + && !cmp_simple_canon_sexp (keyid, subj)) + { + xfree (subj); + break; /* Found matching cert. */ + } + xfree (subj); + ksba_cert_release (cert); + } + if (cert) + return cert; /* Done. */ + + if (DBG_LOOKUP) + log_debug ("find_cert_bysubject: certificate not in cache\n"); + + /* Ask back to the service requester to return the certificate. + This is because we can assume that he already used the + certificate while checking for the CRL. */ + if (keyid) + cert = get_cert_local_ski (ctrl, subject_dn, keyid); + else + { + /* In contrast to get_cert_local_ski, get_cert_local uses any + passed pattern, so we need to make sure that an exact subject + search is done. */ + char *buf; + + buf = xtrymalloc (1 + strlen (subject_dn) + 1); + if (!buf) + { + log_error ("can't allocate enough memory: %s\n", strerror (errno)); + return NULL; + } + strcpy (stpcpy (buf, "/"), subject_dn); + cert = get_cert_local (ctrl, buf); + xfree (buf); + } + if (cert) + { + cache_cert (cert); + return cert; /* Done. */ + } + + if (DBG_LOOKUP) + log_debug ("find_cert_bysubject: certificate not returned by caller" + " - doing lookup\n"); + + /* Locate the certificate using external resources. */ + while (!cert) + { + char *subjdn; + + if (!context) + { + err = ca_cert_fetch (ctrl, &context, subject_dn); + if (err) + { + log_error (_("error fetching certificate by subject: %s\n"), + gpg_strerror (err)); + break; + } + } + + err = fetch_next_ksba_cert (context, &cert); + if (err) + { + log_error (_("error fetching certificate by subject: %s\n"), + gpg_strerror (err) ); + break; + } + + subjdn = ksba_cert_get_subject (cert, 0); + if (strcmp (subject_dn, subjdn)) + { + log_info ("find_cert_bysubject: subject DN does not match\n"); + ksba_cert_release (cert); + cert = NULL; + ksba_free (subjdn); + continue; + } + + + if (DBG_LOOKUP) + { + log_debug (" considering certificate (/"); + dump_string (subjdn); + log_printf (")\n"); + } + ksba_free (subjdn); + + /* If no key ID has been provided, we return the first match. */ + if (!keyid) + { + cache_cert (cert); + if (DBG_LOOKUP) + log_debug (" found\n"); + break; /* Ready. */ + } + + /* With the key ID given we need to compare it. */ + if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)) + { + if (!cmp_simple_canon_sexp (keyid, subj)) + { + ksba_free (subj); + cache_cert (cert); + if (DBG_LOOKUP) + log_debug (" found\n"); + break; /* Ready. */ + } + } + + ksba_free (subj); + ksba_cert_release (cert); + cert = NULL; + } + + end_cert_fetch (context); + return cert; +} + + + +/* Return 0 if the certificate is a trusted certificate. Returns + GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in + case of systems errors. */ +gpg_error_t +is_trusted_cert (ksba_cert_t cert) +{ + unsigned char fpr[20]; + cert_item_t ci; + + cert_compute_fpr (cert, fpr); + + acquire_cache_read_lock (); + for (ci=cert_cache[*fpr]; ci; ci = ci->next) + if (ci->cert && !memcmp (ci->fpr, fpr, 20)) + { + if (ci->flags.trusted) + { + release_cache_lock (); + return 0; /* Yes, it is trusted. */ + } + break; + } + + release_cache_lock (); + return gpg_error (GPG_ERR_NOT_TRUSTED); +} + + + +/* Given the certificate CERT locate the issuer for this certificate + and return it at R_CERT. Returns 0 on success or + GPG_ERR_NOT_FOUND. */ +gpg_error_t +find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert) +{ + gpg_error_t err; + char *issuer_dn; + ksba_cert_t issuer_cert = NULL; + ksba_name_t authid; + ksba_sexp_t authidno; + ksba_sexp_t keyid; + + *r_cert = NULL; + + issuer_dn = ksba_cert_get_issuer (cert, 0); + if (!issuer_dn) + { + log_error (_("no issuer found in certificate\n")); + err = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + /* First we need to check whether we can return that certificate + using the authorithyKeyIdentifier. */ + err = ksba_cert_get_auth_key_id (cert, &keyid, &authid, &authidno); + if (err) + { + log_info (_("error getting authorityKeyIdentifier: %s\n"), + gpg_strerror (err)); + } + else + { + const char *s = ksba_name_enum (authid, 0); + if (s && *authidno) + { + issuer_cert = find_cert_bysn (ctrl, s, authidno); + } + if (!issuer_cert && keyid) + { + /* Not found by issuer+s/n. Now that we have an AKI + keyIdentifier look for a certificate with a matching + SKI. */ + issuer_cert = find_cert_bysubject (ctrl, issuer_dn, keyid); + } + /* Print a note so that the user does not feel too helpless when + an issuer certificate was found and gpgsm prints BAD + signature because it is not the correct one. */ + if (!issuer_cert) + { + log_info ("issuer certificate "); + if (keyid) + { + log_printf ("{"); + dump_serial (keyid); + log_printf ("} "); + } + if (authidno) + { + log_printf ("(#"); + dump_serial (authidno); + log_printf ("/"); + dump_string (s); + log_printf (") "); + } + log_printf ("not found using authorityKeyIdentifier\n"); + } + ksba_name_release (authid); + xfree (authidno); + xfree (keyid); + } + + /* If this did not work, try just with the issuer's name and assume + that there is only one such certificate. We only look into our + cache then. */ + if (err || !issuer_cert) + { + issuer_cert = get_cert_bysubject (issuer_dn, 0); + if (issuer_cert) + err = 0; + } + + leave: + if (!err && !issuer_cert) + err = gpg_error (GPG_ERR_NOT_FOUND); + + xfree (issuer_dn); + + if (err) + ksba_cert_release (issuer_cert); + else + *r_cert = issuer_cert; + + return err; +} diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h new file mode 100644 index 0000000..9986f15 --- /dev/null +++ b/dirmngr/certcache.h @@ -0,0 +1,103 @@ +/* certcache.h - Certificate caching + * Copyright (C) 2004, 2008 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#ifndef CERTCACHE_H +#define CERTCACHE_H + +/* First time initialization of the certificate cache. */ +void cert_cache_init (void); + +/* Deinitialize the certificate cache. */ +void cert_cache_deinit (int full); + +/* Print some statistics to the log file. */ +void cert_cache_print_stats (void); + +/* Compute the fingerprint of the certificate CERT and put it into + the 20 bytes large buffer DIGEST. Return address of this buffer. */ +unsigned char *cert_compute_fpr (ksba_cert_t cert, unsigned char *digest); + +/* Put CERT into the certificate cache. */ +gpg_error_t cache_cert (ksba_cert_t cert); + +/* Put CERT into the certificate cache and return the fingerprint. */ +gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer); + +/* Return 0 if the certificate is a trusted certificate. Returns + GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in + case of systems errors. */ +gpg_error_t is_trusted_cert (ksba_cert_t cert); + + +/* Return a certificate object for the given fingerprint. FPR is + expected to be a 20 byte binary SHA-1 fingerprint. If no matching + certificate is available in the cache NULL is returned. The caller + must release a returned certificate. */ +ksba_cert_t get_cert_byfpr (const unsigned char *fpr); + +/* Return a certificate object for the given fingerprint. STRING is + expected to be a SHA-1 fingerprint in standard hex notation with or + without colons. If no matching certificate is available in the + cache NULL is returned. The caller must release a returned + certificate. */ +ksba_cert_t get_cert_byhexfpr (const char *string); + +/* Return the certificate matching ISSUER_DN and SERIALNO. */ +ksba_cert_t get_cert_bysn (const char *issuer_dn, ksba_sexp_t serialno); + +/* Return the certificate matching ISSUER_DN. SEQ should initially be + set to 0 and bumped up to get the next issuer with that DN. */ +ksba_cert_t get_cert_byissuer (const char *issuer_dn, unsigned int seq); + +/* Return the certificate matching SUBJECT_DN. SEQ should initially be + set to 0 and bumped up to get the next issuer with that DN. */ +ksba_cert_t get_cert_bysubject (const char *subject_dn, unsigned int seq); + +/* Given PATTERN, which is a string as used by GnuPG to specify a + certificate, return all matching certificates by calling the + supplied function RETFNC. */ +gpg_error_t get_certs_bypattern (const char *pattern, + gpg_error_t (*retfnc)(void*,ksba_cert_t), + void *retfnc_data); + +/* Return the certificate matching ISSUER_DN and SERIALNO; if it is + not already in the cache, try to find it from other resources. */ +ksba_cert_t find_cert_bysn (ctrl_t ctrl, + const char *issuer_dn, ksba_sexp_t serialno); + + +/* Return the certificate matching SUBJECT_DN and (if not NULL) KEYID. If + it is not already in the cache, try to find it from other + resources. Note, that the external search does not work for user + certificates because the LDAP lookup is on the caCertificate + attribute. For our purposes this is just fine. */ +ksba_cert_t find_cert_bysubject (ctrl_t ctrl, + const char *subject_dn, ksba_sexp_t keyid); + +/* Given the certificate CERT locate the issuer for this certificate + and return it at R_CERT. Returns 0 on success or + GPG_ERR_NOT_FOUND. */ +gpg_error_t find_issuing_cert (ctrl_t ctrl, + ksba_cert_t cert, ksba_cert_t *r_cert); + + + + +#endif /*CERTCACHE_H*/ diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c new file mode 100644 index 0000000..07fa5b1 --- /dev/null +++ b/dirmngr/crlcache.c @@ -0,0 +1,2584 @@ +/* crlcache.c - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2003, 2004, 2005, 2008 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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, see . + */ + +/* + + 1. To keep track of the CRLs actually cached and to store the meta + information of the CRLs a simple record oriented text file is + used. Fields in the file are colon (':') separated and values + containing colons or linefeeds are percent escaped (e.g. a colon + itself is represented as "%3A"). + + The first field is a record type identifier, so that the file is + useful to keep track of other meta data too. + + The name of the file is "DIR.txt". + + + 1.1. Comment record + + Field 1: Constant beginning with "#". + + Other fields are not defined and such a record is simply + skipped during processing. + + 1.2. Version record + + Field 1: Constant "v" + Field 2: Version number of this file. Must be 1. + + This record must be the first non-comment record record and + there shall only exist one record of this type. + + 1.3. CRL cache record + + Field 1: Constant "c", "u" or "i". + A "c" or "u" indicate a valid cache entry, however + "u" requires that a user root certificate check needs + to be done. + An "i" indicates an invalid cache entry which should + not be used but still exists so that it can be + updated at NEXT_UPDATE. + Field 2: Hexadecimal encoded SHA-1 hash of the issuer DN using + uppercase letters. + Field 3: Issuer DN in RFC-2253 notation. + Field 4: URL used to retrieve the corresponding CRL. + Field 5: 15 character ISO timestamp with THIS_UPDATE. + Field 6: 15 character ISO timestamp with NEXT_UPDATE. + Field 7: Hexadecimal encoded MD-5 hash of the DB file to detect + accidental modified (i.e. deleted and created) cache files. + Field 8: optional CRL number as a hex string. + Field 9: AuthorityKeyID.issuer, each Name separated by 0x01 + Field 10: AuthorityKeyID.serial + Field 11: Hex fingerprint of trust anchor if field 1 is 'u'. + + 2. Layout of the standard CRL Cache DB file: + + We use records of variable length with this structure + + n bytes Serialnumber (binary) used as key + thus there is no need to store the length explicitly with DB2. + 1 byte Reason for revocation + (currently the KSBA reason flags are used) + 15 bytes ISO date of revocation (e.g. 19980815T142000) + Note that there is no terminating 0 stored. + + The filename used is the hexadecimal (using uppercase letters) + SHA-1 hash value of the issuer DN prefixed with a "crl-" and + suffixed with a ".db". Thus the length of the filename is 47. + + +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef HAVE_W32_SYSTEM +#include +#endif +#ifdef MKDIR_TAKES_ONE_ARG +#undef mkdir +#define mkdir(a,b) mkdir(a) +#endif + +#include "dirmngr.h" +#include "validate.h" +#include "certcache.h" +#include "crlcache.h" +#include "crlfetch.h" +#include "misc.h" +#include "cdb.h" + +/* Change this whenever the format changes */ +#define DBDIR_D "crls.d" +#define DBDIRFILE "DIR.txt" +#define DBDIRVERSION 1 + +/* The number of DB files we may have open at one time. We need to + limit this because there is no guarantee that the number of issuers + has a upper limit. We are currently using mmap, so it is a good + idea anyway to limit the number of opened cache files. */ +#define MAX_OPEN_DB_FILES 5 + + +static const char oidstr_crlNumber[] = "2.5.29.20"; +static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; +static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; + + +/* Definition of one cached item. */ +struct crl_cache_entry_s +{ + struct crl_cache_entry_s *next; + int deleted; /* True if marked for deletion. */ + int mark; /* Internally used by update_dir. */ + unsigned int lineno;/* A 0 indicates a new entry. */ + char *release_ptr; /* The actual allocated memory. */ + char *url; /* Points into RELEASE_PTR. */ + char *issuer; /* Ditto. */ + char *issuer_hash; /* Ditto. */ + char *dbfile_hash; /* MD5 sum of the cache file, points into RELEASE_PTR.*/ + int invalid; /* Can't use this CRL. */ + int user_trust_req; /* User supplied root certificate required. */ + char *check_trust_anchor; /* Malloced fingerprint. */ + ksba_isotime_t this_update; + ksba_isotime_t next_update; + ksba_isotime_t last_refresh; /* Use for the force_crl_refresh feature. */ + char *crl_number; + char *authority_issuer; + char *authority_serialno; + + struct cdb *cdb; /* The cache file handle or NULL if not open. */ + + unsigned int cdb_use_count; /* Current use count. */ + unsigned int cdb_lru_count; /* Used for LRU purposes. */ + int dbfile_checked; /* Set to true if the dbfile_hash value has + been checked one. */ +}; + + +/* Definition of the entire cache object. */ +struct crl_cache_s +{ + crl_cache_entry_t entries; +}; + +typedef struct crl_cache_s *crl_cache_t; + + +/* Prototypes. */ +static crl_cache_entry_t find_entry (crl_cache_entry_t first, + const char *issuer_hash); + + + +/* The currently loaded cache object. This is usually initialized + right at startup. */ +static crl_cache_t current_cache; + + + + + +/* Return the current cache object or bail out if it is has not yet + been initialized. */ +static crl_cache_t +get_current_cache (void) +{ + if (!current_cache) + log_fatal ("CRL cache has not yet been initialized\n"); + return current_cache; +} + + +/* + Create ae directory if it does not yet exists. Returns on + success, or -1 on error. + */ +static int +create_directory_if_needed (const char *name) +{ + DIR *dir; + char *fname; + + fname = make_filename (opt.homedir_cache, name, NULL); + dir = opendir (fname); + if (!dir) + { + log_info (_("creating directory '%s'\n"), fname); + if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR) ) + { + int save_errno = errno; + log_error (_("error creating directory '%s': %s\n"), + fname, strerror (errno)); + xfree (fname); + gpg_err_set_errno (save_errno); + return -1; + } + } + else + closedir (dir); + xfree (fname); + return 0; +} + +/* Remove all files from the cache directory. If FORCE is not true, + some sanity checks on the filenames are done. Return 0 if + everything went fine. */ +static int +cleanup_cache_dir (int force) +{ + char *dname = make_filename (opt.homedir_cache, DBDIR_D, NULL); + DIR *dir; + struct dirent *de; + int problem = 0; + + if (!force) + { /* Very minor sanity checks. */ + if (!strcmp (dname, "~/") || !strcmp (dname, "/" )) + { + log_error (_("ignoring database dir '%s'\n"), dname); + xfree (dname); + return -1; + } + } + + dir = opendir (dname); + if (!dir) + { + log_error (_("error reading directory '%s': %s\n"), + dname, strerror (errno)); + xfree (dname); + return -1; + } + + while ((de = readdir (dir))) + { + if (strcmp (de->d_name, "." ) && strcmp (de->d_name, "..")) + { + char *cdbname = make_filename (dname, de->d_name, NULL); + int okay; + struct stat sbuf; + + if (force) + okay = 1; + else + okay = (!stat (cdbname, &sbuf) && S_ISREG (sbuf.st_mode)); + + if (okay) + { + log_info (_("removing cache file '%s'\n"), cdbname); + if (gnupg_remove (cdbname)) + { + log_error ("failed to remove '%s': %s\n", + cdbname, strerror (errno)); + problem = -1; + } + } + else + log_info (_("not removing file '%s'\n"), cdbname); + xfree (cdbname); + } + } + xfree (dname); + closedir (dir); + return problem; +} + + +/* Read the next line from the file FP and return the line in an + malloced buffer. Return NULL on error or EOF. There is no + limitation os the line length. The trailing linefeed has been + removed, the function will read the last line of a file, even if + that is not terminated by a LF. */ +static char * +next_line_from_file (estream_t fp, gpg_error_t *r_err) +{ + char buf[300]; + char *largebuf = NULL; + size_t buflen; + size_t len = 0; + unsigned char *p; + int c; + char *tmpbuf; + + *r_err = 0; + p = buf; + buflen = sizeof buf - 1; + while ((c=es_getc (fp)) != EOF && c != '\n') + { + if (len >= buflen) + { + if (!largebuf) + { + buflen += 1024; + largebuf = xtrymalloc ( buflen + 1 ); + if (!largebuf) + { + *r_err = gpg_error_from_syserror (); + return NULL; + } + memcpy (largebuf, buf, len); + } + else + { + buflen += 1024; + tmpbuf = xtryrealloc (largebuf, buflen + 1); + if (!tmpbuf) + { + *r_err = gpg_error_from_syserror (); + xfree (largebuf); + return NULL; + } + largebuf = tmpbuf; + } + p = largebuf; + } + p[len++] = c; + } + if (c == EOF && !len) + return NULL; + p[len] = 0; + + if (largebuf) + tmpbuf = xtryrealloc (largebuf, len+1); + else + tmpbuf = xtrystrdup (buf); + if (!tmpbuf) + { + *r_err = gpg_error_from_syserror (); + xfree (largebuf); + } + return tmpbuf; +} + + +/* Release one cache entry. */ +static void +release_one_cache_entry (crl_cache_entry_t entry) +{ + if (entry) + { + if (entry->cdb) + { + int fd = cdb_fileno (entry->cdb); + cdb_free (entry->cdb); + xfree (entry->cdb); + if (close (fd)) + log_error (_("error closing cache file: %s\n"), strerror(errno)); + } + xfree (entry->release_ptr); + xfree (entry->check_trust_anchor); + xfree (entry); + } +} + + +/* Release the CACHE object. */ +static void +release_cache (crl_cache_t cache) +{ + crl_cache_entry_t entry, entry2; + + if (!cache) + return; + + for (entry = cache->entries; entry; entry = entry2) + { + entry2 = entry->next; + release_one_cache_entry (entry); + } + cache->entries = NULL; + xfree (cache); +} + + +/* Open the dir file FNAME or create a new one if it does not yet + exist. */ +static estream_t +open_dir_file (const char *fname) +{ + estream_t fp; + + fp = es_fopen (fname, "r"); + if (!fp) + { + log_error (_("failed to open cache dir file '%s': %s\n"), + fname, strerror (errno)); + + /* Make sure that the directory exists, try to create if otherwise. */ + if (create_directory_if_needed (NULL) + || create_directory_if_needed (DBDIR_D)) + return NULL; + fp = es_fopen (fname, "w"); + if (!fp) + { + log_error (_("error creating new cache dir file '%s': %s\n"), + fname, strerror (errno)); + return NULL; + } + es_fprintf (fp, "v:%d:\n", DBDIRVERSION); + if (es_ferror (fp)) + { + log_error (_("error writing new cache dir file '%s': %s\n"), + fname, strerror (errno)); + es_fclose (fp); + return NULL; + } + if (es_fclose (fp)) + { + log_error (_("error closing new cache dir file '%s': %s\n"), + fname, strerror (errno)); + return NULL; + } + + log_info (_("new cache dir file '%s' created\n"), fname); + + fp = es_fopen (fname, "r"); + if (!fp) + { + log_error (_("failed to re-open cache dir file '%s': %s\n"), + fname, strerror (errno)); + return NULL; + } + } + + return fp; +} + +/* Helper for open_dir. */ +static gpg_error_t +check_dir_version (estream_t *fpadr, const char *fname, + unsigned int *lineno, + int cleanup_on_mismatch) +{ + char *line; + gpg_error_t lineerr = 0; + estream_t fp = *fpadr; + int created = 0; + + retry: + while ((line = next_line_from_file (fp, &lineerr))) + { + ++*lineno; + if (*line == 'v' && line[1] == ':') + break; + else if (*line != '#') + { + log_error (_("first record of '%s' is not the version\n"), fname); + xfree (line); + return gpg_error (GPG_ERR_CONFIGURATION); + } + xfree (line); + } + if (lineerr) + return lineerr; + + /* The !line catches the case of an empty DIR file. We handle this + the same as a non-matching version. */ + if (!line || strtol (line+2, NULL, 10) != DBDIRVERSION) + { + if (!created && cleanup_on_mismatch) + { + log_error (_("old version of cache directory - cleaning up\n")); + es_fclose (fp); + *fpadr = NULL; + if (!cleanup_cache_dir (1)) + { + *lineno = 0; + fp = *fpadr = open_dir_file (fname); + if (!fp) + { + xfree (line); + return gpg_error (GPG_ERR_CONFIGURATION); + } + created = 1; + goto retry; + } + } + log_error (_("old version of cache directory - giving up\n")); + xfree (line); + return gpg_error (GPG_ERR_CONFIGURATION); + } + xfree (line); + return 0; +} + + +/* Open the dir file and read in all available information. Store + that in a newly allocated cache object and return that if + everything worked out fine. Create the cache directory and the dir + if it does not yet exist. Remove all files in that directory if + the version does not match. */ +static gpg_error_t +open_dir (crl_cache_t *r_cache) +{ + crl_cache_t cache; + char *fname; + char *line = NULL; + gpg_error_t lineerr = 0; + estream_t fp; + crl_cache_entry_t entry, *entrytail; + unsigned int lineno; + gpg_error_t err = 0; + int anyerr = 0; + + cache = xtrycalloc (1, sizeof *cache); + if (!cache) + return gpg_error_from_syserror (); + + fname = make_filename (opt.homedir_cache, DBDIR_D, DBDIRFILE, NULL); + + lineno = 0; + fp = open_dir_file (fname); + if (!fp) + { + err = gpg_error (GPG_ERR_CONFIGURATION); + goto leave; + } + + err = check_dir_version (&fp, fname, &lineno, 1); + if (err) + goto leave; + + + /* Read in all supported entries from the dir file. */ + cache->entries = NULL; + entrytail = &cache->entries; + xfree (line); + while ((line = next_line_from_file (fp, &lineerr))) + { + int fieldno; + char *p, *endp; + + lineno++; + if ( *line == 'c' || *line == 'u' || *line == 'i' ) + { + entry = xtrycalloc (1, sizeof *entry); + if (!entry) + { + err = gpg_error_from_syserror (); + goto leave; + } + entry->lineno = lineno; + entry->release_ptr = line; + if (*line == 'i') + { + entry->invalid = atoi (line+1); + if (entry->invalid < 1) + entry->invalid = 1; + } + else if (*line == 'u') + entry->user_trust_req = 1; + + for (fieldno=1, p = line; p; p = endp, fieldno++) + { + endp = strchr (p, ':'); + if (endp) + *endp++ = '\0'; + + switch (fieldno) + { + case 1: /* record type */ break; + case 2: entry->issuer_hash = p; break; + case 3: entry->issuer = unpercent_string (p); break; + case 4: entry->url = unpercent_string (p); break; + case 5: + strncpy (entry->this_update, p, 15); + entry->this_update[15] = 0; + break; + case 6: + strncpy (entry->next_update, p, 15); + entry->next_update[15] = 0; + break; + case 7: entry->dbfile_hash = p; break; + case 8: if (*p) entry->crl_number = p; break; + case 9: + if (*p) + entry->authority_issuer = unpercent_string (p); + break; + case 10: + if (*p) + entry->authority_serialno = unpercent_string (p); + break; + case 11: + if (*p) + entry->check_trust_anchor = xtrystrdup (p); + break; + default: + if (*p) + log_info (_("extra field detected in crl record of " + "'%s' line %u\n"), fname, lineno); + break; + } + } + + if (!entry->issuer_hash) + { + log_info (_("invalid line detected in '%s' line %u\n"), + fname, lineno); + xfree (entry); + entry = NULL; + } + else if (find_entry (cache->entries, entry->issuer_hash)) + { + /* Fixme: The duplicate checking used is not very + effective for large numbers of issuers. */ + log_info (_("duplicate entry detected in '%s' line %u\n"), + fname, lineno); + xfree (entry); + entry = NULL; + } + else + { + line = NULL; + *entrytail = entry; + entrytail = &entry->next; + } + } + else if (*line == '#') + ; + else + log_info (_("unsupported record type in '%s' line %u skipped\n"), + fname, lineno); + + if (line) + xfree (line); + } + if (lineerr) + { + err = lineerr; + log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); + goto leave; + } + if (es_ferror (fp)) + { + log_error (_("error reading '%s': %s\n"), fname, strerror (errno)); + err = gpg_error (GPG_ERR_CONFIGURATION); + goto leave; + } + + /* Now do some basic checks on the data. */ + for (entry = cache->entries; entry; entry = entry->next) + { + assert (entry->lineno); + if (strlen (entry->issuer_hash) != 40) + { + anyerr++; + log_error (_("invalid issuer hash in '%s' line %u\n"), + fname, entry->lineno); + } + else if ( !*entry->issuer ) + { + anyerr++; + log_error (_("no issuer DN in '%s' line %u\n"), + fname, entry->lineno); + } + else if ( check_isotime (entry->this_update) + || check_isotime (entry->next_update)) + { + anyerr++; + log_error (_("invalid timestamp in '%s' line %u\n"), + fname, entry->lineno); + } + + /* Checks not leading to an immediate fail. */ + if (strlen (entry->dbfile_hash) != 32) + log_info (_("WARNING: invalid cache file hash in '%s' line %u\n"), + fname, entry->lineno); + } + + if (anyerr) + { + log_error (_("detected errors in cache dir file\n")); + log_info (_("please check the reason and manually delete that file\n")); + err = gpg_error (GPG_ERR_CONFIGURATION); + } + + + leave: + es_fclose (fp); + xfree (line); + xfree (fname); + if (err) + { + release_cache (cache); + cache = NULL; + } + *r_cache = cache; + return err; +} + +static void +write_percented_string (const char *s, estream_t fp) +{ + for (; *s; s++) + if (*s == ':') + es_fputs ("%3A", fp); + else if (*s == '\n') + es_fputs ("%0A", fp); + else if (*s == '\r') + es_fputs ("%0D", fp); + else + es_putc (*s, fp); +} + + +static void +write_dir_line_crl (estream_t fp, crl_cache_entry_t e) +{ + if (e->invalid) + es_fprintf (fp, "i%d", e->invalid); + else if (e->user_trust_req) + es_putc ('u', fp); + else + es_putc ('c', fp); + es_putc (':', fp); + es_fputs (e->issuer_hash, fp); + es_putc (':', fp); + write_percented_string (e->issuer, fp); + es_putc (':', fp); + write_percented_string (e->url, fp); + es_putc (':', fp); + es_fwrite (e->this_update, 15, 1, fp); + es_putc (':', fp); + es_fwrite (e->next_update, 15, 1, fp); + es_putc (':', fp); + es_fputs (e->dbfile_hash, fp); + es_putc (':', fp); + if (e->crl_number) + es_fputs (e->crl_number, fp); + es_putc (':', fp); + if (e->authority_issuer) + write_percented_string (e->authority_issuer, fp); + es_putc (':', fp); + if (e->authority_serialno) + es_fputs (e->authority_serialno, fp); + es_putc (':', fp); + if (e->check_trust_anchor && e->user_trust_req) + es_fputs (e->check_trust_anchor, fp); + es_putc ('\n', fp); +} + + +/* Update the current dir file using the cache. */ +static gpg_error_t +update_dir (crl_cache_t cache) +{ + char *fname = NULL; + char *tmpfname = NULL; + char *line = NULL; + gpg_error_t lineerr = 0; + estream_t fp; + estream_t fpout = NULL; + crl_cache_entry_t e; + unsigned int lineno; + gpg_error_t err = 0; + + fname = make_filename (opt.homedir_cache, DBDIR_D, DBDIRFILE, NULL); + + /* Fixme: Take an update file lock here. */ + + for (e= cache->entries; e; e = e->next) + e->mark = 1; + + lineno = 0; + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_errno (errno); + log_error (_("failed to open cache dir file '%s': %s\n"), + fname, strerror (errno)); + goto leave; + } + err = check_dir_version (&fp, fname, &lineno, 0); + if (err) + goto leave; + es_rewind (fp); + lineno = 0; + + /* Create a temporary DIR file. */ + { + char *tmpbuf, *p; + const char *nodename; +#ifndef HAVE_W32_SYSTEM + struct utsname utsbuf; +#endif + +#ifdef HAVE_W32_SYSTEM + nodename = "unknown"; +#else + if (uname (&utsbuf)) + nodename = "unknown"; + else + nodename = utsbuf.nodename; +#endif + + gpgrt_asprintf (&tmpbuf, "DIR-tmp-%s-%u-%p.txt.tmp", + nodename, (unsigned int)getpid (), &tmpbuf); + if (!tmpbuf) + { + err = gpg_error_from_errno (errno); + log_error (_("failed to create temporary cache dir file '%s': %s\n"), + tmpfname, strerror (errno)); + goto leave; + } + for (p=tmpbuf; *p; p++) + if (*p == '/') + *p = '.'; + tmpfname = make_filename (opt.homedir_cache, DBDIR_D, tmpbuf, NULL); + xfree (tmpbuf); + } + fpout = es_fopen (tmpfname, "w"); + if (!fpout) + { + err = gpg_error_from_errno (errno); + log_error (_("failed to create temporary cache dir file '%s': %s\n"), + tmpfname, strerror (errno)); + goto leave; + } + + while ((line = next_line_from_file (fp, &lineerr))) + { + lineno++; + if (*line == 'c' || *line == 'u' || *line == 'i') + { + /* Extract the issuer hash field. */ + char *fieldp, *endp; + + fieldp = strchr (line, ':'); + endp = fieldp? strchr (++fieldp, ':') : NULL; + if (endp) + { + /* There should be no percent within the issuer hash + field, thus we can compare it pretty easily. */ + *endp = 0; + e = find_entry ( cache->entries, fieldp); + *endp = ':'; /* Restore original line. */ + if (e && e->deleted) + { + /* Marked for deletion, so don't write it. */ + e->mark = 0; + } + else if (e) + { + /* Yep, this is valid entry we know about; write it out */ + write_dir_line_crl (fpout, e); + e->mark = 0; + } + else + { /* We ignore entries we don't have in our cache + because they may have been added in the meantime + by other instances of dirmngr. */ + es_fprintf (fpout, "# Next line added by " + "another process; our pid is %lu\n", + (unsigned long)getpid ()); + es_fputs (line, fpout); + es_putc ('\n', fpout); + } + } + else + { + es_fputs ("# Invalid line detected: ", fpout); + es_fputs (line, fpout); + es_putc ('\n', fpout); + } + } + else + { + /* Write out all non CRL lines as they are. */ + es_fputs (line, fpout); + es_putc ('\n', fpout); + } + + xfree (line); + } + if (!es_ferror (fp) && !es_ferror (fpout) && !lineerr) + { + /* Write out the remaining entries. */ + for (e= cache->entries; e; e = e->next) + if (e->mark) + { + if (!e->deleted) + write_dir_line_crl (fpout, e); + e->mark = 0; + } + } + if (lineerr) + { + err = lineerr; + log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); + goto leave; + } + if (es_ferror (fp)) + { + err = gpg_error_from_errno (errno); + log_error (_("error reading '%s': %s\n"), fname, strerror (errno)); + } + if (es_ferror (fpout)) + { + err = gpg_error_from_errno (errno); + log_error (_("error writing '%s': %s\n"), tmpfname, strerror (errno)); + } + if (err) + goto leave; + + /* Rename the files. */ + es_fclose (fp); + fp = NULL; + if (es_fclose (fpout)) + { + err = gpg_error_from_errno (errno); + log_error (_("error closing '%s': %s\n"), tmpfname, strerror (errno)); + goto leave; + } + fpout = NULL; + +#ifdef HAVE_W32_SYSTEM + /* No atomic mv on W32 systems. */ + gnupg_remove (fname); +#endif + if (rename (tmpfname, fname)) + { + err = gpg_error_from_errno (errno); + log_error (_("error renaming '%s' to '%s': %s\n"), + tmpfname, fname, strerror (errno)); + goto leave; + } + + leave: + /* Fixme: Relinquish update lock. */ + xfree (line); + es_fclose (fp); + xfree (fname); + if (fpout) + { + es_fclose (fpout); + if (err && tmpfname) + gnupg_remove (tmpfname); + } + xfree (tmpfname); + return err; +} + + + + +/* Create the filename for the cache file from the 40 byte ISSUER_HASH + string. Caller must release the return string. */ +static char * +make_db_file_name (const char *issuer_hash) +{ + char bname[50]; + + assert (strlen (issuer_hash) == 40); + memcpy (bname, "crl-", 4); + memcpy (bname + 4, issuer_hash, 40); + strcpy (bname + 44, ".db"); + return make_filename (opt.homedir_cache, DBDIR_D, bname, NULL); +} + + +/* Hash the file FNAME and return the MD5 digest in MD5BUFFER. The + caller must allocate MD%buffer wityh at least 16 bytes. Returns 0 + on success. */ +static int +hash_dbfile (const char *fname, unsigned char *md5buffer) +{ + estream_t fp; + char *buffer; + size_t n; + gcry_md_hd_t md5; + gpg_error_t err; + + buffer = xtrymalloc (65536); + fp = buffer? es_fopen (fname, "rb") : NULL; + if (!fp) + { + log_error (_("can't hash '%s': %s\n"), fname, strerror (errno)); + xfree (buffer); + return -1; + } + + err = gcry_md_open (&md5, GCRY_MD_MD5, 0); + if (err) + { + log_error (_("error setting up MD5 hash context: %s\n"), + gpg_strerror (err)); + xfree (buffer); + es_fclose (fp); + return -1; + } + + /* We better hash some information about the cache file layout in. */ + sprintf (buffer, "%.100s/%.100s:%d", DBDIR_D, DBDIRFILE, DBDIRVERSION); + gcry_md_write (md5, buffer, strlen (buffer)); + + for (;;) + { + n = es_fread (buffer, 1, 65536, fp); + if (n < 65536 && es_ferror (fp)) + { + log_error (_("error hashing '%s': %s\n"), fname, strerror (errno)); + xfree (buffer); + es_fclose (fp); + gcry_md_close (md5); + return -1; + } + if (!n) + break; + gcry_md_write (md5, buffer, n); + } + es_fclose (fp); + xfree (buffer); + gcry_md_final (md5); + + memcpy (md5buffer, gcry_md_read (md5, GCRY_MD_MD5), 16); + gcry_md_close (md5); + return 0; +} + +/* Compare the file FNAME against the dexified MD5 hash MD5HASH and + return 0 if they match. */ +static int +check_dbfile (const char *fname, const char *md5hexvalue) +{ + unsigned char buffer1[16], buffer2[16]; + + if (strlen (md5hexvalue) != 32) + { + log_error (_("invalid formatted checksum for '%s'\n"), fname); + return -1; + } + unhexify (buffer1, md5hexvalue); + + if (hash_dbfile (fname, buffer2)) + return -1; + + return memcmp (buffer1, buffer2, 16); +} + + +/* Open the cache file for ENTRY. This function implements a caching + strategy and might close unused cache files. It is required to use + unlock_db_file after using the file. */ +static struct cdb * +lock_db_file (crl_cache_t cache, crl_cache_entry_t entry) +{ + char *fname; + int fd; + int open_count; + crl_cache_entry_t e; + + if (entry->cdb) + { + entry->cdb_use_count++; + return entry->cdb; + } + + for (open_count = 0, e = cache->entries; e; e = e->next) + { + if (e->cdb) + open_count++; +/* log_debug ("CACHE: cdb=%p use_count=%u lru_count=%u\n", */ +/* e->cdb,e->cdb_use_count,e->cdb_lru_count); */ + } + + /* If there are too many file open, find the least recent used DB + file and close it. Note that for Pth thread safeness we need to + use a loop here. */ + while (open_count >= MAX_OPEN_DB_FILES ) + { + crl_cache_entry_t last_e = NULL; + unsigned int last_lru = (unsigned int)(-1); + + for (e = cache->entries; e; e = e->next) + if (e->cdb && !e->cdb_use_count && e->cdb_lru_count < last_lru) + { + last_lru = e->cdb_lru_count; + last_e = e; + } + if (!last_e) + { + log_error (_("too many open cache files; can't open anymore\n")); + return NULL; + } + +/* log_debug ("CACHE: closing file at cdb=%p\n", last_e->cdb); */ + + fd = cdb_fileno (last_e->cdb); + cdb_free (last_e->cdb); + xfree (last_e->cdb); + last_e->cdb = NULL; + if (close (fd)) + log_error (_("error closing cache file: %s\n"), strerror(errno)); + open_count--; + } + + + fname = make_db_file_name (entry->issuer_hash); + if (opt.verbose) + log_info (_("opening cache file '%s'\n"), fname ); + + if (!entry->dbfile_checked) + { + if (!check_dbfile (fname, entry->dbfile_hash)) + entry->dbfile_checked = 1; + /* Note, in case of an error we don't print an error here but + let require the caller to do that check. */ + } + + entry->cdb = xtrycalloc (1, sizeof *entry->cdb); + if (!entry->cdb) + { + xfree (fname); + return NULL; + } + fd = open (fname, O_RDONLY); + if (fd == -1) + { + log_error (_("error opening cache file '%s': %s\n"), + fname, strerror (errno)); + xfree (entry->cdb); + entry->cdb = NULL; + xfree (fname); + return NULL; + } + if (cdb_init (entry->cdb, fd)) + { + log_error (_("error initializing cache file '%s' for reading: %s\n"), + fname, strerror (errno)); + xfree (entry->cdb); + entry->cdb = NULL; + close (fd); + xfree (fname); + return NULL; + } + xfree (fname); + + entry->cdb_use_count = 1; + entry->cdb_lru_count = 0; + + return entry->cdb; +} + +/* Unlock a cache file, so that it can be reused. */ +static void +unlock_db_file (crl_cache_t cache, crl_cache_entry_t entry) +{ + if (!entry->cdb) + log_error (_("calling unlock_db_file on a closed file\n")); + else if (!entry->cdb_use_count) + log_error (_("calling unlock_db_file on an unlocked file\n")); + else + { + entry->cdb_use_count--; + entry->cdb_lru_count++; + } + + /* If the entry was marked for deletion in the meantime do it now. + We do this for the sake of Pth thread safeness. */ + if (!entry->cdb_use_count && entry->deleted) + { + crl_cache_entry_t eprev, enext; + + enext = entry->next; + for (eprev = cache->entries; + eprev && eprev->next != entry; eprev = eprev->next) + ; + assert (eprev); + if (eprev == cache->entries) + cache->entries = enext; + else + eprev->next = enext; + /* FIXME: Do we leak ENTRY? */ + } +} + + +/* Find ISSUER_HASH in our cache FIRST. This may be used to enumerate + the linked list we use to keep the CRLs of an issuer. */ +static crl_cache_entry_t +find_entry (crl_cache_entry_t first, const char *issuer_hash) +{ + while (first && (first->deleted || strcmp (issuer_hash, first->issuer_hash))) + first = first->next; + return first; +} + + +/* Create a new CRL cache. This function is usually called only once. + never fail. */ +void +crl_cache_init(void) +{ + crl_cache_t cache = NULL; + gpg_error_t err; + + if (current_cache) + { + log_error ("crl cache has already been initialized - not doing twice\n"); + return; + } + + err = open_dir (&cache); + if (err) + log_fatal (_("failed to create a new cache object: %s\n"), + gpg_strerror (err)); + current_cache = cache; +} + + +/* Remove the cache information and all its resources. Note that we + still keep the cache on disk. */ +void +crl_cache_deinit (void) +{ + if (current_cache) + { + release_cache (current_cache); + current_cache = NULL; + } +} + + +/* Delete the cache from disk. Return 0 on success.*/ +int +crl_cache_flush (void) +{ + int rc; + + rc = cleanup_cache_dir (0)? -1 : 0; + + return rc; +} + + +/* Check whether the certificate identified by ISSUER_HASH and + SN/SNLEN is valid; i.e. not listed in our cache. With + FORCE_REFRESH set to true, a new CRL will be retrieved even if the + cache has not yet expired. We use a 30 minutes threshold here so + that invoking this function several times won't load the CRL over + and over. */ +static crl_cache_result_t +cache_isvalid (ctrl_t ctrl, const char *issuer_hash, + const unsigned char *sn, size_t snlen, + int force_refresh) +{ + crl_cache_t cache = get_current_cache (); + crl_cache_result_t retval; + struct cdb *cdb; + int rc; + crl_cache_entry_t entry; + gnupg_isotime_t current_time; + size_t n; + + (void)ctrl; + + entry = find_entry (cache->entries, issuer_hash); + if (!entry) + { + log_info (_("no CRL available for issuer id %s\n"), issuer_hash ); + return CRL_CACHE_DONTKNOW; + } + + gnupg_get_isotime (current_time); + if (strcmp (entry->next_update, current_time) < 0 ) + { + log_info (_("cached CRL for issuer id %s too old; update required\n"), + issuer_hash); + return CRL_CACHE_DONTKNOW; + } + if (force_refresh) + { + gnupg_isotime_t tmptime; + + if (*entry->last_refresh) + { + gnupg_copy_time (tmptime, entry->last_refresh); + add_seconds_to_isotime (tmptime, 30 * 60); + if (strcmp (tmptime, current_time) < 0 ) + { + log_info (_("force-crl-refresh active and %d minutes passed for" + " issuer id %s; update required\n"), + 30, issuer_hash); + return CRL_CACHE_DONTKNOW; + } + } + else + { + log_info (_("force-crl-refresh active for" + " issuer id %s; update required\n"), + issuer_hash); + return CRL_CACHE_DONTKNOW; + } + } + + if (entry->invalid) + { + log_info (_("available CRL for issuer ID %s can't be used\n"), + issuer_hash); + return CRL_CACHE_CANTUSE; + } + + cdb = lock_db_file (cache, entry); + if (!cdb) + return CRL_CACHE_DONTKNOW; /* Hmmm, not the best error code. */ + + if (!entry->dbfile_checked) + { + log_error (_("cached CRL for issuer id %s tampered; we need to update\n") + , issuer_hash); + unlock_db_file (cache, entry); + return CRL_CACHE_DONTKNOW; + } + + rc = cdb_find (cdb, sn, snlen); + if (rc == 1) + { + n = cdb_datalen (cdb); + if (n != 16) + { + log_error (_("WARNING: invalid cache record length for S/N ")); + log_printf ("0x"); + log_printhex ("", sn, snlen); + } + else if (opt.verbose) + { + unsigned char record[16]; + char *tmp = hexify_data (sn, snlen, 1); + + if (cdb_read (cdb, record, n, cdb_datapos (cdb))) + log_error (_("problem reading cache record for S/N %s: %s\n"), + tmp, strerror (errno)); + else + log_info (_("S/N %s is not valid; reason=%02X date=%.15s\n"), + tmp, *record, record+1); + xfree (tmp); + } + retval = CRL_CACHE_INVALID; + } + else if (!rc) + { + if (opt.verbose) + { + char *serialno = hexify_data (sn, snlen, 1); + log_info (_("S/N %s is valid, it is not listed in the CRL\n"), + serialno ); + xfree (serialno); + } + retval = CRL_CACHE_VALID; + } + else + { + log_error (_("error getting data from cache file: %s\n"), + strerror (errno)); + retval = CRL_CACHE_DONTKNOW; + } + + + if (entry->user_trust_req + && (retval == CRL_CACHE_VALID || retval == CRL_CACHE_INVALID)) + { + if (!entry->check_trust_anchor) + { + log_error ("inconsistent data on user trust check\n"); + retval = CRL_CACHE_CANTUSE; + } + else if (get_istrusted_from_client (ctrl, entry->check_trust_anchor)) + { + if (opt.verbose) + log_info ("no system trust and client does not trust either\n"); + retval = CRL_CACHE_CANTUSE; + } + else + { + /* Okay, the CRL is considered valid by the client and thus + we can return the result as is. */ + } + } + + unlock_db_file (cache, entry); + + return retval; +} + + +/* Check whether the certificate identified by ISSUER_HASH and + SERIALNO is valid; i.e. not listed in our cache. With + FORCE_REFRESH set to true, a new CRL will be retrieved even if the + cache has not yet expired. We use a 30 minutes threshold here so + that invoking this function several times won't load the CRL over + and over. */ +crl_cache_result_t +crl_cache_isvalid (ctrl_t ctrl, const char *issuer_hash, const char *serialno, + int force_refresh) +{ + crl_cache_result_t result; + unsigned char snbuf_buffer[50]; + unsigned char *snbuf; + size_t n; + + n = strlen (serialno)/2+1; + if (n < sizeof snbuf_buffer - 1) + snbuf = snbuf_buffer; + else + { + snbuf = xtrymalloc (n); + if (!snbuf) + return CRL_CACHE_DONTKNOW; + } + + n = unhexify (snbuf, serialno); + + result = cache_isvalid (ctrl, issuer_hash, snbuf, n, force_refresh); + + if (snbuf != snbuf_buffer) + xfree (snbuf); + + return result; +} + + +/* Check whether the certificate CERT is valid; i.e. not listed in our + cache. With FORCE_REFRESH set to true, a new CRL will be retrieved + even if the cache has not yet expired. We use a 30 minutes + threshold here so that invoking this function several times won't + load the CRL over and over. */ +gpg_error_t +crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert, + int force_refresh) +{ + gpg_error_t err; + crl_cache_result_t result; + unsigned char issuerhash[20]; + char issuerhash_hex[41]; + ksba_sexp_t serial; + unsigned char *sn; + size_t snlen; + char *endp, *tmp; + int i; + + /* Compute the hash value of the issuer name. */ + tmp = ksba_cert_get_issuer (cert, 0); + if (!tmp) + { + log_error ("oops: issuer missing in certificate\n"); + return gpg_error (GPG_ERR_INV_CERT_OBJ); + } + gcry_md_hash_buffer (GCRY_MD_SHA1, issuerhash, tmp, strlen (tmp)); + xfree (tmp); + for (i=0,tmp=issuerhash_hex; i < 20; i++, tmp += 2) + sprintf (tmp, "%02X", issuerhash[i]); + + /* Get the serial number. */ + serial = ksba_cert_get_serial (cert); + if (!serial) + { + log_error ("oops: S/N missing in certificate\n"); + return gpg_error (GPG_ERR_INV_CERT_OBJ); + } + sn = serial; + if (*sn != '(') + { + log_error ("oops: invalid S/N\n"); + xfree (serial); + return gpg_error (GPG_ERR_INV_CERT_OBJ); + } + sn++; + snlen = strtoul (sn, &endp, 10); + sn = endp; + if (*sn != ':') + { + log_error ("oops: invalid S/N\n"); + xfree (serial); + return gpg_error (GPG_ERR_INV_CERT_OBJ); + } + sn++; + + /* Check the cache. */ + result = cache_isvalid (ctrl, issuerhash_hex, sn, snlen, force_refresh); + switch (result) + { + case CRL_CACHE_VALID: + err = 0; + break; + case CRL_CACHE_INVALID: + err = gpg_error (GPG_ERR_CERT_REVOKED); + break; + case CRL_CACHE_DONTKNOW: + err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + break; + case CRL_CACHE_CANTUSE: + err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + break; + default: + log_fatal ("cache_isvalid returned invalid status code %d\n", result); + } + + xfree (serial); + return err; +} + + +/* Prepare a hash context for the signature verification. Input is + the CRL and the output is the hash context MD as well as the uses + algorithm identifier ALGO. */ +static gpg_error_t +start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo) +{ + gpg_error_t err; + const char *algoid; + + algoid = ksba_crl_get_digest_algo (crl); + *algo = gcry_md_map_name (algoid); + if (!*algo) + { + log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); + return gpg_error (GPG_ERR_DIGEST_ALGO); + } + + err = gcry_md_open (md, *algo, 0); + if (err) + { + log_error (_("gcry_md_open for algorithm %d failed: %s\n"), + *algo, gcry_strerror (err)); + return err; + } + if (DBG_HASHING) + gcry_md_debug (*md, "hash.cert"); + + ksba_crl_set_hash_function (crl, HASH_FNC, *md); + return 0; +} + + +/* Finish a hash context and verify the signature. This function + should return 0 on a good signature, GPG_ERR_BAD_SIGNATURE if the + signature does not verify or any other error code. CRL is the CRL + object we are working on, MD the hash context and ISSUER_CERT the + certificate of the CRL issuer. This function closes MD. */ +static gpg_error_t +finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, + ksba_cert_t issuer_cert) +{ + gpg_error_t err; + ksba_sexp_t sigval = NULL, pubkey = NULL; + const char *s; + char algoname[50]; + size_t n; + gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL; + unsigned int i; + + /* This also stops debugging on the MD. */ + gcry_md_final (md); + + /* Get and convert the signature value. */ + sigval = ksba_crl_get_sig_val (crl); + n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); + if (!n) + { + log_error (_("got an invalid S-expression from libksba\n")); + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + err = gcry_sexp_sscan (&s_sig, NULL, sigval, n); + if (err) + { + log_error (_("converting S-expression failed: %s\n"), + gcry_strerror (err)); + goto leave; + } + + /* Get and convert the public key for the issuer certificate. */ + if (DBG_X509) + dump_cert ("crl_issuer_cert", issuer_cert); + pubkey = ksba_cert_get_public_key (issuer_cert); + n = gcry_sexp_canon_len (pubkey, 0, NULL, NULL); + if (!n) + { + log_error (_("got an invalid S-expression from libksba\n")); + err = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + err = gcry_sexp_sscan (&s_pkey, NULL, pubkey, n); + if (err) + { + log_error (_("converting S-expression failed: %s\n"), + gcry_strerror (err)); + goto leave; + } + + /* Create an S-expression with the actual hash value. */ + s = gcry_md_algo_name (algo); + for (i = 0; *s && i < sizeof(algoname) - 1; s++, i++) + algoname[i] = ascii_tolower (*s); + algoname[i] = 0; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + algoname, + gcry_md_get_algo_dlen (algo), gcry_md_read (md, algo)); + if (err) + { + log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); + goto leave; + } + + /* Pass this on to the signature verification. */ + err = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (DBG_X509) + log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); + + leave: + xfree (sigval); + xfree (pubkey); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_pkey); + gcry_md_close (md); + + return err; +} + + +/* Call this to match a start_sig_check that can not be completed + normally. */ +static void +abort_sig_check (ksba_crl_t crl, gcry_md_hd_t md) +{ + (void)crl; + gcry_md_close (md); +} + + +/* Workhorse of the CRL loading machinery. The CRL is read using the + CRL object and stored in the data base file DB with the name FNAME + (only used for printing error messages). That DB should be a + temporary one and not the actual one. If the function fails the + caller should delete this temporary database file. CTRL is + required to retrieve certificates using the general dirmngr + callback service. R_CRLISSUER returns an allocated string with the + crl-issuer DN, THIS_UPDATE and NEXT_UPDATE are filled with the + corresponding data from the CRL. Note that these values might get + set even if the CRL processing fails at a later step; thus the + caller should free *R_ISSUER even if the function returns with an + error. R_TRUST_ANCHOR is set on exit to NULL or a string with the + hexified fingerprint of the root certificate, if checking this + certificate for trustiness is required. +*/ +static int +crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, + struct cdb_make *cdb, const char *fname, + char **r_crlissuer, + ksba_isotime_t thisupdate, ksba_isotime_t nextupdate, + char **r_trust_anchor) +{ + gpg_error_t err; + ksba_stop_reason_t stopreason; + ksba_cert_t crlissuer_cert = NULL; + gcry_md_hd_t md = NULL; + int algo = 0; + size_t n; + + (void)fname; + + *r_crlissuer = NULL; + *thisupdate = *nextupdate = 0; + *r_trust_anchor = NULL; + + /* Start of the KSBA parser loop. */ + do + { + err = ksba_crl_parse (crl, &stopreason); + if (err) + { + log_error (_("ksba_crl_parse failed: %s\n"), gpg_strerror (err) ); + goto failure; + } + + switch (stopreason) + { + case KSBA_SR_BEGIN_ITEMS: + { + err = start_sig_check (crl, &md, &algo); + if (err) + goto failure; + + err = ksba_crl_get_update_times (crl, thisupdate, nextupdate); + if (err) + { + log_error (_("error getting update times of CRL: %s\n"), + gpg_strerror (err)); + err = gpg_error (GPG_ERR_INV_CRL); + goto failure; + } + + if (opt.verbose || !*nextupdate) + log_info (_("update times of this CRL: this=%s next=%s\n"), + thisupdate, nextupdate); + if (!*nextupdate) + { + log_info (_("nextUpdate not given; " + "assuming a validity period of one day\n")); + gnupg_copy_time (nextupdate, thisupdate); + add_seconds_to_isotime (nextupdate, 86400); + } + } + break; + + case KSBA_SR_GOT_ITEM: + { + ksba_sexp_t serial; + const unsigned char *p; + ksba_isotime_t rdate; + ksba_crl_reason_t reason; + int rc; + unsigned char record[1+15]; + + err = ksba_crl_get_item (crl, &serial, rdate, &reason); + if (err) + { + log_error (_("error getting CRL item: %s\n"), + gpg_strerror (err)); + err = gpg_error (GPG_ERR_INV_CRL); + ksba_free (serial); + goto failure; + } + p = serial_to_buffer (serial, &n); + if (!p) + BUG (); + record[0] = (reason & 0xff); + memcpy (record+1, rdate, 15); + rc = cdb_make_add (cdb, p, n, record, 1+15); + if (rc) + { + err = gpg_error_from_errno (errno); + log_error (_("error inserting item into " + "temporary cache file: %s\n"), + strerror (errno)); + goto failure; + } + + ksba_free (serial); + } + break; + + case KSBA_SR_END_ITEMS: + break; + + case KSBA_SR_READY: + { + char *crlissuer; + ksba_name_t authid; + ksba_sexp_t authidsn; + ksba_sexp_t keyid; + + /* We need to look for the issuer only after having read + all items. The issuer itselfs comes before the items + but the optional authorityKeyIdentifier comes after the + items. */ + err = ksba_crl_get_issuer (crl, &crlissuer); + if( err ) + { + log_error (_("no CRL issuer found in CRL: %s\n"), + gpg_strerror (err) ); + err = gpg_error (GPG_ERR_INV_CRL); + goto failure; + } + /* Note: This should be released by ksba_free, not xfree. + May need a memory reallocation dance. */ + *r_crlissuer = crlissuer; /* (Do it here so we don't need + to free it later) */ + + if (!ksba_crl_get_auth_key_id (crl, &keyid, &authid, &authidsn)) + { + const char *s; + + if (opt.verbose) + log_info (_("locating CRL issuer certificate by " + "authorityKeyIdentifier\n")); + + s = ksba_name_enum (authid, 0); + if (s && *authidsn) + crlissuer_cert = find_cert_bysn (ctrl, s, authidsn); + if (!crlissuer_cert && keyid) + crlissuer_cert = find_cert_bysubject (ctrl, + crlissuer, keyid); + + if (!crlissuer_cert) + { + log_info ("CRL issuer certificate "); + if (keyid) + { + log_printf ("{"); + dump_serial (keyid); + log_printf ("} "); + } + if (authidsn) + { + log_printf ("(#"); + dump_serial (authidsn); + log_printf ("/"); + dump_string (s); + log_printf (") "); + } + log_printf ("not found\n"); + } + ksba_name_release (authid); + xfree (authidsn); + xfree (keyid); + } + else + crlissuer_cert = find_cert_bysubject (ctrl, crlissuer, NULL); + err = 0; + if (!crlissuer_cert) + { + err = gpg_error (GPG_ERR_MISSING_CERT); + goto failure; + } + + err = finish_sig_check (crl, md, algo, crlissuer_cert); + if (err) + { + log_error (_("CRL signature verification failed: %s\n"), + gpg_strerror (err)); + goto failure; + } + md = NULL; + + err = validate_cert_chain (ctrl, crlissuer_cert, NULL, + VALIDATE_MODE_CRL_RECURSIVE, + r_trust_anchor); + if (err) + { + log_error (_("error checking validity of CRL " + "issuer certificate: %s\n"), + gpg_strerror (err)); + goto failure; + } + + } + break; + + default: + log_debug ("crl_parse_insert: unknown stop reason\n"); + err = gpg_error (GPG_ERR_BUG); + goto failure; + } + } + while (stopreason != KSBA_SR_READY); + assert (!err); + + + failure: + if (md) + abort_sig_check (crl, md); + ksba_cert_release (crlissuer_cert); + return err; +} + + + +/* Return the crlNumber extension as an allocated hex string or NULL + if there is none. */ +static char * +get_crl_number (ksba_crl_t crl) +{ + gpg_error_t err; + ksba_sexp_t number; + char *string; + + err = ksba_crl_get_crl_number (crl, &number); + if (err) + return NULL; + string = serial_hex (number); + ksba_free (number); + return string; +} + + +/* Return the authorityKeyIdentifier or NULL if it is not available. + The issuer name may consists of several parts - they are delimted by + 0x01. */ +static char * +get_auth_key_id (ksba_crl_t crl, char **serialno) +{ + gpg_error_t err; + ksba_name_t name; + ksba_sexp_t sn; + int idx; + const char *s; + char *string; + size_t length; + + *serialno = NULL; + err = ksba_crl_get_auth_key_id (crl, NULL, &name, &sn); + if (err) + return NULL; + *serialno = serial_hex (sn); + ksba_free (sn); + + if (!name) + return xstrdup (""); + + length = 0; + for (idx=0; (s = ksba_name_enum (name, idx)); idx++) + { + char *p = ksba_name_get_uri (name, idx); + length += strlen (p?p:s) + 1; + xfree (p); + } + string = xtrymalloc (length+1); + if (string) + { + *string = 0; + for (idx=0; (s = ksba_name_enum (name, idx)); idx++) + { + char *p = ksba_name_get_uri (name, idx); + if (*string) + strcat (string, "\x01"); + strcat (string, p?p:s); + xfree (p); + } + } + ksba_name_release (name); + return string; +} + + + +/* Insert the CRL retrieved using URL into the cache specified by + CACHE. The CRL itself will be read from the stream FP and is + expected in binary format. + + Called by: + crl_cache_load + cmd_loadcrl + --load-crl + crl_cache_reload_crl + cmd_isvalid + cmd_checkcrl + cmd_loadcrl + --fetch-crl + + */ +gpg_error_t +crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) +{ + crl_cache_t cache = get_current_cache (); + gpg_error_t err, err2; + ksba_crl_t crl; + char *fname = NULL; + char *newfname = NULL; + struct cdb_make cdb; + int fd_cdb = -1; + char *issuer = NULL; + char *issuer_hash = NULL; + ksba_isotime_t thisupdate, nextupdate; + crl_cache_entry_t entry = NULL; + crl_cache_entry_t e; + gnupg_isotime_t current_time; + char *checksum = NULL; + int invalidate_crl = 0; + int idx; + const char *oid; + int critical; + char *trust_anchor = NULL; + + /* FIXME: We should acquire a mutex for the URL, so that we don't + simultaneously enter the same CRL twice. However this needs to be + interweaved with the checking function.*/ + + err2 = 0; + + err = ksba_crl_new (&crl); + if (err) + { + log_error (_("ksba_crl_new failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + err = ksba_crl_set_reader (crl, reader); + if ( err ) + { + log_error (_("ksba_crl_set_reader failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Create a temporary cache file to load the CRL into. */ + { + char *tmpfname, *p; + const char *nodename; +#ifndef HAVE_W32_SYSTEM + struct utsname utsbuf; +#endif + +#ifdef HAVE_W32_SYSTEM + nodename = "unknown"; +#else + if (uname (&utsbuf)) + nodename = "unknown"; + else + nodename = utsbuf.nodename; +#endif + + gpgrt_asprintf (&tmpfname, "crl-tmp-%s-%u-%p.db.tmp", + nodename, (unsigned int)getpid (), &tmpfname); + if (!tmpfname) + { + err = gpg_error_from_syserror (); + goto leave; + } + for (p=tmpfname; *p; p++) + if (*p == '/') + *p = '.'; + fname = make_filename (opt.homedir_cache, DBDIR_D, tmpfname, NULL); + xfree (tmpfname); + if (!gnupg_remove (fname)) + log_info (_("removed stale temporary cache file '%s'\n"), fname); + else if (errno != ENOENT) + { + err = gpg_error_from_syserror (); + log_error (_("problem removing stale temporary cache file '%s': %s\n"), + fname, gpg_strerror (err)); + goto leave; + } + } + + fd_cdb = open (fname, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd_cdb == -1) + { + err = gpg_error_from_errno (errno); + log_error (_("error creating temporary cache file '%s': %s\n"), + fname, strerror (errno)); + goto leave; + } + cdb_make_start(&cdb, fd_cdb); + + err = crl_parse_insert (ctrl, crl, &cdb, fname, + &issuer, thisupdate, nextupdate, &trust_anchor); + if (err) + { + log_error (_("crl_parse_insert failed: %s\n"), gpg_strerror (err)); + /* Error in cleanup ignored. */ + cdb_make_finish (&cdb); + goto leave; + } + + /* Finish the database. */ + if (cdb_make_finish (&cdb)) + { + err = gpg_error_from_errno (errno); + log_error (_("error finishing temporary cache file '%s': %s\n"), + fname, strerror (errno)); + goto leave; + } + if (close (fd_cdb)) + { + err = gpg_error_from_errno (errno); + log_error (_("error closing temporary cache file '%s': %s\n"), + fname, strerror (errno)); + goto leave; + } + fd_cdb = -1; + + + /* Create a checksum. */ + { + unsigned char md5buf[16]; + + if (hash_dbfile (fname, md5buf)) + { + err = gpg_error (GPG_ERR_CHECKSUM); + goto leave; + } + checksum = hexify_data (md5buf, 16, 0); + } + + + /* Check whether that new CRL is still not expired. */ + gnupg_get_isotime (current_time); + if (strcmp (nextupdate, current_time) < 0 ) + { + if (opt.force) + log_info (_("WARNING: new CRL still too old; it expired on %s " + "- loading anyway\n"), nextupdate); + else + { + log_error (_("new CRL still too old; it expired on %s\n"), + nextupdate); + if (!err2) + err2 = gpg_error (GPG_ERR_CRL_TOO_OLD); + invalidate_crl |= 1; + } + } + + /* Check for unknown critical extensions. */ + for (idx=0; !(err=ksba_crl_get_extension (crl, idx, &oid, &critical, + NULL, NULL)); idx++) + { + if (!critical + || !strcmp (oid, oidstr_authorityKeyIdentifier) + || !strcmp (oid, oidstr_crlNumber) ) + continue; + log_error (_("unknown critical CRL extension %s\n"), oid); + if (!err2) + err2 = gpg_error (GPG_ERR_INV_CRL); + invalidate_crl |= 2; + } + if (gpg_err_code (err) == GPG_ERR_EOF + || gpg_err_code (err) == GPG_ERR_NO_DATA ) + err = 0; + if (err) + { + log_error (_("error reading CRL extensions: %s\n"), gpg_strerror (err)); + err = gpg_error (GPG_ERR_INV_CRL); + } + + + /* Create an hex encoded SHA-1 hash of the issuer DN to be + used as the key for the cache. */ + issuer_hash = hashify_data (issuer, strlen (issuer)); + + /* Create an ENTRY. */ + entry = xtrycalloc (1, sizeof *entry); + if (!entry) + { + err = gpg_error_from_syserror (); + goto leave; + } + entry->release_ptr = xtrymalloc (strlen (issuer_hash) + 1 + + strlen (issuer) + 1 + + strlen (url) + 1 + + strlen (checksum) + 1); + if (!entry->release_ptr) + { + err = gpg_error_from_syserror (); + xfree (entry); + entry = NULL; + goto leave; + } + entry->issuer_hash = entry->release_ptr; + entry->issuer = stpcpy (entry->issuer_hash, issuer_hash) + 1; + entry->url = stpcpy (entry->issuer, issuer) + 1; + entry->dbfile_hash = stpcpy (entry->url, url) + 1; + strcpy (entry->dbfile_hash, checksum); + gnupg_copy_time (entry->this_update, thisupdate); + gnupg_copy_time (entry->next_update, nextupdate); + gnupg_copy_time (entry->last_refresh, current_time); + entry->crl_number = get_crl_number (crl); + entry->authority_issuer = get_auth_key_id (crl, &entry->authority_serialno); + entry->invalid = invalidate_crl; + entry->user_trust_req = !!trust_anchor; + entry->check_trust_anchor = trust_anchor; + trust_anchor = NULL; + + /* Check whether we already have an entry for this issuer and mark + it as deleted. We better use a loop, just in case duplicates got + somehow into the list. */ + for (e = cache->entries; (e=find_entry (e, entry->issuer_hash)); e = e->next) + e->deleted = 1; + + /* Rename the temporary DB to the real name. */ + newfname = make_db_file_name (entry->issuer_hash); + if (opt.verbose) + log_info (_("creating cache file '%s'\n"), newfname); + + /* Just in case close unused matching files. Actually we need this + only under Windows but saving file descriptors is never bad. */ + { + int any; + do + { + any = 0; + for (e = cache->entries; e; e = e->next) + if (!e->cdb_use_count && e->cdb + && !strcmp (e->issuer_hash, entry->issuer_hash)) + { + int fd = cdb_fileno (e->cdb); + cdb_free (e->cdb); + xfree (e->cdb); + e->cdb = NULL; + if (close (fd)) + log_error (_("error closing cache file: %s\n"), + strerror(errno)); + any = 1; + break; + } + } + while (any); + } +#ifdef HAVE_W32_SYSTEM + gnupg_remove (newfname); +#endif + if (rename (fname, newfname)) + { + err = gpg_error_from_syserror (); + log_error (_("problem renaming '%s' to '%s': %s\n"), + fname, newfname, gpg_strerror (err)); + goto leave; + } + xfree (fname); fname = NULL; /*(let the cleanup code not try to remove it)*/ + + /* Link the new entry in. */ + entry->next = cache->entries; + cache->entries = entry; + entry = NULL; + + err = update_dir (cache); + if (err) + { + log_error (_("updating the DIR file failed - " + "cache entry will get lost with the next program start\n")); + err = 0; /* Keep on running. */ + } + + + leave: + release_one_cache_entry (entry); + if (fd_cdb != -1) + close (fd_cdb); + if (fname) + { + gnupg_remove (fname); + xfree (fname); + } + xfree (newfname); + ksba_crl_release (crl); + xfree (issuer); + xfree (issuer_hash); + xfree (checksum); + xfree (trust_anchor); + return err ? err : err2; +} + + +/* Print one cached entry E in a human readable format to stream + FP. Return 0 on success. */ +static gpg_error_t +list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, estream_t fp) +{ + struct cdb_find cdbfp; + struct cdb *cdb; + int rc; + int warn = 0; + const unsigned char *s; + + es_fputs ("--------------------------------------------------------\n", fp ); + es_fprintf (fp, _("Begin CRL dump (retrieved via %s)\n"), e->url ); + es_fprintf (fp, " Issuer:\t%s\n", e->issuer ); + es_fprintf (fp, " Issuer Hash:\t%s\n", e->issuer_hash ); + es_fprintf (fp, " This Update:\t%s\n", e->this_update ); + es_fprintf (fp, " Next Update:\t%s\n", e->next_update ); + es_fprintf (fp, " CRL Number :\t%s\n", e->crl_number? e->crl_number: "none"); + es_fprintf (fp, " AuthKeyId :\t%s\n", + e->authority_serialno? e->authority_serialno:"none"); + if (e->authority_serialno && e->authority_issuer) + { + es_fputs (" \t", fp); + for (s=e->authority_issuer; *s; s++) + if (*s == '\x01') + es_fputs ("\n \t", fp); + else + es_putc (*s, fp); + es_putc ('\n', fp); + } + es_fprintf (fp, " Trust Check:\t%s\n", + !e->user_trust_req? "[system]" : + e->check_trust_anchor? e->check_trust_anchor:"[missing]"); + + if ((e->invalid & 1)) + es_fprintf (fp, _(" ERROR: The CRL will not be used " + "because it was still too old after an update!\n")); + if ((e->invalid & 2)) + es_fprintf (fp, _(" ERROR: The CRL will not be used " + "due to an unknown critical extension!\n")); + if ((e->invalid & ~3)) + es_fprintf (fp, _(" ERROR: The CRL will not be used\n")); + + cdb = lock_db_file (cache, e); + if (!cdb) + return gpg_error (GPG_ERR_GENERAL); + + if (!e->dbfile_checked) + es_fprintf (fp, _(" ERROR: This cached CRL may have been tampered with!\n")); + + es_putc ('\n', fp); + + rc = cdb_findinit (&cdbfp, cdb, NULL, 0); + while (!rc && (rc=cdb_findnext (&cdbfp)) > 0 ) + { + unsigned char keyrecord[256]; + unsigned char record[16]; + int reason; + int any = 0; + cdbi_t n; + cdbi_t i; + + rc = 0; + n = cdb_datalen (cdb); + if (n != 16) + { + log_error (_(" WARNING: invalid cache record length\n")); + warn = 1; + continue; + } + + if (cdb_read (cdb, record, n, cdb_datapos (cdb))) + { + log_error (_("problem reading cache record: %s\n"), + strerror (errno)); + warn = 1; + continue; + } + + n = cdb_keylen (cdb); + if (n > sizeof keyrecord) + n = sizeof keyrecord; + if (cdb_read (cdb, keyrecord, n, cdb_keypos (cdb))) + { + log_error (_("problem reading cache key: %s\n"), strerror (errno)); + warn = 1; + continue; + } + + reason = *record; + es_fputs (" ", fp); + for (i = 0; i < n; i++) + es_fprintf (fp, "%02X", keyrecord[i]); + es_fputs (":\t reasons( ", fp); + + if (reason & KSBA_CRLREASON_UNSPECIFIED) + es_fputs( "unspecified ", fp ), any = 1; + if (reason & KSBA_CRLREASON_KEY_COMPROMISE ) + es_fputs( "key_compromise ", fp ), any = 1; + if (reason & KSBA_CRLREASON_CA_COMPROMISE ) + es_fputs( "ca_compromise ", fp ), any = 1; + if (reason & KSBA_CRLREASON_AFFILIATION_CHANGED ) + es_fputs( "affiliation_changed ", fp ), any = 1; + if (reason & KSBA_CRLREASON_SUPERSEDED ) + es_fputs( "superseded", fp ), any = 1; + if (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION ) + es_fputs( "cessation_of_operation", fp ), any = 1; + if (reason & KSBA_CRLREASON_CERTIFICATE_HOLD ) + es_fputs( "certificate_hold", fp ), any = 1; + if (reason && !any) + es_fputs( "other", fp ); + + es_fprintf (fp, ") rdate: %.15s\n", record+1); + } + if (rc) + log_error (_("error reading cache entry from db: %s\n"), strerror (rc)); + + unlock_db_file (cache, e); + es_fprintf (fp, _("End CRL dump\n") ); + es_putc ('\n', fp); + + return (rc||warn)? gpg_error (GPG_ERR_GENERAL) : 0; +} + + +/* Print the contents of the CRL CACHE in a human readable format to + stream FP. */ +gpg_error_t +crl_cache_list (estream_t fp) +{ + crl_cache_t cache = get_current_cache (); + crl_cache_entry_t entry; + gpg_error_t err = 0; + + for (entry = cache->entries; + entry && !entry->deleted && !err; + entry = entry->next ) + err = list_one_crl_entry (cache, entry, fp); + + return err; +} + + +/* Load the CRL containing the file named FILENAME into our CRL cache. */ +gpg_error_t +crl_cache_load (ctrl_t ctrl, const char *filename) +{ + gpg_error_t err; + estream_t fp; + ksba_reader_t reader; + + fp = es_fopen (filename, "rb"); + if (!fp) + { + err = gpg_error_from_errno (errno); + log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); + return err; + } + + err = create_estream_ksba_reader (&reader, fp); + if (!err) + { + err = crl_cache_insert (ctrl, filename, reader); + ksba_reader_release (reader); + } + es_fclose (fp); + return err; +} + + +/* Locate the corresponding CRL for the certificate CERT, read and + verify the CRL and store it in the cache. */ +gpg_error_t +crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert) +{ + gpg_error_t err; + ksba_reader_t reader = NULL; + char *issuer = NULL; + ksba_name_t distpoint = NULL; + ksba_name_t issuername = NULL; + char *distpoint_uri = NULL; + char *issuername_uri = NULL; + int any_dist_point = 0; + int seq; + + /* Loop over all distribution points, get the CRLs and put them into + the cache. */ + if (opt.verbose) + log_info ("checking distribution points\n"); + seq = 0; + while ( !(err = ksba_cert_get_crl_dist_point (cert, seq++, + &distpoint, + &issuername, NULL ))) + { + int name_seq; + gpg_error_t last_err = 0; + + if (!distpoint && !issuername) + { + if (opt.verbose) + log_info ("no issuer name and no distribution point\n"); + break; /* Not allowed; i.e. an invalid certificate. We give + up here and hope that the default method returns a + suitable CRL. */ + } + + xfree (issuername_uri); issuername_uri = NULL; + + /* Get the URIs. We do this in a loop to iterate over all names + in the crlDP. */ + for (name_seq=0; ksba_name_enum (distpoint, name_seq); name_seq++) + { + xfree (distpoint_uri); distpoint_uri = NULL; + distpoint_uri = ksba_name_get_uri (distpoint, name_seq); + if (!distpoint_uri) + continue; + + if (!strncmp (distpoint_uri, "ldap:", 5) + || !strncmp (distpoint_uri, "ldaps:", 6)) + { + if (opt.ignore_ldap_dp) + continue; + } + else if (!strncmp (distpoint_uri, "http:", 5) + || !strncmp (distpoint_uri, "https:", 6)) + { + if (opt.ignore_http_dp) + continue; + } + else + continue; /* Skip unknown schemes. */ + + any_dist_point = 1; + + if (opt.verbose) + log_info ("fetching CRL from '%s'\n", distpoint_uri); + err = crl_fetch (ctrl, distpoint_uri, &reader); + if (err) + { + log_error (_("crl_fetch via DP failed: %s\n"), + gpg_strerror (err)); + last_err = err; + continue; /* with the next name. */ + } + + if (opt.verbose) + log_info ("inserting CRL (reader %p)\n", reader); + err = crl_cache_insert (ctrl, distpoint_uri, reader); + if (err) + { + log_error (_("crl_cache_insert via DP failed: %s\n"), + gpg_strerror (err)); + last_err = err; + continue; /* with the next name. */ + } + last_err = 0; + break; /* Ready. */ + } + if (last_err) + { + err = last_err; + goto leave; + } + + ksba_name_release (distpoint); distpoint = NULL; + + /* We don't do anything with issuername_uri yet but we keep the + code for documentation. */ + issuername_uri = ksba_name_get_uri (issuername, 0); + ksba_name_release (issuername); issuername = NULL; + + /* Close the reader. */ + crl_close_reader (reader); + reader = NULL; + } + if (gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + + /* If we did not found any distpoint, try something reasonable. */ + if (!any_dist_point ) + { + if (opt.verbose) + log_info ("no distribution point - trying issuer name\n"); + + crl_close_reader (reader); + reader = NULL; + + issuer = ksba_cert_get_issuer (cert, 0); + if (!issuer) + { + log_error ("oops: issuer missing in certificate\n"); + err = gpg_error (GPG_ERR_INV_CERT_OBJ); + goto leave; + } + + if (opt.verbose) + log_info ("fetching CRL from default location\n"); + err = crl_fetch_default (ctrl, issuer, &reader); + if (err) + { + log_error ("crl_fetch via issuer failed: %s\n", + gpg_strerror (err)); + goto leave; + } + + if (opt.verbose) + log_info ("inserting CRL (reader %p)\n", reader); + err = crl_cache_insert (ctrl, "default location(s)", reader); + if (err) + { + log_error (_("crl_cache_insert via issuer failed: %s\n"), + gpg_strerror (err)); + goto leave; + } + } + + leave: + crl_close_reader (reader); + xfree (distpoint_uri); + xfree (issuername_uri); + ksba_name_release (distpoint); + ksba_name_release (issuername); + ksba_free (issuer); + return err; +} diff --git a/dirmngr/crlcache.h b/dirmngr/crlcache.h new file mode 100644 index 0000000..0e60def --- /dev/null +++ b/dirmngr/crlcache.h @@ -0,0 +1,70 @@ +/* crlcache.h - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#ifndef CRLCACHE_H +#define CRLCACHE_H + + +typedef enum + { + CRL_CACHE_VALID = 0, + CRL_CACHE_INVALID, + CRL_CACHE_DONTKNOW, + CRL_CACHE_CANTUSE + } +crl_cache_result_t; + +typedef enum foo + { + CRL_SIG_OK = 0, + CRL_SIG_NOT_OK, + CRL_TOO_OLD, + CRL_SIG_ERROR, + CRL_GENERAL_ERROR + } +crl_sig_result_t; + +struct crl_cache_entry_s; +typedef struct crl_cache_entry_s *crl_cache_entry_t; + + +void crl_cache_init (void); +void crl_cache_deinit (void); +int crl_cache_flush(void); + +crl_cache_result_t crl_cache_isvalid (ctrl_t ctrl, + const char *issuer_hash, + const char *cert_id, + int force_refresh); + +gpg_error_t crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert, + int force_refresh); + +gpg_error_t crl_cache_insert (ctrl_t ctrl, const char *url, + ksba_reader_t reader); + +gpg_error_t crl_cache_list (estream_t fp); + +gpg_error_t crl_cache_load (ctrl_t ctrl, const char *filename); + +gpg_error_t crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert); + + +#endif /* CRLCACHE_H */ diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c new file mode 100644 index 0000000..8fe6e0b --- /dev/null +++ b/dirmngr/crlfetch.c @@ -0,0 +1,565 @@ +/* crlfetch.c - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2003, 2004, 2005, 2006, 2007 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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, see . + */ + +#include + +#include +#include +#include + +#include "crlfetch.h" +#include "dirmngr.h" +#include "misc.h" +#include "http.h" + +#if USE_LDAP +# include "ldap-wrapper.h" +#endif + +/* For detecting armored CRLs received via HTTP (yes, such CRLS really + exits, e.g. http://grid.fzk.de/ca/gridka-crl.pem at least in June + 2008) we need a context in the reader callback. */ +struct reader_cb_context_s +{ + estream_t fp; /* The stream used with the ksba reader. */ + int checked:1; /* PEM/binary detection ahs been done. */ + int is_pem:1; /* The file stream is PEM encoded. */ + struct b64state b64state; /* The state used for Base64 decoding. */ +}; + + +/* We need to associate a reader object with the reader callback + context. This table is used for it. */ +struct file_reader_map_s +{ + ksba_reader_t reader; + struct reader_cb_context_s *cb_ctx; +}; +#define MAX_FILE_READER 50 +static struct file_reader_map_s file_reader_map[MAX_FILE_READER]; + +/* Associate FP with READER. If the table is full wait until another + thread has removed an entry. */ +static void +register_file_reader (ksba_reader_t reader, struct reader_cb_context_s *cb_ctx) +{ + int i; + + for (;;) + { + for (i=0; i < MAX_FILE_READER; i++) + if (!file_reader_map[i].reader) + { + file_reader_map[i].reader = reader; + file_reader_map[i].cb_ctx = cb_ctx; + return; + } + log_info (_("reader to file mapping table full - waiting\n")); + npth_sleep (2); + } +} + +/* Scan the table for an entry matching READER, remove that entry and + return the associated file pointer. */ +static struct reader_cb_context_s * +get_file_reader (ksba_reader_t reader) +{ + struct reader_cb_context_s *cb_ctx = NULL; + int i; + + for (i=0; i < MAX_FILE_READER; i++) + if (file_reader_map[i].reader == reader) + { + cb_ctx = file_reader_map[i].cb_ctx; + file_reader_map[i].reader = NULL; + file_reader_map[i].cb_ctx = NULL; + break; + } + return cb_ctx; +} + + + +static int +my_es_read (void *opaque, char *buffer, size_t nbytes, size_t *nread) +{ + struct reader_cb_context_s *cb_ctx = opaque; + int result; + + result = es_read (cb_ctx->fp, buffer, nbytes, nread); + if (result) + return result; + /* Fixme we should check whether the semantics of es_read are okay + and well defined. I have some doubts. */ + if (nbytes && !*nread && es_feof (cb_ctx->fp)) + return gpg_error (GPG_ERR_EOF); + if (!nread && es_ferror (cb_ctx->fp)) + return gpg_error (GPG_ERR_EIO); + + if (!cb_ctx->checked && *nread) + { + int c = *(unsigned char *)buffer; + + cb_ctx->checked = 1; + if ( ((c & 0xc0) >> 6) == 0 /* class: universal */ + && (c & 0x1f) == 16 /* sequence */ + && (c & 0x20) /* is constructed */ ) + ; /* Binary data. */ + else + { + cb_ctx->is_pem = 1; + b64dec_start (&cb_ctx->b64state, ""); + } + } + if (cb_ctx->is_pem && *nread) + { + size_t nread2; + + if (b64dec_proc (&cb_ctx->b64state, buffer, *nread, &nread2)) + { + /* EOF from decoder. */ + *nread = 0; + result = gpg_error (GPG_ERR_EOF); + } + else + *nread = nread2; + } + + return result; +} + + +/* Fetch CRL from URL and return the entire CRL using new ksba reader + object in READER. Note that this reader object should be closed + only using ldap_close_reader. */ +gpg_error_t +crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) +{ + gpg_error_t err; + parsed_uri_t uri; + char *free_this = NULL; + int redirects_left = 2; /* We allow for 2 redirect levels. */ + + *reader = NULL; + + if (!url) + return gpg_error (GPG_ERR_INV_ARG); + + once_more: + err = http_parse_uri (&uri, url, 0); + http_release_parsed_uri (uri); + if (err && !strncmp (url, "https:", 6)) + { + /* Our HTTP code does not support TLS, thus we can't use this + scheme and it is frankly not useful for CRL retrieval anyway. + We resort to using http, assuming that the server also + provides plain http access. */ + free_this = xtrymalloc (strlen (url) + 1); + if (free_this) + { + strcpy (stpcpy (free_this,"http:"), url+6); + err = http_parse_uri (&uri, free_this, 0); + http_release_parsed_uri (uri); + if (!err) + { + log_info (_("using \"http\" instead of \"https\"\n")); + url = free_this; + } + } + } + if (!err) /* Yes, our HTTP code groks that. */ + { + http_t hd; + + if (opt.disable_http) + { + log_error (_("CRL access not possible due to disabled %s\n"), + "HTTP"); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + } + else + err = http_open_document (&hd, url, NULL, + ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) + |(DBG_LOOKUP? HTTP_FLAG_LOG_RESP:0) + |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + ctrl->http_proxy, NULL, NULL, NULL); + + switch ( err? 99999 : http_get_status_code (hd) ) + { + case 200: + { + estream_t fp = http_get_read_ptr (hd); + struct reader_cb_context_s *cb_ctx; + + cb_ctx = xtrycalloc (1, sizeof *cb_ctx); + if (!cb_ctx) + err = gpg_error_from_syserror (); + if (!err) + err = ksba_reader_new (reader); + if (!err) + { + cb_ctx->fp = fp; + err = ksba_reader_set_cb (*reader, &my_es_read, cb_ctx); + } + if (err) + { + log_error (_("error initializing reader object: %s\n"), + gpg_strerror (err)); + ksba_reader_release (*reader); + *reader = NULL; + http_close (hd, 0); + } + else + { + /* The ksba reader misses a user pointer thus we need + to come up with our own way of associating a file + pointer (or well the callback context) with the + reader. It is only required when closing the + reader thus there is no performance issue doing it + this way. FIXME: We now have a close notification + which might be used here. */ + register_file_reader (*reader, cb_ctx); + http_close (hd, 1); + } + } + break; + + case 301: /* Redirection (perm.). */ + case 302: /* Redirection (temp.). */ + { + const char *s = http_get_header (hd, "Location"); + + log_info (_("URL '%s' redirected to '%s' (%u)\n"), + url, s?s:"[none]", http_get_status_code (hd)); + if (s && *s && redirects_left-- ) + { + xfree (free_this); url = NULL; + free_this = xtrystrdup (s); + if (!free_this) + err = gpg_error_from_errno (errno); + else + { + url = free_this; + http_close (hd, 0); + /* Note, that our implementation of redirection + actually handles a redirect to LDAP. */ + goto once_more; + } + } + else + err = gpg_error (GPG_ERR_NO_DATA); + log_error (_("too many redirections\n")); /* Or no "Location". */ + http_close (hd, 0); + } + break; + + case 99999: /* Made up status code for error reporting. */ + log_error (_("error retrieving '%s': %s\n"), + url, gpg_strerror (err)); + break; + + default: + log_error (_("error retrieving '%s': http status %u\n"), + url, http_get_status_code (hd)); + err = gpg_error (GPG_ERR_NO_DATA); + http_close (hd, 0); + } + } + else /* Let the LDAP code try other schemes. */ + { + if (opt.disable_ldap) + { + log_error (_("CRL access not possible due to disabled %s\n"), + "LDAP"); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + } + else if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("CRL access not possible due to Tor mode\n")); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + } + else + { +# if USE_LDAP + err = url_fetch_ldap (ctrl, url, NULL, 0, reader); +# else /*!USE_LDAP*/ + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); +# endif /*!USE_LDAP*/ + } + } + + xfree (free_this); + return err; +} + + +/* Fetch CRL for ISSUER using a default server. Return the entire CRL + as a newly opened stream returned in R_FP. */ +gpg_error_t +crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader) +{ + if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("CRL access not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + if (opt.disable_ldap) + { + log_error (_("CRL access not possible due to disabled %s\n"), + "LDAP"); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + +#if USE_LDAP + return attr_fetch_ldap (ctrl, issuer, "certificateRevocationList", + reader); +#else + (void)ctrl; + (void)issuer; + (void)reader; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + + +/* Fetch a CA certificate for DN using the default server. This + function only initiates the fetch; fetch_next_cert must be used to + actually read the certificate; end_cert_fetch to end the + operation. */ +gpg_error_t +ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn) +{ + if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("CRL access not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + if (opt.disable_ldap) + { + log_error (_("CRL access not possible due to disabled %s\n"), + "LDAP"); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } +#if USE_LDAP + return start_default_fetch_ldap (ctrl, context, dn, "cACertificate"); +#else + (void)ctrl; + (void)context; + (void)dn; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + + +gpg_error_t +start_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, + strlist_t patterns, const ldap_server_t server) +{ + if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("CRL access not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + if (opt.disable_ldap) + { + log_error (_("certificate search not possible due to disabled %s\n"), + "LDAP"); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } +#if USE_LDAP + return start_cert_fetch_ldap (ctrl, context, patterns, server); +#else + (void)ctrl; + (void)context; + (void)patterns; + (void)server; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + + +gpg_error_t +fetch_next_cert (cert_fetch_context_t context, + unsigned char **value, size_t * valuelen) +{ +#if USE_LDAP + return fetch_next_cert_ldap (context, value, valuelen); +#else + (void)context; + (void)value; + (void)valuelen; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + + +/* Fetch the next data from CONTEXT, assuming it is a certificate and return + it as a cert object in R_CERT. */ +gpg_error_t +fetch_next_ksba_cert (cert_fetch_context_t context, ksba_cert_t *r_cert) +{ + gpg_error_t err; + unsigned char *value; + size_t valuelen; + ksba_cert_t cert; + + *r_cert = NULL; + +#if USE_LDAP + err = fetch_next_cert_ldap (context, &value, &valuelen); + if (!err && !value) + err = gpg_error (GPG_ERR_BUG); +#else + (void)context; + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif + if (err) + return err; + + err = ksba_cert_new (&cert); + if (err) + { + xfree (value); + return err; + } + + err = ksba_cert_init_from_mem (cert, value, valuelen); + xfree (value); + if (err) + { + ksba_cert_release (cert); + return err; + } + *r_cert = cert; + return 0; +} + + +void +end_cert_fetch (cert_fetch_context_t context) +{ +#if USE_LDAP + end_cert_fetch_ldap (context); +#else + (void)context; +#endif +} + + +/* Lookup a cert by it's URL. */ +gpg_error_t +fetch_cert_by_url (ctrl_t ctrl, const char *url, + unsigned char **value, size_t *valuelen) +{ + const unsigned char *cert_image; + size_t cert_image_n; + ksba_reader_t reader; + ksba_cert_t cert; + gpg_error_t err; + + *value = NULL; + *valuelen = 0; + cert_image = NULL; + reader = NULL; + cert = NULL; + +#if USE_LDAP + err = url_fetch_ldap (ctrl, url, NULL, 0, &reader); +#else + (void)ctrl; + (void)url; + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif /*USE_LDAP*/ + if (err) + goto leave; + + err = ksba_cert_new (&cert); + if (err) + goto leave; + + err = ksba_cert_read_der (cert, reader); + if (err) + goto leave; + + cert_image = ksba_cert_get_image (cert, &cert_image_n); + if (!cert_image || !cert_image_n) + { + err = gpg_error (GPG_ERR_INV_CERT_OBJ); + goto leave; + } + + *value = xtrymalloc (cert_image_n); + if (!*value) + { + err = gpg_error_from_syserror (); + goto leave; + } + + memcpy (*value, cert_image, cert_image_n); + *valuelen = cert_image_n; + + leave: + + ksba_cert_release (cert); +#if USE_LDAP + ldap_wrapper_release_context (reader); +#endif /*USE_LDAP*/ + + return err; +} + +/* This function is to be used to close the reader object. In + addition to running ksba_reader_release it also releases the LDAP + or HTTP contexts associated with that reader. */ +void +crl_close_reader (ksba_reader_t reader) +{ + struct reader_cb_context_s *cb_ctx; + + if (!reader) + return; + + /* Check whether this is a HTTP one. */ + cb_ctx = get_file_reader (reader); + if (cb_ctx) + { + /* This is an HTTP context. */ + if (cb_ctx->fp) + es_fclose (cb_ctx->fp); + /* Release the base64 decoder state. */ + if (cb_ctx->is_pem) + b64dec_finish (&cb_ctx->b64state); + /* Release the callback context. */ + xfree (cb_ctx); + } + else /* This is an ldap wrapper context (Currently not used). */ + { +#if USE_LDAP + ldap_wrapper_release_context (reader); +#endif /*USE_LDAP*/ + } + + /* Now get rid of the reader object. */ + ksba_reader_release (reader); +} diff --git a/dirmngr/crlfetch.h b/dirmngr/crlfetch.h new file mode 100644 index 0000000..cf4a3c0 --- /dev/null +++ b/dirmngr/crlfetch.h @@ -0,0 +1,88 @@ +/* crlfetch.h - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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, see . + */ + +#ifndef CRLFETCH_H +#define CRLFETCH_H + +#include "dirmngr.h" + + +struct cert_fetch_context_s; +typedef struct cert_fetch_context_s *cert_fetch_context_t; + + +/* Fetch CRL from URL. */ +gpg_error_t crl_fetch (ctrl_t ctrl, const char* url, ksba_reader_t *reader); + +/* Fetch CRL for ISSUER using default server. */ +gpg_error_t crl_fetch_default (ctrl_t ctrl, + const char* issuer, ksba_reader_t *reader); + + +/* Fetch cert for DN. */ +gpg_error_t ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, + const char *dn); + + +/* Query the server for certs matching patterns. */ +gpg_error_t start_cert_fetch (ctrl_t ctrl, + cert_fetch_context_t *context, + strlist_t patterns, + const ldap_server_t server); +gpg_error_t fetch_next_cert(cert_fetch_context_t context, + unsigned char **value, size_t *valuelen); +gpg_error_t fetch_next_ksba_cert (cert_fetch_context_t context, + ksba_cert_t *r_cert); +void end_cert_fetch (cert_fetch_context_t context); + +/* Lookup a cert by it's URL. */ +gpg_error_t fetch_cert_by_url (ctrl_t ctrl, const char *url, + unsigned char **value, size_t *valuelen); + +/* Close a reader object. */ +void crl_close_reader (ksba_reader_t reader); + + + +/*-- ldap.c --*/ +gpg_error_t url_fetch_ldap (ctrl_t ctrl, + const char *url, const char *host, int port, + ksba_reader_t *reader); +gpg_error_t attr_fetch_ldap (ctrl_t ctrl, + const char *dn, const char *attr, + ksba_reader_t *reader); + + +gpg_error_t start_default_fetch_ldap (ctrl_t ctrl, + cert_fetch_context_t *context, + const char *dn, const char *attr); +gpg_error_t start_cert_fetch_ldap( ctrl_t ctrl, + cert_fetch_context_t *context, + strlist_t patterns, + const ldap_server_t server ); +gpg_error_t fetch_next_cert_ldap (cert_fetch_context_t context, + unsigned char **value, size_t *valuelen ); +void end_cert_fetch_ldap (cert_fetch_context_t context); + + + + + + +#endif /* CRLFETCH_H */ diff --git a/dirmngr/dirmngr-client.c b/dirmngr/dirmngr-client.c new file mode 100644 index 0000000..01cface --- /dev/null +++ b/dirmngr/dirmngr-client.c @@ -0,0 +1,934 @@ +/* dirmngr-client.c - A client for the dirmngr daemon + * Copyright (C) 2004, 2007 g10 Code GmbH + * Copyright (C) 2002, 2003 Free Software Foundation, Inc. + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../common/logging.h" +#include "../common/argparse.h" +#include "../common/stringhelp.h" +#include "../common/mischelp.h" +#include "../common/strlist.h" +#include "../common/asshelp.h" + +#include "i18n.h" +#include "util.h" +#include "init.h" + + +/* Constants for the options. */ +enum + { + oQuiet = 'q', + oVerbose = 'v', + oLocal = 'l', + oUrl = 'u', + + oOCSP = 500, + oPing, + oCacheCert, + oValidate, + oLookup, + oLoadCRL, + oSquidMode, + oPEM, + oEscapedPEM, + oForceDefaultResponder + }; + + +/* The list of options as used by the argparse.c code. */ +static ARGPARSE_OPTS opts[] = { + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oOCSP, "ocsp", 0, N_("use OCSP instead of CRLs") }, + { oPing, "ping", 0, N_("check whether a dirmngr is running")}, + { oCacheCert,"cache-cert",0, N_("add a certificate to the cache")}, + { oValidate, "validate", 0, N_("validate a certificate")}, + { oLookup, "lookup", 0, N_("lookup a certificate")}, + { oLocal, "local", 0, N_("lookup only locally stored certificates")}, + { oUrl, "url", 0, N_("expect an URL for --lookup")}, + { oLoadCRL, "load-crl", 0, N_("load a CRL into the dirmngr")}, + { oSquidMode,"squid-mode",0, N_("special mode for use by Squid")}, + { oPEM, "pem", 0, N_("expect certificates in PEM format")}, + { oForceDefaultResponder, "force-default-responder", 0, + N_("force the use of the default OCSP responder")}, + { 0, NULL, 0, NULL } +}; + + +/* The usual structure for the program flags. */ +static struct +{ + int quiet; + int verbose; + const char *dirmngr_program; + int force_default_responder; + int pem; + int escaped_pem; /* PEM is additional percent encoded. */ + int url; /* Expect an URL. */ + int local; /* Lookup up only local certificates. */ + + int use_ocsp; +} opt; + + +/* Communication structure for the certificate inquire callback. */ +struct inq_cert_parm_s +{ + assuan_context_t ctx; + const unsigned char *cert; + size_t certlen; +}; + + +/* Base64 conversion tables. */ +static unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; +static unsigned char asctobin[256]; /* runtime initialized */ + + +/* Build the helptable for radix64 to bin conversion. */ +static void +init_asctobin (void) +{ + static int initialized; + int i; + unsigned char *s; + + if (initialized) + return; + initialized = 1; + + for (i=0; i < 256; i++ ) + asctobin[i] = 255; /* Used to detect invalid characters. */ + for (s=bintoasc, i=0; *s; s++, i++) + asctobin[*s] = i; +} + + +/* Prototypes. */ +static gpg_error_t read_certificate (const char *fname, + unsigned char **rbuf, size_t *rbuflen); +static gpg_error_t do_check (assuan_context_t ctx, + const unsigned char *cert, size_t certlen); +static gpg_error_t do_cache (assuan_context_t ctx, + const unsigned char *cert, size_t certlen); +static gpg_error_t do_validate (assuan_context_t ctx, + const unsigned char *cert, size_t certlen); +static gpg_error_t do_loadcrl (assuan_context_t ctx, const char *filename); +static gpg_error_t do_lookup (assuan_context_t ctx, const char *pattern); +static gpg_error_t squid_loop_body (assuan_context_t ctx); + + + +/* Function called by argparse.c to display information. */ +static const char * +my_strusage (int level) +{ + const char *p; + + switch(level) + { + case 11: p = "dirmngr-client (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + case 49: p = PACKAGE_BUGREPORT; break; + case 1: + case 40: p = + _("Usage: dirmngr-client [options] " + "[certfile|pattern] (-h for help)\n"); + break; + case 41: p = + _("Syntax: dirmngr-client [options] [certfile|pattern]\n" + "Test an X.509 certificate against a CRL or do an OCSP check\n" + "The process returns 0 if the certificate is valid, 1 if it is\n" + "not valid and other error codes for general failures\n"); + break; + + default: p = NULL; + } + return p; +} + + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + assuan_context_t ctx; + gpg_error_t err; + unsigned char *certbuf; + size_t certbuflen = 0; + int cmd_ping = 0; + int cmd_cache_cert = 0; + int cmd_validate = 0; + int cmd_lookup = 0; + int cmd_loadcrl = 0; + int cmd_squid_mode = 0; + + early_system_init (); + set_strusage (my_strusage); + log_set_prefix ("dirmngr-client", + GPGRT_LOG_WITH_PREFIX); + + /* For W32 we need to initialize the socket subsystem. Because we + don't use Pth we need to do this explicit. */ +#ifdef HAVE_W32_SYSTEM + { + WSADATA wsadat; + + WSAStartup (0x202, &wsadat); + } +#endif /*HAVE_W32_SYSTEM*/ + + /* Init Assuan. */ + assuan_set_assuan_log_prefix (log_get_prefix (NULL)); + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + + /* Setup I18N. */ + i18n_init(); + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* Do not remove the args. */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oQuiet: opt.quiet++; break; + + case oOCSP: opt.use_ocsp++; break; + case oPing: cmd_ping = 1; break; + case oCacheCert: cmd_cache_cert = 1; break; + case oValidate: cmd_validate = 1; break; + case oLookup: cmd_lookup = 1; break; + case oUrl: opt.url = 1; break; + case oLocal: opt.local = 1; break; + case oLoadCRL: cmd_loadcrl = 1; break; + case oPEM: opt.pem = 1; break; + case oSquidMode: + opt.pem = 1; + opt.escaped_pem = 1; + cmd_squid_mode = 1; + break; + case oForceDefaultResponder: opt.force_default_responder = 1; break; + + default : pargs.err = 2; break; + } + } + if (log_get_errorcount (0)) + exit (2); + + if (cmd_ping) + err = 0; + else if (cmd_lookup || cmd_loadcrl) + { + if (!argc) + usage (1); + err = 0; + } + else if (cmd_squid_mode) + { + err = 0; + if (argc) + usage (1); + } + else if (!argc) + { + err = read_certificate (NULL, &certbuf, &certbuflen); + if (err) + log_error (_("error reading certificate from stdin: %s\n"), + gpg_strerror (err)); + } + else if (argc == 1) + { + err = read_certificate (*argv, &certbuf, &certbuflen); + if (err) + log_error (_("error reading certificate from '%s': %s\n"), + *argv, gpg_strerror (err)); + } + else + { + err = 0; + usage (1); + } + + if (log_get_errorcount (0)) + exit (2); + + if (certbuflen > 20000) + { + log_error (_("certificate too large to make any sense\n")); + exit (2); + } + + err = start_new_dirmngr (&ctx, + GPG_ERR_SOURCE_DEFAULT, + opt.dirmngr_program + ? opt.dirmngr_program + : gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR), + ! cmd_ping, + opt.verbose, + 0, + NULL, NULL); + if (err) + { + log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (err)); + exit (2); + } + + if (cmd_ping) + ; + else if (cmd_squid_mode) + { + while (!(err = squid_loop_body (ctx))) + ; + if (gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + } + else if (cmd_lookup) + { + int last_err = 0; + + for (; argc; argc--, argv++) + { + err = do_lookup (ctx, *argv); + if (err) + { + log_error (_("lookup failed: %s\n"), gpg_strerror (err)); + last_err = err; + } + } + err = last_err; + } + else if (cmd_loadcrl) + { + int last_err = 0; + + for (; argc; argc--, argv++) + { + err = do_loadcrl (ctx, *argv); + if (err) + { + log_error (_("loading CRL '%s' failed: %s\n"), + *argv, gpg_strerror (err)); + last_err = err; + } + } + err = last_err; + } + else if (cmd_cache_cert) + { + err = do_cache (ctx, certbuf, certbuflen); + xfree (certbuf); + } + else if (cmd_validate) + { + err = do_validate (ctx, certbuf, certbuflen); + xfree (certbuf); + } + else + { + err = do_check (ctx, certbuf, certbuflen); + xfree (certbuf); + } + + assuan_release (ctx); + + if (cmd_ping) + { + if (!opt.quiet) + log_info (_("a dirmngr daemon is up and running\n")); + return 0; + } + else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode) + return err? 1:0; + else if (cmd_cache_cert) + { + if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE ) + { + if (!opt.quiet) + log_info (_("certificate already cached\n")); + } + else if (err) + { + log_error (_("error caching certificate: %s\n"), + gpg_strerror (err)); + return 1; + } + return 0; + } + else if (cmd_validate && err) + { + log_error (_("validation of certificate failed: %s\n"), + gpg_strerror (err)); + return 1; + } + else if (!err) + { + if (!opt.quiet) + log_info (_("certificate is valid\n")); + return 0; + } + else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED ) + { + if (!opt.quiet) + log_info (_("certificate has been revoked\n")); + return 1; + } + else + { + log_error (_("certificate check failed: %s\n"), gpg_strerror (err)); + return 2; + } +} + + +/* Print status line from the assuan protocol. */ +static gpg_error_t +status_cb (void *opaque, const char *line) +{ + (void)opaque; + + if (opt.verbose > 2) + log_info (_("got status: '%s'\n"), line); + return 0; +} + +/* Print data as retrieved by the lookup function. */ +static gpg_error_t +data_cb (void *opaque, const void *buffer, size_t length) +{ + gpg_error_t err; + struct b64state *state = opaque; + + if (buffer) + { + err = b64enc_write (state, buffer, length); + if (err) + log_error (_("error writing base64 encoding: %s\n"), + gpg_strerror (err)); + } + return 0; +} + + +/* Read the first PEM certificate from the file FNAME. If fname is + NULL the next certificate is read from stdin. The certificate is + returned in an alloced buffer whose address will be returned in + RBUF and its length in RBUFLEN. */ +static gpg_error_t +read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen) +{ + FILE *fp; + int c; + int pos; + int value; + unsigned char *buf; + size_t bufsize, buflen; + enum { + s_init, s_idle, s_lfseen, s_begin, + s_b64_0, s_b64_1, s_b64_2, s_b64_3, + s_waitend + } state = s_init; + + init_asctobin (); + + fp = fname? fopen (fname, "r") : stdin; + if (!fp) + return gpg_error_from_errno (errno); + + pos = 0; + value = 0; + bufsize = 8192; + buf = xmalloc (bufsize); + buflen = 0; + while ((c=getc (fp)) != EOF) + { + int escaped_c = 0; + + if (opt.escaped_pem) + { + if (c == '%') + { + char tmp[2]; + if ((c = getc(fp)) == EOF) + break; + tmp[0] = c; + if ((c = getc(fp)) == EOF) + break; + tmp[1] = c; + if (!hexdigitp (tmp) || !hexdigitp (tmp+1)) + { + log_error ("invalid percent escape sequence\n"); + state = s_idle; /* Force an error. */ + /* Skip to end of line. */ + while ( (c=getc (fp)) != EOF && c != '\n') + ; + goto ready; + } + c = xtoi_2 (tmp); + escaped_c = 1; + } + else if (c == '\n') + goto ready; /* Ready. */ + } + switch (state) + { + case s_idle: + if (c == '\n') + { + state = s_lfseen; + pos = 0; + } + break; + case s_init: + state = s_lfseen; + case s_lfseen: + if (c != "-----BEGIN "[pos]) + state = s_idle; + else if (pos == 10) + state = s_begin; + else + pos++; + break; + case s_begin: + if (c == '\n') + state = s_b64_0; + break; + case s_b64_0: + case s_b64_1: + case s_b64_2: + case s_b64_3: + { + if (buflen >= bufsize) + { + bufsize += 8192; + buf = xrealloc (buf, bufsize); + } + + if (c == '-') + state = s_waitend; + else if ((c = asctobin[c & 0xff]) == 255 ) + ; /* Just skip invalid base64 characters. */ + else if (state == s_b64_0) + { + value = c << 2; + state = s_b64_1; + } + else if (state == s_b64_1) + { + value |= (c>>4)&3; + buf[buflen++] = value; + value = (c<<4)&0xf0; + state = s_b64_2; + } + else if (state == s_b64_2) + { + value |= (c>>2)&15; + buf[buflen++] = value; + value = (c<<6)&0xc0; + state = s_b64_3; + } + else + { + value |= c&0x3f; + buf[buflen++] = value; + state = s_b64_0; + } + } + break; + case s_waitend: + /* Note that we do not check that the base64 decoder has + been left in the expected state. We assume that the PEM + header is just fine. However we need to wait for the + real LF and not a trailing percent escaped one. */ + if (c== '\n' && !escaped_c) + goto ready; + break; + default: + BUG(); + } + } + ready: + if (fname) + fclose (fp); + + if (state == s_init && c == EOF) + { + xfree (buf); + return gpg_error (GPG_ERR_EOF); + } + else if (state != s_waitend) + { + log_error ("no certificate or invalid encoded\n"); + xfree (buf); + return gpg_error (GPG_ERR_INV_ARMOR); + } + + *rbuf = buf; + *rbuflen = buflen; + return 0; +} + +/* Read a binary certificate from the file FNAME. If fname is NULL the + file is read from stdin. The certificate is returned in an alloced + buffer whose address will be returned in RBUF and its length in + RBUFLEN. */ +static gpg_error_t +read_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen) +{ + gpg_error_t err; + FILE *fp; + unsigned char *buf; + size_t nread, bufsize, buflen; + + if (opt.pem) + return read_pem_certificate (fname, rbuf, rbuflen); + else if (fname) + { + /* A filename has been given. Let's just assume it is in PEM + format and decode it, and fall back to interpreting it as + binary certificate if that fails. */ + err = read_pem_certificate (fname, rbuf, rbuflen); + if (! err) + return 0; + } + + fp = fname? fopen (fname, "rb") : stdin; + if (!fp) + return gpg_error_from_errno (errno); + + buf = NULL; + bufsize = buflen = 0; +#define NCHUNK 8192 + do + { + bufsize += NCHUNK; + if (!buf) + buf = xmalloc (bufsize); + else + buf = xrealloc (buf, bufsize); + + nread = fread (buf+buflen, 1, NCHUNK, fp); + if (nread < NCHUNK && ferror (fp)) + { + err = gpg_error_from_errno (errno); + xfree (buf); + if (fname) + fclose (fp); + return err; + } + buflen += nread; + } + while (nread == NCHUNK); +#undef NCHUNK + if (fname) + fclose (fp); + *rbuf = buf; + *rbuflen = buflen; + return 0; +} + + +/* Callback for the inquire fiunction to send back the certificate. */ +static gpg_error_t +inq_cert (void *opaque, const char *line) +{ + struct inq_cert_parm_s *parm = opaque; + gpg_error_t err; + + if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10])) + { + err = assuan_send_data (parm->ctx, parm->cert, parm->certlen); + } + else if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8])) + { + /* We don't support this but dirmngr might ask for it. So + simply ignore it by sending back and empty value. */ + err = assuan_send_data (parm->ctx, NULL, 0); + } + else if (!strncmp (line, "SENDCERT_SKI", 12) + && (line[12]==' ' || !line[12])) + { + /* We don't support this but dirmngr might ask for it. So + simply ignore it by sending back an empty value. */ + err = assuan_send_data (parm->ctx, NULL, 0); + } + else if (!strncmp (line, "SENDISSUERCERT", 14) + && (line[14] == ' ' || !line[14])) + { + /* We don't support this but dirmngr might ask for it. So + simply ignore it by sending back an empty value. */ + err = assuan_send_data (parm->ctx, NULL, 0); + } + else + { + log_info (_("unsupported inquiry '%s'\n"), line); + err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + /* Note that this error will let assuan_transact terminate + immediately instead of return the error to the caller. It is + not clear whether this is the desired behaviour - it may + change in future. */ + } + + return err; +} + + +/* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP. + Return a proper error code. */ +static gpg_error_t +do_check (assuan_context_t ctx, const unsigned char *cert, size_t certlen) +{ + gpg_error_t err; + struct inq_cert_parm_s parm; + + memset (&parm, 0, sizeof parm); + parm.ctx = ctx; + parm.cert = cert; + parm.certlen = certlen; + + err = assuan_transact (ctx, + (opt.use_ocsp && opt.force_default_responder + ? "CHECKOCSP --force-default-responder" + : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"), + NULL, NULL, inq_cert, &parm, status_cb, NULL); + if (opt.verbose > 1) + log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay"); + return err; +} + +/* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP. + Return a proper error code. */ +static gpg_error_t +do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen) +{ + gpg_error_t err; + struct inq_cert_parm_s parm; + + memset (&parm, 0, sizeof parm); + parm.ctx = ctx; + parm.cert = cert; + parm.certlen = certlen; + + err = assuan_transact (ctx, "CACHECERT", NULL, NULL, + inq_cert, &parm, + status_cb, NULL); + if (opt.verbose > 1) + log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay"); + return err; +} + +/* Check the certificate CERT,CERTLEN for validity using dirmngrs + internal validate feature. Return a proper error code. */ +static gpg_error_t +do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen) +{ + gpg_error_t err; + struct inq_cert_parm_s parm; + + memset (&parm, 0, sizeof parm); + parm.ctx = ctx; + parm.cert = cert; + parm.certlen = certlen; + + err = assuan_transact (ctx, "VALIDATE", NULL, NULL, + inq_cert, &parm, + status_cb, NULL); + if (opt.verbose > 1) + log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay"); + return err; +} + +/* Load a CRL into the dirmngr. */ +static gpg_error_t +do_loadcrl (assuan_context_t ctx, const char *filename) +{ + gpg_error_t err; + const char *s; + char *fname, *line, *p; + + if (opt.url) + fname = xstrdup (filename); + else + { +#ifdef HAVE_CANONICALIZE_FILE_NAME + fname = canonicalize_file_name (filename); + if (!fname) + { + log_error ("error canonicalizing '%s': %s\n", + filename, strerror (errno)); + return gpg_error (GPG_ERR_GENERAL); + } +#else + fname = xstrdup (filename); +#endif + if (*fname != '/') + { + log_error (_("absolute file name expected\n")); + return gpg_error (GPG_ERR_GENERAL); + } + } + + line = xmalloc (8 + 6 + strlen (fname) * 3 + 1); + p = stpcpy (line, "LOADCRL "); + if (opt.url) + p = stpcpy (p, "--url "); + for (s = fname; *s; s++) + { + if (*s < ' ' || *s == '+') + { + sprintf (p, "%%%02X", *s); + p += 3; + } + else if (*s == ' ') + *p++ = '+'; + else + *p++ = *s; + } + *p = 0; + + err = assuan_transact (ctx, line, NULL, NULL, + NULL, NULL, + status_cb, NULL); + if (opt.verbose > 1) + log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay"); + xfree (line); + xfree (fname); + return err; +} + + +/* Do a LDAP lookup using PATTERN and print the result in a base-64 + encoded format. */ +static gpg_error_t +do_lookup (assuan_context_t ctx, const char *pattern) +{ + gpg_error_t err; + const unsigned char *s; + char *line, *p; + struct b64state state; + + if (opt.verbose) + log_info (_("looking up '%s'\n"), pattern); + + err = b64enc_start (&state, stdout, NULL); + if (err) + return err; + + line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1); + + p = stpcpy (line, "LOOKUP "); + if (opt.url) + p = stpcpy (p, "--url "); + if (opt.local) + p = stpcpy (p, "--cache-only "); + for (s=pattern; *s; s++) + { + if (*s < ' ' || *s == '+') + { + sprintf (p, "%%%02X", *s); + p += 3; + } + else if (*s == ' ') + *p++ = '+'; + else + *p++ = *s; + } + *p = 0; + + + err = assuan_transact (ctx, line, + data_cb, &state, + NULL, NULL, + status_cb, NULL); + if (opt.verbose > 1) + log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay"); + + err = b64enc_finish (&state); + + xfree (line); + return err; +} + +/* The body of an endless loop: Read a line from stdin, retrieve the + certificate from it, validate it and print "ERR" or "OK" to stdout. + Continue. */ +static gpg_error_t +squid_loop_body (assuan_context_t ctx) +{ + gpg_error_t err; + unsigned char *certbuf; + size_t certbuflen = 0; + + err = read_pem_certificate (NULL, &certbuf, &certbuflen); + if (gpg_err_code (err) == GPG_ERR_EOF) + return err; + if (err) + { + log_error (_("error reading certificate from stdin: %s\n"), + gpg_strerror (err)); + puts ("ERROR"); + return 0; + } + + err = do_check (ctx, certbuf, certbuflen); + xfree (certbuf); + if (!err) + { + if (opt.verbose) + log_info (_("certificate is valid\n")); + puts ("OK"); + } + else + { + if (!opt.quiet) + { + if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED ) + log_info (_("certificate has been revoked\n")); + else + log_error (_("certificate check failed: %s\n"), + gpg_strerror (err)); + } + puts ("ERROR"); + } + + fflush (stdout); + + return 0; +} diff --git a/dirmngr/dirmngr-err.h b/dirmngr/dirmngr-err.h new file mode 100644 index 0000000..17e8255 --- /dev/null +++ b/dirmngr/dirmngr-err.h @@ -0,0 +1,12 @@ +/* Definition of the gpg-error source. */ + +#ifndef DIRMNGR_ERR_H +#define DIRMNGR_ERR_H + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_DIRMNGR +#include + +#endif /*DIRMNGR_ERR_H*/ diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c new file mode 100644 index 0000000..5ee589e --- /dev/null +++ b/dirmngr/dirmngr.c @@ -0,0 +1,2155 @@ +/* dirmngr.c - Keyserver and X.509 LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2010, 2011 g10 Code GmbH + * Copyright (C) 2014 Werner Koch + * + * 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef HAVE_W32_SYSTEM +#include +#include +#endif +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#ifdef HAVE_INOTIFY_INIT +# include +#endif /*HAVE_INOTIFY_INIT*/ +#include + +#include "dirmngr-err.h" + +#if HTTP_USE_NTBTLS +# include +#elif HTTP_USE_GNUTLS +# include +#endif /*HTTP_USE_GNUTLS*/ + + +#define GNUPG_COMMON_NEED_AFLOCAL +#include "dirmngr.h" + +#include + +#include "certcache.h" +#include "crlcache.h" +#include "crlfetch.h" +#include "misc.h" +#if USE_LDAP +# include "ldapserver.h" +#endif +#include "asshelp.h" +#if USE_LDAP +# include "ldap-wrapper.h" +#endif +#include "../common/init.h" +#include "gc-opt-flags.h" +#include "dns-stuff.h" + +#ifndef ENAMETOOLONG +# define ENAMETOOLONG EINVAL +#endif + + +enum cmd_and_opt_values { + aNull = 0, + oCsh = 'c', + oQuiet = 'q', + oSh = 's', + oVerbose = 'v', + oNoVerbose = 500, + + aServer, + aDaemon, + aSupervised, + aListCRLs, + aLoadCRL, + aFetchCRL, + aShutdown, + aFlush, + aGPGConfList, + aGPGConfTest, + + oOptions, + oDebug, + oDebugAll, + oDebugWait, + oDebugLevel, + oGnutlsDebug, + oNoGreeting, + oNoOptions, + oHomedir, + oNoDetach, + oLogFile, + oBatch, + oDisableHTTP, + oDisableLDAP, + oIgnoreLDAPDP, + oIgnoreHTTPDP, + oIgnoreOCSPSvcUrl, + oHonorHTTPProxy, + oHTTPProxy, + oLDAPProxy, + oOnlyLDAPProxy, + oLDAPFile, + oLDAPTimeout, + oLDAPAddServers, + oOCSPResponder, + oOCSPSigner, + oOCSPMaxClockSkew, + oOCSPMaxPeriod, + oOCSPCurrentPeriod, + oMaxReplies, + oHkpCaCert, + oFakedSystemTime, + oForce, + oAllowOCSP, + oAllowVersionCheck, + oSocketName, + oLDAPWrapperProgram, + oHTTPWrapperProgram, + oIgnoreCertExtension, + oUseTor, + oKeyServer, + oNameServer, + oDisableCheckOwnSocket, + oStandardResolver, + oRecursiveResolver, + oResolverTimeout, + aTest +}; + + + +static ARGPARSE_OPTS opts[] = { + + ARGPARSE_group (300, N_("@Commands:\n ")), + + ARGPARSE_c (aServer, "server", N_("run in server mode (foreground)") ), + ARGPARSE_c (aDaemon, "daemon", N_("run in daemon mode (background)") ), +#ifndef HAVE_W32_SYSTEM + ARGPARSE_c (aSupervised, "supervised", N_("run in supervised mode")), +#endif + ARGPARSE_c (aListCRLs, "list-crls", N_("list the contents of the CRL cache")), + ARGPARSE_c (aLoadCRL, "load-crl", N_("|FILE|load CRL from FILE into cache")), + ARGPARSE_c (aFetchCRL, "fetch-crl", N_("|URL|fetch a CRL from URL")), + ARGPARSE_c (aShutdown, "shutdown", N_("shutdown the dirmngr")), + ARGPARSE_c (aFlush, "flush", N_("flush the cache")), + ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), + ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), + + ARGPARSE_group (301, N_("@\nOptions:\n ")), + + ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), + ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), + ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")), + ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")), + ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")), + ARGPARSE_s_s (oDebugLevel, "debug-level", + N_("|LEVEL|set the debugging level to LEVEL")), + ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")), + ARGPARSE_s_s (oLogFile, "log-file", + N_("|FILE|write server mode logs to FILE")), + ARGPARSE_s_n (oBatch, "batch", N_("run without asking a user")), + ARGPARSE_s_n (oForce, "force", N_("force loading of outdated CRLs")), + ARGPARSE_s_n (oAllowOCSP, "allow-ocsp", N_("allow sending OCSP requests")), + ARGPARSE_s_n (oAllowVersionCheck, "allow-version-check", + N_("allow online software version check")), + ARGPARSE_s_n (oDisableHTTP, "disable-http", N_("inhibit the use of HTTP")), + ARGPARSE_s_n (oDisableLDAP, "disable-ldap", N_("inhibit the use of LDAP")), + ARGPARSE_s_n (oIgnoreHTTPDP,"ignore-http-dp", + N_("ignore HTTP CRL distribution points")), + ARGPARSE_s_n (oIgnoreLDAPDP,"ignore-ldap-dp", + N_("ignore LDAP CRL distribution points")), + ARGPARSE_s_n (oIgnoreOCSPSvcUrl, "ignore-ocsp-service-url", + N_("ignore certificate contained OCSP service URLs")), + + ARGPARSE_s_s (oHTTPProxy, "http-proxy", + N_("|URL|redirect all HTTP requests to URL")), + ARGPARSE_s_s (oLDAPProxy, "ldap-proxy", + N_("|HOST|use HOST for LDAP queries")), + ARGPARSE_s_n (oOnlyLDAPProxy, "only-ldap-proxy", + N_("do not use fallback hosts with --ldap-proxy")), + + ARGPARSE_s_s (oLDAPFile, "ldapserverlist-file", + N_("|FILE|read LDAP server list from FILE")), + ARGPARSE_s_n (oLDAPAddServers, "add-servers", + N_("add new servers discovered in CRL distribution" + " points to serverlist")), + ARGPARSE_s_i (oLDAPTimeout, "ldaptimeout", + N_("|N|set LDAP timeout to N seconds")), + + ARGPARSE_s_s (oOCSPResponder, "ocsp-responder", + N_("|URL|use OCSP responder at URL")), + ARGPARSE_s_s (oOCSPSigner, "ocsp-signer", + N_("|FPR|OCSP response signed by FPR")), + ARGPARSE_s_i (oOCSPMaxClockSkew, "ocsp-max-clock-skew", "@"), + ARGPARSE_s_i (oOCSPMaxPeriod, "ocsp-max-period", "@"), + ARGPARSE_s_i (oOCSPCurrentPeriod, "ocsp-current-period", "@"), + + ARGPARSE_s_i (oMaxReplies, "max-replies", + N_("|N|do not return more than N items in one query")), + + ARGPARSE_s_s (oNameServer, "nameserver", "@"), + ARGPARSE_s_s (oKeyServer, "keyserver", "@"), + ARGPARSE_s_s (oHkpCaCert, "hkp-cacert", + N_("|FILE|use the CA certificates in FILE for HKP over TLS")), + + ARGPARSE_s_n (oUseTor, "use-tor", N_("route all network traffic via Tor")), + + ARGPARSE_s_s (oSocketName, "socket-name", "@"), /* Only for debugging. */ + + ARGPARSE_s_u (oFakedSystemTime, "faked-system-time", "@"), /*(epoch time)*/ + ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_n (oDebugAll, "debug-all", "@"), + ARGPARSE_s_i (oGnutlsDebug, "gnutls-debug", "@"), + ARGPARSE_s_i (oGnutlsDebug, "tls-debug", "@"), + ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), + ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"), + ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), + ARGPARSE_s_s (oHomedir, "homedir", "@"), + ARGPARSE_s_s (oLDAPWrapperProgram, "ldap-wrapper-program", "@"), + ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"), + ARGPARSE_s_n (oHonorHTTPProxy, "honor-http-proxy", "@"), + ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"), + ARGPARSE_s_n (oStandardResolver, "standard-resolver", "@"), + ARGPARSE_s_n (oRecursiveResolver, "recursive-resolver", "@"), + ARGPARSE_s_i (oResolverTimeout, "resolver-timeout", "@"), + + ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing " + "of all commands and options)\n")), + + ARGPARSE_end () +}; + +/* The list of supported debug flags. */ +static struct debug_flags_s debug_flags [] = + { + { DBG_X509_VALUE , "x509" }, + { DBG_CRYPTO_VALUE , "crypto" }, + { DBG_MEMORY_VALUE , "memory" }, + { DBG_CACHE_VALUE , "cache" }, + { DBG_MEMSTAT_VALUE, "memstat" }, + { DBG_HASHING_VALUE, "hashing" }, + { DBG_IPC_VALUE , "ipc" }, + { DBG_DNS_VALUE , "dns" }, + { DBG_NETWORK_VALUE, "network" }, + { DBG_LOOKUP_VALUE , "lookup" }, + { 77, NULL } /* 77 := Do not exit on "help" or "?". */ + }; + +#define DEFAULT_MAX_REPLIES 10 +#define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */ + +/* For the cleanup handler we need to keep track of the socket's name. */ +static const char *socket_name; +/* If the socket has been redirected, this is the name of the + redirected socket.. */ +static const char *redir_socket_name; + +/* We need to keep track of the server's nonces (these are dummies for + POSIX systems). */ +static assuan_sock_nonce_t socket_nonce; + +/* Only if this flag has been set will we remove the socket file. */ +static int cleanup_socket; + +/* Keep track of the current log file so that we can avoid updating + the log file after a SIGHUP if it didn't changed. Malloced. */ +static char *current_logfile; + +/* Helper to implement --debug-level. */ +static const char *debug_level; + +/* Helper to set the NTBTLS or GNUTLS log level. */ +static int opt_gnutls_debug = -1; + +/* Flag indicating that a shutdown has been requested. */ +static volatile int shutdown_pending; + +/* Flags to indicate that we shall not watch our own socket. */ +static int disable_check_own_socket; + +/* Counter for the active connections. */ +static int active_connections; + +/* This flag is set by any network access and used by the housekeeping + * thread to run background network tasks. */ +static int network_activity_seen; + +/* The timer tick used for housekeeping stuff. */ +#define TIMERTICK_INTERVAL (60) + +/* How oft to run the housekeeping. */ +#define HOUSEKEEPING_INTERVAL (600) + + +/* This union is used to avoid compiler warnings in case a pointer is + 64 bit and an int 32 bit. We store an integer in a pointer and get + it back later (npth_getspecific et al.). */ +union int_and_ptr_u +{ + int aint; + assuan_fd_t afd; + void *aptr; +}; + + + +/* The key used to store the current file descriptor in the thread + local storage. We use this in conjunction with the + log_set_pid_suffix_cb feature. */ +#ifndef HAVE_W32_SYSTEM +static int my_tlskey_current_fd; +#endif + +/* Prototypes. */ +static void cleanup (void); +#if USE_LDAP +static ldap_server_t parse_ldapserver_file (const char* filename); +#endif /*USE_LDAP*/ +static fingerprint_list_t parse_ocsp_signer (const char *string); +static void netactivity_action (void); +static void handle_connections (assuan_fd_t listen_fd); + +/* NPth wrapper function definitions. */ +ASSUAN_SYSTEM_NPTH_IMPL; + +static const char * +my_strusage( int level ) +{ + const char *p; + switch ( level ) + { + case 11: p = "@DIRMNGR@ (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + /* TRANSLATORS: @EMAIL@ will get replaced by the actual bug + reporting address. This is so that we can change the + reporting address without breaking the translations. */ + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + case 49: p = PACKAGE_BUGREPORT; break; + case 1: + case 40: p = _("Usage: @DIRMNGR@ [options] (-h for help)"); + break; + case 41: p = _("Syntax: @DIRMNGR@ [options] [command [args]]\n" + "Keyserver, CRL, and OCSP access for @GNUPG@\n"); + break; + + default: p = NULL; + } + return p; +} + + +/* Callback from libksba to hash a provided buffer. Our current + implementation does only allow SHA-1 for hashing. This may be + extended by mapping the name, testing for algorithm availibility + and adjust the length checks accordingly. */ +static gpg_error_t +my_ksba_hash_buffer (void *arg, const char *oid, + const void *buffer, size_t length, size_t resultsize, + unsigned char *result, size_t *resultlen) +{ + (void)arg; + + if (oid && strcmp (oid, "1.3.14.3.2.26")) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (resultsize < 20) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + gcry_md_hash_buffer (2, result, buffer, length); + *resultlen = 20; + return 0; +} + + +/* GNUTLS log function callback. */ +#ifdef HTTP_USE_GNUTLS +static void +my_gnutls_log (int level, const char *text) +{ + int n; + + n = strlen (text); + while (n && text[n-1] == '\n') + n--; + + log_debug ("gnutls:L%d: %.*s\n", level, n, text); +} +#endif /*HTTP_USE_GNUTLS*/ + +/* Setup the debugging. With a LEVEL of NULL only the active debug + flags are propagated to the subsystems. With LEVEL set, a specific + set of debug flags is set; thus overriding all flags already + set. */ +static void +set_debug (void) +{ + int numok = (debug_level && digitp (debug_level)); + int numlvl = numok? atoi (debug_level) : 0; + + if (!debug_level) + ; + else if (!strcmp (debug_level, "none") || (numok && numlvl < 1)) + opt.debug = 0; + else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2)) + opt.debug = DBG_IPC_VALUE; + else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5)) + opt.debug = (DBG_IPC_VALUE|DBG_X509_VALUE|DBG_LOOKUP_VALUE); + else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) + opt.debug = (DBG_IPC_VALUE|DBG_X509_VALUE|DBG_LOOKUP_VALUE + |DBG_CACHE_VALUE|DBG_CRYPTO_VALUE); + else if (!strcmp (debug_level, "guru") || numok) + { + opt.debug = ~0; + /* Unless the "guru" string has been used we don't want to allow + hashing debugging. The rationale is that people tend to + select the highest debug value and would then clutter their + disk with debug files which may reveal confidential data. */ + if (numok) + opt.debug &= ~(DBG_HASHING_VALUE); + } + else + { + log_error (_("invalid debug-level '%s' given\n"), debug_level); + log_info (_("valid debug levels are: %s\n"), + "none, basic, advanced, expert, guru"); + opt.debug = 0; /* Reset debugging, so that prior debug + statements won't have an undesired effect. */ + } + + + if (opt.debug && !opt.verbose) + { + opt.verbose = 1; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + } + if (opt.debug && opt.quiet) + opt.quiet = 0; + + if (opt.debug & DBG_CRYPTO_VALUE ) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); + +#if HTTP_USE_NTBTLS + if (opt_gnutls_debug >= 0) + { + ntbtls_set_debug (opt_gnutls_debug, NULL, NULL); + } +#elif HTTP_USE_GNUTLS + if (opt_gnutls_debug >= 0) + { + gnutls_global_set_log_function (my_gnutls_log); + gnutls_global_set_log_level (opt_gnutls_debug); + } +#endif /*HTTP_USE_GNUTLS*/ + + if (opt.debug) + parse_debug_flag (NULL, &opt.debug, debug_flags); +} + + +static void +set_tor_mode (void) +{ + if (opt.use_tor) + { + if (assuan_sock_set_flag (ASSUAN_INVALID_FD, "tor-mode", 1)) + { + log_error ("error enabling Tor mode: %s\n", strerror (errno)); + log_info ("(is your Libassuan recent enough?)\n"); + } + } +} + + +static void +wrong_args (const char *text) +{ + es_fprintf (es_stderr, _("usage: %s [options] "), DIRMNGR_NAME); + es_fputs (text, es_stderr); + es_putc ('\n', es_stderr); + dirmngr_exit (2); +} + + +/* Helper to stop the reaper thread for the ldap wrapper. */ +static void +shutdown_reaper (void) +{ +#if USE_LDAP + ldap_wrapper_wait_connections (); +#endif +} + + +/* Handle options which are allowed to be reset after program start. + Return true if the current option in PARGS could be handled and + false if not. As a special feature, passing a value of NULL for + PARGS, resets the options to the default. REREAD should be set + true if it is not the initial option parsing. */ +static int +parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) +{ + if (!pargs) + { /* Reset mode. */ + opt.quiet = 0; + opt.verbose = 0; + opt.debug = 0; + opt.ldap_wrapper_program = NULL; + opt.disable_http = 0; + opt.disable_ldap = 0; + opt.honor_http_proxy = 0; + opt.http_proxy = NULL; + opt.ldap_proxy = NULL; + opt.only_ldap_proxy = 0; + opt.ignore_http_dp = 0; + opt.ignore_ldap_dp = 0; + opt.ignore_ocsp_service_url = 0; + opt.allow_ocsp = 0; + opt.allow_version_check = 0; + opt.ocsp_responder = NULL; + opt.ocsp_max_clock_skew = 10 * 60; /* 10 minutes. */ + opt.ocsp_max_period = 90 * 86400; /* 90 days. */ + opt.ocsp_current_period = 3 * 60 * 60; /* 3 hours. */ + opt.max_replies = DEFAULT_MAX_REPLIES; + while (opt.ocsp_signer) + { + fingerprint_list_t tmp = opt.ocsp_signer->next; + xfree (opt.ocsp_signer); + opt.ocsp_signer = tmp; + } + FREE_STRLIST (opt.ignored_cert_extensions); + http_register_tls_ca (NULL); + FREE_STRLIST (opt.keyserver); + /* Note: We do not allow resetting of opt.use_tor at runtime. */ + disable_check_own_socket = 0; + enable_standard_resolver (0); + set_dns_timeout (0); + return 1; + } + + switch (pargs->r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oDebug: + parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags); + break; + case oDebugAll: opt.debug = ~0; break; + case oDebugLevel: debug_level = pargs->r.ret_str; break; + case oGnutlsDebug: opt_gnutls_debug = pargs->r.ret_int; break; + + case oLogFile: + if (!reread) + return 0; /* Not handled. */ + if (!current_logfile || !pargs->r.ret_str + || strcmp (current_logfile, pargs->r.ret_str)) + { + log_set_file (pargs->r.ret_str); + xfree (current_logfile); + current_logfile = xtrystrdup (pargs->r.ret_str); + } + break; + + case oDisableCheckOwnSocket: disable_check_own_socket = 1; break; + + case oLDAPWrapperProgram: + opt.ldap_wrapper_program = pargs->r.ret_str; + break; + case oHTTPWrapperProgram: + opt.http_wrapper_program = pargs->r.ret_str; + break; + + case oDisableHTTP: opt.disable_http = 1; break; + case oDisableLDAP: opt.disable_ldap = 1; break; + case oHonorHTTPProxy: opt.honor_http_proxy = 1; break; + case oHTTPProxy: opt.http_proxy = pargs->r.ret_str; break; + case oLDAPProxy: opt.ldap_proxy = pargs->r.ret_str; break; + case oOnlyLDAPProxy: opt.only_ldap_proxy = 1; break; + case oIgnoreHTTPDP: opt.ignore_http_dp = 1; break; + case oIgnoreLDAPDP: opt.ignore_ldap_dp = 1; break; + case oIgnoreOCSPSvcUrl: opt.ignore_ocsp_service_url = 1; break; + + case oAllowOCSP: opt.allow_ocsp = 1; break; + case oAllowVersionCheck: opt.allow_version_check = 1; break; + case oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break; + case oOCSPSigner: + opt.ocsp_signer = parse_ocsp_signer (pargs->r.ret_str); + break; + case oOCSPMaxClockSkew: opt.ocsp_max_clock_skew = pargs->r.ret_int; break; + case oOCSPMaxPeriod: opt.ocsp_max_period = pargs->r.ret_int; break; + case oOCSPCurrentPeriod: opt.ocsp_current_period = pargs->r.ret_int; break; + + case oMaxReplies: opt.max_replies = pargs->r.ret_int; break; + + case oHkpCaCert: + { + char *tmpname; + + /* Do tilde expansion and make path absolute. */ + tmpname = make_absfilename (pargs->r.ret_str, NULL); + http_register_tls_ca (tmpname); + xfree (tmpname); + } + break; + + case oIgnoreCertExtension: + add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str); + break; + + case oUseTor: opt.use_tor = 1; break; + + case oStandardResolver: enable_standard_resolver (1); break; + case oRecursiveResolver: enable_recursive_resolver (1); break; + + case oKeyServer: + if (*pargs->r.ret_str) + add_to_strlist (&opt.keyserver, pargs->r.ret_str); + break; + + case oNameServer: + set_dns_nameserver (pargs->r.ret_str); + break; + + case oResolverTimeout: + set_dns_timeout (pargs->r.ret_int); + break; + + default: + return 0; /* Not handled. */ + } + + set_dns_verbose (opt.verbose, !!DBG_DNS); + + return 1; /* Handled. */ +} + + +#ifndef HAVE_W32_SYSTEM +static int +pid_suffix_callback (unsigned long *r_suffix) +{ + union int_and_ptr_u value; + + memset (&value, 0, sizeof value); + value.aptr = npth_getspecific (my_tlskey_current_fd); + *r_suffix = value.aint; + return (*r_suffix != -1); /* Use decimal representation. */ +} +#endif /*!HAVE_W32_SYSTEM*/ + + +static void +thread_init (void) +{ + npth_init (); + gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + + /* Now with NPth running we can set the logging callback. Our + windows implementation does not yet feature the NPth TLS + functions. */ +#ifndef HAVE_W32_SYSTEM + if (npth_key_create (&my_tlskey_current_fd, NULL) == 0) + if (npth_setspecific (my_tlskey_current_fd, NULL) == 0) + log_set_pid_suffix_cb (pid_suffix_callback); +#endif /*!HAVE_W32_SYSTEM*/ +} + + +int +main (int argc, char **argv) +{ + enum cmd_and_opt_values cmd = 0; + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + FILE *configfp = NULL; + char *configname = NULL; + const char *shell; + unsigned configlineno; + int parse_debug = 0; + int default_config =1; + int greeting = 0; + int nogreeting = 0; + int nodetach = 0; + int csh_style = 0; + char *logfile = NULL; +#if USE_LDAP + char *ldapfile = NULL; +#endif /*USE_LDAP*/ + int debug_wait = 0; + int rc; + struct assuan_malloc_hooks malloc_hooks; + + early_system_init (); + set_strusage (my_strusage); + log_set_prefix (DIRMNGR_NAME, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID); + + /* Make sure that our subsystems are ready. */ + i18n_init (); + init_common_subsystems (&argc, &argv); + + gcry_control (GCRYCTL_DISABLE_SECMEM, 0); + + /* Check that the libraries are suitable. Do it here because + the option parsing may need services of the libraries. */ + if (!ksba_check_version (NEED_KSBA_VERSION) ) + log_fatal( _("%s is too old (need %s, have %s)\n"), "libksba", + NEED_KSBA_VERSION, ksba_check_version (NULL) ); + + ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); + ksba_set_hash_buffer_function (my_ksba_hash_buffer, NULL); + + /* Init TLS library. */ +#if HTTP_USE_NTBTLS + if (!ntbtls_check_version (NEED_NTBTLS_VERSION) ) + log_fatal( _("%s is too old (need %s, have %s)\n"), "ntbtls", + NEED_NTBTLS_VERSION, ntbtls_check_version (NULL) ); +#elif HTTP_USE_GNUTLS + rc = gnutls_global_init (); + if (rc) + log_fatal ("gnutls_global_init failed: %s\n", gnutls_strerror (rc)); +#endif /*HTTP_USE_GNUTLS*/ + + /* Init Assuan. */ + malloc_hooks.malloc = gcry_malloc; + malloc_hooks.realloc = gcry_realloc; + malloc_hooks.free = gcry_free; + assuan_set_malloc_hooks (&malloc_hooks); + assuan_set_assuan_log_prefix (log_get_prefix (NULL)); + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); + assuan_sock_init (); + setup_libassuan_logging (&opt.debug, dirmngr_assuan_log_monitor); + + setup_libgcrypt_logging (); + + /* Setup defaults. */ + shell = getenv ("SHELL"); + if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) + csh_style = 1; + + /* Reset rereadable options to default values. */ + parse_rereadable_options (NULL, 0); + + /* LDAP defaults. */ + opt.add_new_ldapservers = 0; + opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT; + + /* Other defaults. */ + + /* 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 == oDebug || pargs.r_opt == oDebugAll) + parse_debug++; + else 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) + { + gnupg_set_homedir (pargs.r.ret_str); + } + } + + socket_name = dirmngr_socket_name (); + if (default_config) + configname = make_filename (gnupg_homedir (), DIRMNGR_NAME".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) + { + if( parse_debug ) + log_info (_("Note: no default option file '%s'\n"), + configname ); + } + else + { + log_error (_("option file '%s': %s\n"), + configname, strerror(errno) ); + exit(2); + } + xfree (configname); + configname = NULL; + } + if (parse_debug && configname ) + log_info (_("reading options from '%s'\n"), configname ); + default_config = 0; + } + + while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) + { + if (parse_rereadable_options (&pargs, 0)) + continue; /* Already handled */ + switch (pargs.r_opt) + { + case aServer: + case aDaemon: + case aSupervised: + case aShutdown: + case aFlush: + case aListCRLs: + case aLoadCRL: + case aFetchCRL: + case aGPGConfList: + case aGPGConfTest: + cmd = pargs.r_opt; + break; + + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oBatch: opt.batch=1; break; + + case oDebugWait: debug_wait = pargs.r.ret_int; 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 oNoGreeting: nogreeting = 1; break; + case oNoVerbose: opt.verbose = 0; break; + case oNoOptions: break; /* no-options */ + case oHomedir: /* Ignore this option here. */; break; + case oNoDetach: nodetach = 1; break; + case oLogFile: logfile = pargs.r.ret_str; break; + case oCsh: csh_style = 1; break; + case oSh: csh_style = 0; break; + case oLDAPFile: +# if USE_LDAP + ldapfile = pargs.r.ret_str; +# endif /*USE_LDAP*/ + break; + case oLDAPAddServers: opt.add_new_ldapservers = 1; break; + case oLDAPTimeout: + opt.ldaptimeout = pargs.r.ret_int; + break; + + case oFakedSystemTime: + gnupg_set_time ((time_t)pargs.r.ret_ulong, 0); + break; + + case oForce: opt.force = 1; break; + + case oSocketName: socket_name = pargs.r.ret_str; break; + + default : pargs.err = configfp? 1:2; break; + } + } + if (configfp) + { + fclose (configfp); + configfp = NULL; + /* Keep a copy of the name so that it can be read on SIGHUP. */ + opt.config_filename = configname; + configname = NULL; + goto next_pass; + } + xfree (configname); + configname = NULL; + if (log_get_errorcount(0)) + exit(2); + if (nogreeting ) + greeting = 0; + + if (!opt.homedir_cache) + opt.homedir_cache = xstrdup (gnupg_homedir ()); + + if (greeting) + { + es_fprintf (es_stderr, "%s %s; %s\n", + strusage(11), strusage(13), strusage(14) ); + es_fprintf (es_stderr, "%s\n", strusage(15) ); + } + +#ifdef IS_DEVELOPMENT_VERSION + log_info ("NOTE: this is a development version!\n"); +#endif + + if (opt.use_tor) + { + log_info ("WARNING: ***************************************\n"); + log_info ("WARNING: Tor mode (--use-tor) MAY NOT FULLY WORK!\n"); + log_info ("WARNING: ***************************************\n"); + } + + /* Print a warning if an argument looks like an option. */ + if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) + { + int i; + + for (i=0; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '-') + log_info (_("Note: '%s' is not considered an option\n"), argv[i]); + } + + if (!access ("/etc/"DIRMNGR_NAME, F_OK) + && !strncmp (gnupg_homedir (), "/etc/", 5)) + log_info + ("NOTE: DirMngr is now a proper part of %s. The configuration and" + " other directory names changed. Please check that no other version" + " of dirmngr is still installed. To disable this warning, remove the" + " directory '/etc/dirmngr'.\n", GNUPG_NAME); + + if (gnupg_faked_time_p ()) + { + gnupg_isotime_t tbuf; + + log_info (_("WARNING: running with faked system time: ")); + gnupg_get_isotime (tbuf); + dump_isotime (tbuf); + log_printf ("\n"); + } + + set_debug (); + set_tor_mode (); + + /* Get LDAP server list from file. */ +#if USE_LDAP + if (!ldapfile) + { + ldapfile = make_filename (gnupg_homedir (), + "dirmngr_ldapservers.conf", + NULL); + opt.ldapservers = parse_ldapserver_file (ldapfile); + xfree (ldapfile); + } + else + opt.ldapservers = parse_ldapserver_file (ldapfile); +#endif /*USE_LDAP*/ + +#ifndef HAVE_W32_SYSTEM + /* We need to ignore the PIPE signal because the we might log to a + socket and that code handles EPIPE properly. The ldap wrapper + also requires us to ignore this silly signal. Assuan would set + this signal to ignore anyway.*/ + signal (SIGPIPE, SIG_IGN); +#endif + + /* Ready. Now to our duties. */ + if (!cmd) + cmd = aServer; + rc = 0; + + if (cmd == aServer) + { + /* Note that this server mode is mainly useful for debugging. */ + if (argc) + wrong_args ("--server"); + + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID); + } + + if (debug_wait) + { + log_debug ("waiting for debugger - my pid is %u .....\n", + (unsigned int)getpid()); + gnupg_sleep (debug_wait); + log_debug ("... okay\n"); + } + + + thread_init (); + cert_cache_init (); + crl_cache_init (); + http_register_netactivity_cb (netactivity_action); + start_command_handler (ASSUAN_INVALID_FD); + shutdown_reaper (); + } +#ifndef HAVE_W32_SYSTEM + else if (cmd == aSupervised) + { + /* In supervised mode, we expect file descriptor 3 to be an + already opened, listening socket. + + We will also not detach from the controlling process or close + stderr; the supervisor should handle all of that. */ + struct stat statbuf; + if (fstat (3, &statbuf) == -1 && errno == EBADF) + { + log_error ("file descriptor 3 must be validin --supervised mode\n"); + dirmngr_exit (1); + } + socket_name = gnupg_get_socket_name (3); + + /* Now start with logging to a file if this is desired. */ + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX + |GPGRT_LOG_WITH_TIME + |GPGRT_LOG_WITH_PID)); + current_logfile = xstrdup (logfile); + } + else + log_set_prefix (NULL, 0); + + thread_init (); + cert_cache_init (); + crl_cache_init (); + http_register_netactivity_cb (netactivity_action); + handle_connections (3); + shutdown_reaper (); + } +#endif /*HAVE_W32_SYSTEM*/ + else if (cmd == aDaemon) + { + assuan_fd_t fd; + pid_t pid; + int len; + struct sockaddr_un serv_addr; + + if (argc) + wrong_args ("--daemon"); + + /* Now start with logging to a file if this is desired. */ + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX + |GPGRT_LOG_WITH_TIME + |GPGRT_LOG_WITH_PID)); + current_logfile = xstrdup (logfile); + } + +#ifndef HAVE_W32_SYSTEM + if (strchr (socket_name, ':')) + { + log_error (_("colons are not allowed in the socket name\n")); + dirmngr_exit (1); + } +#endif + fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0); + if (fd == ASSUAN_INVALID_FD) + { + log_error (_("can't create socket: %s\n"), strerror (errno)); + cleanup (); + dirmngr_exit (1); + } + + { + int redirected; + + if (assuan_sock_set_sockaddr_un (socket_name, + (struct sockaddr*)&serv_addr, + &redirected)) + { + if (errno == ENAMETOOLONG) + log_error (_("socket name '%s' is too long\n"), socket_name); + else + log_error ("error preparing socket '%s': %s\n", + socket_name, + gpg_strerror (gpg_error_from_syserror ())); + dirmngr_exit (1); + } + if (redirected) + { + redir_socket_name = xstrdup (serv_addr.sun_path); + if (opt.verbose) + log_info ("redirecting socket '%s' to '%s'\n", + socket_name, redir_socket_name); + } + } + + len = SUN_LEN (&serv_addr); + + rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, len); + if (rc == -1 + && (errno == EADDRINUSE +#ifdef HAVE_W32_SYSTEM + || errno == EEXIST +#endif + )) + { + /* Fixme: We should test whether a dirmngr is already running. */ + gnupg_remove (redir_socket_name? redir_socket_name : socket_name); + rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, len); + } + if (rc != -1 + && (rc = assuan_sock_get_nonce ((struct sockaddr*) &serv_addr, len, &socket_nonce))) + log_error (_("error getting nonce for the socket\n")); + if (rc == -1) + { + log_error (_("error binding socket to '%s': %s\n"), + serv_addr.sun_path, + gpg_strerror (gpg_error_from_errno (errno))); + assuan_sock_close (fd); + dirmngr_exit (1); + } + cleanup_socket = 1; + + if (gnupg_chmod (serv_addr.sun_path, "-rwx")) + log_error (_("can't set permissions of '%s': %s\n"), + serv_addr.sun_path, strerror (errno)); + + if (listen (FD2INT (fd), 5) == -1) + { + log_error (_("listen() failed: %s\n"), strerror (errno)); + assuan_sock_close (fd); + dirmngr_exit (1); + } + + if (opt.verbose) + log_info (_("listening on socket '%s'\n"), serv_addr.sun_path); + + es_fflush (NULL); + + /* Note: We keep the dirmngr_info output only for the sake of + existing scripts which might use this to detect a successful + start of the dirmngr. */ +#ifdef HAVE_W32_SYSTEM + (void)csh_style; + (void)nodetach; + + pid = getpid (); + es_printf ("set %s=%s;%lu;1\n", + DIRMNGR_INFO_NAME, socket_name, (ulong) pid); +#else + pid = fork(); + if (pid == (pid_t)-1) + { + log_fatal (_("error forking process: %s\n"), strerror (errno)); + dirmngr_exit (1); + } + + if (pid) + { /* We are the parent */ + char *infostr; + + /* Don't let cleanup() remove the socket - the child is + responsible for doing that. */ + cleanup_socket = 0; + + close (fd); + + /* Create the info string: :: */ + if (asprintf (&infostr, "%s=%s:%lu:1", + DIRMNGR_INFO_NAME, serv_addr.sun_path, (ulong)pid ) < 0) + { + log_error (_("out of core\n")); + kill (pid, SIGTERM); + dirmngr_exit (1); + } + /* Print the environment string, so that the caller can use + shell's eval to set it. But see above. */ + if (csh_style) + { + *strchr (infostr, '=') = ' '; + es_printf ( "setenv %s;\n", infostr); + } + else + { + es_printf ( "%s; export %s;\n", infostr, DIRMNGR_INFO_NAME); + } + free (infostr); + exit (0); + /*NEVER REACHED*/ + } /* end parent */ + + + /* + This is the child + */ + + /* Detach from tty and put process into a new session */ + if (!nodetach ) + { + int i; + unsigned int oldflags; + + /* Close stdin, stdout and stderr unless it is the log stream */ + for (i=0; i <= 2; i++) + { + if (!log_test_fd (i) && i != fd ) + { + if ( !close (i) + && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1) + { + log_error ("failed to open '%s': %s\n", + "/dev/null", strerror (errno)); + cleanup (); + dirmngr_exit (1); + } + } + } + + if (setsid() == -1) + { + log_error ("setsid() failed: %s\n", strerror(errno) ); + dirmngr_exit (1); + } + + log_get_prefix (&oldflags); + log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED); + opt.running_detached = 1; + + if (chdir("/")) + { + log_error ("chdir to / failed: %s\n", strerror (errno)); + dirmngr_exit (1); + } + } +#endif + + thread_init (); + cert_cache_init (); + crl_cache_init (); + http_register_netactivity_cb (netactivity_action); + handle_connections (fd); + shutdown_reaper (); + } + else if (cmd == aListCRLs) + { + /* Just list the CRL cache and exit. */ + if (argc) + wrong_args ("--list-crls"); + crl_cache_init (); + crl_cache_list (es_stdout); + } + else if (cmd == aLoadCRL) + { + struct server_control_s ctrlbuf; + + memset (&ctrlbuf, 0, sizeof ctrlbuf); + dirmngr_init_default_ctrl (&ctrlbuf); + + thread_init (); + cert_cache_init (); + crl_cache_init (); + if (!argc) + rc = crl_cache_load (&ctrlbuf, NULL); + else + { + for (; !rc && argc; argc--, argv++) + rc = crl_cache_load (&ctrlbuf, *argv); + } + dirmngr_deinit_default_ctrl (&ctrlbuf); + } + else if (cmd == aFetchCRL) + { + ksba_reader_t reader; + struct server_control_s ctrlbuf; + + if (argc != 1) + wrong_args ("--fetch-crl URL"); + + memset (&ctrlbuf, 0, sizeof ctrlbuf); + dirmngr_init_default_ctrl (&ctrlbuf); + + thread_init (); + cert_cache_init (); + crl_cache_init (); + rc = crl_fetch (&ctrlbuf, argv[0], &reader); + if (rc) + log_error (_("fetching CRL from '%s' failed: %s\n"), + argv[0], gpg_strerror (rc)); + else + { + rc = crl_cache_insert (&ctrlbuf, argv[0], reader); + if (rc) + log_error (_("processing CRL from '%s' failed: %s\n"), + argv[0], gpg_strerror (rc)); + crl_close_reader (reader); + } + dirmngr_deinit_default_ctrl (&ctrlbuf); + } + else if (cmd == aFlush) + { + /* Delete cache and exit. */ + if (argc) + wrong_args ("--flush"); + rc = crl_cache_flush(); + } + else if (cmd == aGPGConfTest) + dirmngr_exit (0); + else if (cmd == aGPGConfList) + { + unsigned long flags = 0; + char *filename; + char *filename_esc; + + /* First the configuration file. This is not an option, but it + is vital information for GPG Conf. */ + if (!opt.config_filename) + opt.config_filename = make_filename (gnupg_homedir (), + "dirmngr.conf", NULL ); + + filename = percent_escape (opt.config_filename, NULL); + es_printf ("gpgconf-dirmngr.conf:%lu:\"%s\n", + GC_OPT_FLAG_DEFAULT, filename); + xfree (filename); + + es_printf ("verbose:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("quiet:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("debug-level:%lu:\"none\n", flags | GC_OPT_FLAG_DEFAULT); + es_printf ("log-file:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("force:%lu:\n", flags | GC_OPT_FLAG_NONE); + + /* --csh and --sh are mutually exclusive, something we can not + express in GPG Conf. --options is only usable from the + command line, really. --debug-all interacts with --debug, + and having both of them is thus problematic. --no-detach is + also only usable on the command line. --batch is unused. */ + + filename = make_filename (gnupg_homedir (), + "dirmngr_ldapservers.conf", + NULL); + filename_esc = percent_escape (filename, NULL); + es_printf ("ldapserverlist-file:%lu:\"%s\n", flags | GC_OPT_FLAG_DEFAULT, + filename_esc); + xfree (filename_esc); + xfree (filename); + + es_printf ("ldaptimeout:%lu:%u\n", + flags | GC_OPT_FLAG_DEFAULT, DEFAULT_LDAP_TIMEOUT); + es_printf ("max-replies:%lu:%u\n", + flags | GC_OPT_FLAG_DEFAULT, DEFAULT_MAX_REPLIES); + es_printf ("allow-ocsp:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("allow-version-check:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("ocsp-responder:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("ocsp-signer:%lu:\n", flags | GC_OPT_FLAG_NONE); + + es_printf ("faked-system-time:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("no-greeting:%lu:\n", flags | GC_OPT_FLAG_NONE); + + es_printf ("disable-http:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("disable-ldap:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("honor-http-proxy:%lu\n", flags | GC_OPT_FLAG_NONE); + es_printf ("http-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("only-ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("ignore-ldap-dp:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("ignore-http-dp:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("ignore-ocsp-service-url:%lu:\n", flags | GC_OPT_FLAG_NONE); + /* Note: The next one is to fix a typo in gpgconf - should be + removed eventually. */ + es_printf ("ignore-ocsp-servic-url:%lu:\n", flags | GC_OPT_FLAG_NONE); + + es_printf ("use-tor:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("keyserver:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("nameserver:%lu:\n", flags | GC_OPT_FLAG_NONE); + es_printf ("resolver-timeout:%lu:%u\n", + flags | GC_OPT_FLAG_DEFAULT, 0); + } + cleanup (); + return !!rc; +} + + +static void +cleanup (void) +{ + crl_cache_deinit (); + cert_cache_deinit (1); + reload_dns_stuff (1); + +#if USE_LDAP + ldapserver_list_free (opt.ldapservers); +#endif /*USE_LDAP*/ + opt.ldapservers = NULL; + + if (cleanup_socket) + { + cleanup_socket = 0; + if (redir_socket_name) + gnupg_remove (redir_socket_name); + else if (socket_name && *socket_name) + gnupg_remove (socket_name); + } +} + + +void +dirmngr_exit (int rc) +{ + cleanup (); + exit (rc); +} + + +void +dirmngr_init_default_ctrl (ctrl_t ctrl) +{ + if (opt.http_proxy) + ctrl->http_proxy = xstrdup (opt.http_proxy); +} + + +void +dirmngr_deinit_default_ctrl (ctrl_t ctrl) +{ + if (!ctrl) + return; + xfree (ctrl->http_proxy); + ctrl->http_proxy = NULL; +} + + +/* Create a list of LDAP servers from the file FILENAME. Returns the + list or NULL in case of errors. + + The format fo such a file is line oriented where empty lines and + lines starting with a hash mark are ignored. All other lines are + assumed to be colon seprated with these fields: + + 1. field: Hostname + 2. field: Portnumber + 3. field: Username + 4. field: Password + 5. field: Base DN + +*/ +#if USE_LDAP +static ldap_server_t +parse_ldapserver_file (const char* filename) +{ + char buffer[1024]; + char *p; + ldap_server_t server, serverstart, *serverend; + int c; + unsigned int lineno = 0; + estream_t fp; + + fp = es_fopen (filename, "r"); + if (!fp) + { + log_error (_("error opening '%s': %s\n"), filename, strerror (errno)); + return NULL; + } + + serverstart = NULL; + serverend = &serverstart; + while (es_fgets (buffer, sizeof buffer, fp)) + { + lineno++; + if (!*buffer || buffer[strlen(buffer)-1] != '\n') + { + if (*buffer && es_feof (fp)) + ; /* Last line not terminated - continue. */ + else + { + log_error (_("%s:%u: line too long - skipped\n"), + filename, lineno); + while ( (c=es_fgetc (fp)) != EOF && c != '\n') + ; /* Skip until end of line. */ + continue; + } + } + /* Skip empty and comment lines.*/ + for (p=buffer; spacep (p); p++) + ; + if (!*p || *p == '\n' || *p == '#') + continue; + + /* Parse the colon separated fields. */ + server = ldapserver_parse_one (buffer, filename, lineno); + if (server) + { + *serverend = server; + serverend = &server->next; + } + } + + if (es_ferror (fp)) + log_error (_("error reading '%s': %s\n"), filename, strerror (errno)); + es_fclose (fp); + + return serverstart; +} +#endif /*USE_LDAP*/ + +static fingerprint_list_t +parse_ocsp_signer (const char *string) +{ + gpg_error_t err; + char *fname; + estream_t fp; + char line[256]; + char *p; + fingerprint_list_t list, *list_tail, item; + unsigned int lnr = 0; + int c, i, j; + int errflag = 0; + + + /* Check whether this is not a filename and treat it as a direct + fingerprint specification. */ + if (!strpbrk (string, "/.~\\")) + { + item = xcalloc (1, sizeof *item); + for (i=j=0; (string[i] == ':' || hexdigitp (string+i)) && j < 40; i++) + if ( string[i] != ':' ) + item->hexfpr[j++] = string[i] >= 'a'? (string[i] & 0xdf): string[i]; + item->hexfpr[j] = 0; + if (j != 40 || !(spacep (string+i) || !string[i])) + { + log_error (_("%s:%u: invalid fingerprint detected\n"), + "--ocsp-signer", 0); + xfree (item); + return NULL; + } + return item; + } + + /* Well, it is a filename. */ + if (*string == '/' || (*string == '~' && string[1] == '/')) + fname = make_filename (string, NULL); + else + { + if (string[0] == '.' && string[1] == '/' ) + string += 2; + fname = make_filename (gnupg_homedir (), string, NULL); + } + + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); + xfree (fname); + return NULL; + } + + list = NULL; + list_tail = &list; + for (;;) + { + if (!es_fgets (line, DIM(line)-1, fp) ) + { + if (!es_feof (fp)) + { + err = gpg_error_from_syserror (); + log_error (_("%s:%u: read error: %s\n"), + fname, lnr, gpg_strerror (err)); + errflag = 1; + } + es_fclose (fp); + if (errflag) + { + while (list) + { + fingerprint_list_t tmp = list->next; + xfree (list); + list = tmp; + } + } + xfree (fname); + return list; /* Ready. */ + } + + lnr++; + if (!*line || line[strlen(line)-1] != '\n') + { + /* Eat until end of line. */ + while ( (c=es_getc (fp)) != EOF && c != '\n') + ; + err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG + /* */: GPG_ERR_INCOMPLETE_LINE); + log_error (_("%s:%u: read error: %s\n"), + fname, lnr, gpg_strerror (err)); + errflag = 1; + continue; + } + + /* Allow for empty lines and spaces */ + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '\n' || *p == '#') + continue; + + item = xcalloc (1, sizeof *item); + *list_tail = item; + list_tail = &item->next; + + for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++) + if ( p[i] != ':' ) + item->hexfpr[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i]; + item->hexfpr[j] = 0; + if (j != 40 || !(spacep (p+i) || p[i] == '\n')) + { + log_error (_("%s:%u: invalid fingerprint detected\n"), fname, lnr); + errflag = 1; + } + i++; + while (spacep (p+i)) + i++; + if (p[i] && p[i] != '\n') + log_info (_("%s:%u: garbage at end of line ignored\n"), fname, lnr); + } + /*NOTREACHED*/ +} + + + + +/* + Stuff used in daemon mode. + */ + + + +/* Reread parts of the configuration. Note, that this function is + obviously not thread-safe and should only be called from the NPTH + signal handler. + + Fixme: Due to the way the argument parsing works, we create a + memory leak here for all string type arguments. There is currently + no clean way to tell whether the memory for the argument has been + allocated or points into the process' original arguments. Unless + we have a mechanism to tell this, we need to live on with this. */ +static void +reread_configuration (void) +{ + ARGPARSE_ARGS pargs; + FILE *fp; + unsigned int configlineno = 0; + int dummy; + + if (!opt.config_filename) + return; /* No config file. */ + + fp = fopen (opt.config_filename, "r"); + if (!fp) + { + log_error (_("option file '%s': %s\n"), + opt.config_filename, strerror(errno) ); + return; + } + + parse_rereadable_options (NULL, 1); /* Start from the default values. */ + + memset (&pargs, 0, sizeof pargs); + dummy = 0; + pargs.argc = &dummy; + pargs.flags = 1; /* do not remove the args */ + while (optfile_parse (fp, opt.config_filename, &configlineno, &pargs, opts) ) + { + if (pargs.r_opt < -1) + pargs.err = 1; /* Print a warning. */ + else /* Try to parse this option - ignore unchangeable ones. */ + parse_rereadable_options (&pargs, 1); + } + fclose (fp); + + set_debug (); + set_tor_mode (); +} + + +/* A global function which allows us to trigger the reload stuff from + other places. */ +void +dirmngr_sighup_action (void) +{ + log_info (_("SIGHUP received - " + "re-reading configuration and flushing caches\n")); + reread_configuration (); + cert_cache_deinit (0); + crl_cache_deinit (); + cert_cache_init (); + crl_cache_init (); + reload_dns_stuff (0); +} + + +/* This function is called if some network activity was done. At this + * point we know the we have a network and we can decide whether to + * run scheduled background tasks soon. The function should return + * quickly and only trigger actions for another thread. */ +static void +netactivity_action (void) +{ + network_activity_seen = 1; +} + + +/* The signal handler. */ +#ifndef HAVE_W32_SYSTEM +static void +handle_signal (int signo) +{ + switch (signo) + { + case SIGHUP: + dirmngr_sighup_action (); + break; + + case SIGUSR1: + cert_cache_print_stats (); + break; + + case SIGUSR2: + log_info (_("SIGUSR2 received - no action defined\n")); + break; + + case SIGTERM: + if (!shutdown_pending) + log_info (_("SIGTERM received - shutting down ...\n")); + else + log_info (_("SIGTERM received - still %d active connections\n"), + active_connections); + shutdown_pending++; + if (shutdown_pending > 2) + { + log_info (_("shutdown forced\n")); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + cleanup (); + dirmngr_exit (0); + } + break; + + case SIGINT: + log_info (_("SIGINT received - immediate shutdown\n")); + log_info( "%s %s stopped\n", strusage(11), strusage(13)); + cleanup (); + dirmngr_exit (0); + break; + + default: + log_info (_("signal %d received - no action defined\n"), signo); + } +} +#endif /*!HAVE_W32_SYSTEM*/ + + +/* Thread to do the housekeeping. */ +static void * +housekeeping_thread (void *arg) +{ + static int sentinel; + time_t curtime; + struct server_control_s ctrlbuf; + + (void)arg; + + curtime = gnupg_get_time (); + if (sentinel) + { + log_info ("housekeeping is already going on\n"); + return NULL; + } + sentinel++; + if (opt.verbose > 1) + log_info ("starting housekeeping\n"); + + memset (&ctrlbuf, 0, sizeof ctrlbuf); + dirmngr_init_default_ctrl (&ctrlbuf); + + ks_hkp_housekeeping (curtime); + if (network_activity_seen) + { + network_activity_seen = 0; + if (opt.use_tor || opt.allow_version_check) + dirmngr_load_swdb (&ctrlbuf, 0); + } + + dirmngr_deinit_default_ctrl (&ctrlbuf); + + if (opt.verbose > 1) + log_info ("ready with housekeeping\n"); + sentinel--; + return NULL; + +} + + +#if GPGRT_GCC_HAVE_PUSH_PRAGMA +# pragma GCC push_options +# pragma GCC optimize ("no-strict-overflow") +#endif +static int +time_for_housekeeping_p (time_t curtime) +{ + static time_t last_housekeeping; + + if (!last_housekeeping) + last_housekeeping = curtime; + + if (last_housekeeping + HOUSEKEEPING_INTERVAL <= curtime + || last_housekeeping > curtime /*(be prepared for y2038)*/) + { + last_housekeeping = curtime; + return 1; + } + return 0; +} +#if GPGRT_GCC_HAVE_PUSH_PRAGMA +# pragma GCC pop_options +#endif + + +/* This is the worker for the ticker. It is called every few seconds + and may only do fast operations. */ +static void +handle_tick (void) +{ + if (time_for_housekeeping_p (gnupg_get_time ())) + { + npth_t thread; + npth_attr_t tattr; + int err; + + err = npth_attr_init (&tattr); + if (err) + log_error ("error preparing housekeeping thread: %s\n", strerror (err)); + else + { + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + err = npth_create (&thread, &tattr, housekeeping_thread, NULL); + if (err) + log_error ("error spawning housekeeping thread: %s\n", + strerror (err)); + npth_attr_destroy (&tattr); + } + } +} + + +/* Check the nonce on a new connection. This is a NOP unless we are + using our Unix domain socket emulation under Windows. */ +static int +check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce) +{ + if (assuan_sock_check_nonce (fd, nonce)) + { + log_info (_("error reading nonce on fd %d: %s\n"), + FD2INT (fd), strerror (errno)); + assuan_sock_close (fd); + return -1; + } + else + return 0; +} + + +/* Helper to call a connection's main function. */ +static void * +start_connection_thread (void *arg) +{ + union int_and_ptr_u argval; + gnupg_fd_t fd; + + memset (&argval, 0, sizeof argval); + argval.aptr = arg; + fd = argval.afd; + + if (check_nonce (fd, &socket_nonce)) + { + log_error ("handler nonce check FAILED\n"); + return NULL; + } + +#ifndef HAVE_W32_SYSTEM + npth_setspecific (my_tlskey_current_fd, argval.aptr); +#endif + + active_connections++; + if (opt.verbose) + log_info (_("handler for fd %d started\n"), FD2INT (fd)); + + start_command_handler (fd); + + if (opt.verbose) + log_info (_("handler for fd %d terminated\n"), FD2INT (fd)); + active_connections--; + +#ifndef HAVE_W32_SYSTEM + argval.afd = ASSUAN_INVALID_FD; + npth_setspecific (my_tlskey_current_fd, argval.aptr); +#endif + + return NULL; +} + + +#ifdef HAVE_INOTIFY_INIT +/* Read an inotify event and return true if it matches NAME. */ +static int +my_inotify_is_name (int fd, const char *name) +{ + union { + struct inotify_event ev; + char _buf[sizeof (struct inotify_event) + 100 + 1]; + } buf; + int n; + const char *s; + + s = strrchr (name, '/'); + if (s && s[1]) + name = s + 1; + + n = npth_read (fd, &buf, sizeof buf); + if (n < sizeof (struct inotify_event)) + return 0; + if (buf.ev.len < strlen (name)+1) + return 0; + if (strcmp (buf.ev.name, name)) + return 0; /* Not the desired file. */ + + return 1; /* Found. */ +} +#endif /*HAVE_INOTIFY_INIT*/ + + +/* Main loop in daemon mode. Note that LISTEN_FD will be owned by + * this function. */ +static void +handle_connections (assuan_fd_t listen_fd) +{ + npth_attr_t tattr; +#ifndef HAVE_W32_SYSTEM + int signo; +#endif + struct sockaddr_un paddr; + socklen_t plen = sizeof( paddr ); + gnupg_fd_t fd; + int nfd, ret; + fd_set fdset, read_fdset; + struct timespec abstime; + struct timespec curtime; + struct timespec timeout; + int saved_errno; + int my_inotify_fd = -1; + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + +#ifndef HAVE_W32_SYSTEM /* FIXME */ + npth_sigev_init (); + npth_sigev_add (SIGHUP); + npth_sigev_add (SIGUSR1); + npth_sigev_add (SIGUSR2); + npth_sigev_add (SIGINT); + npth_sigev_add (SIGTERM); + npth_sigev_fini (); +#endif + +#ifdef HAVE_INOTIFY_INIT + if (disable_check_own_socket) + my_inotify_fd = -1; + else if ((my_inotify_fd = inotify_init ()) == -1) + log_info ("error enabling fast daemon termination: %s\n", + strerror (errno)); + else + { + /* We need to watch the directory for the file because there + * won't be an IN_DELETE_SELF for a socket file. */ + char *slash = strrchr (socket_name, '/'); + log_assert (slash && slash[1]); + *slash = 0; + if (inotify_add_watch (my_inotify_fd, socket_name, IN_DELETE) == -1) + { + close (my_inotify_fd); + my_inotify_fd = -1; + } + *slash = '/'; + } +#endif /*HAVE_INOTIFY_INIT*/ + + + /* Setup the fdset. It has only one member. This is because we use + pth_select instead of pth_accept to properly sync timeouts with + to full second. */ + FD_ZERO (&fdset); + FD_SET (FD2INT (listen_fd), &fdset); + nfd = FD2INT (listen_fd); + if (my_inotify_fd != -1) + { + FD_SET (my_inotify_fd, &fdset); + if (my_inotify_fd > nfd) + nfd = my_inotify_fd; + } + + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + + /* Main loop. */ + for (;;) + { + /* Shutdown test. */ + if (shutdown_pending) + { + if (!active_connections) + break; /* ready */ + + /* Do not accept new connections but keep on running the + * loop to cope with the timer events. + * + * Note that we do not close the listening socket because a + * client trying to connect to that socket would instead + * restart a new dirmngr instance - which is unlikely the + * intention of a shutdown. */ + /* assuan_sock_close (listen_fd); */ + /* listen_fd = -1; */ + FD_ZERO (&fdset); + nfd = -1; + if (my_inotify_fd != -1) + { + FD_SET (my_inotify_fd, &fdset); + nfd = my_inotify_fd; + } + } + + /* Take a copy of the fdset. */ + read_fdset = fdset; + + npth_clock_gettime (&curtime); + if (!(npth_timercmp (&curtime, &abstime, <))) + { + /* Timeout. */ + handle_tick (); + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + } + npth_timersub (&abstime, &curtime, &timeout); + +#ifndef HAVE_W32_SYSTEM + ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask()); + saved_errno = errno; + + while (npth_sigev_get_pending(&signo)) + handle_signal (signo); +#else + ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL); + saved_errno = errno; +#endif + + if (ret == -1 && saved_errno != EINTR) + { + log_error (_("npth_pselect failed: %s - waiting 1s\n"), + strerror (saved_errno)); + npth_sleep (1); + continue; + } + + if (ret <= 0) + { + /* Interrupt or timeout. Will be handled when calculating the + next timeout. */ + continue; + } + + if (shutdown_pending) + { + /* Do not anymore accept connections. */ + continue; + } + +#ifdef HAVE_INOTIFY_INIT + if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset) + && my_inotify_is_name (my_inotify_fd, socket_name)) + { + shutdown_pending = 1; + log_info ("socket file has been removed - shutting down\n"); + } +#endif /*HAVE_INOTIFY_INIT*/ + + if (FD_ISSET (FD2INT (listen_fd), &read_fdset)) + { + plen = sizeof paddr; + fd = INT2FD (npth_accept (FD2INT(listen_fd), + (struct sockaddr *)&paddr, &plen)); + if (fd == GNUPG_INVALID_FD) + { + log_error ("accept failed: %s\n", strerror (errno)); + } + else + { + char threadname[50]; + union int_and_ptr_u argval; + npth_t thread; + + memset (&argval, 0, sizeof argval); + argval.afd = fd; + snprintf (threadname, sizeof threadname, + "conn fd=%d", FD2INT(fd)); + + ret = npth_create (&thread, &tattr, + start_connection_thread, argval.aptr); + if (ret) + { + log_error ("error spawning connection handler: %s\n", + strerror (ret) ); + assuan_sock_close (fd); + } + npth_setname_np (thread, threadname); + } + fd = GNUPG_INVALID_FD; + } + } + +#ifdef HAVE_INOTIFY_INIT + if (my_inotify_fd != -1) + close (my_inotify_fd); +#endif /*HAVE_INOTIFY_INIT*/ + npth_attr_destroy (&tattr); + if (listen_fd != -1) + assuan_sock_close (fd); + cleanup (); + log_info ("%s %s stopped\n", strusage(11), strusage(13)); +} + +const char* +dirmngr_get_current_socket_name (void) +{ + if (socket_name) + return socket_name; + else + return dirmngr_socket_name (); +} diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h new file mode 100644 index 0000000..9a87878 --- /dev/null +++ b/dirmngr/dirmngr.h @@ -0,0 +1,218 @@ +/* dirmngr.h - Common definitions for the dirmngr + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2004, 2015 g10 Code GmbH + * Copyright (C) 2014 Werner Koch + * + * 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 3 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, see . + */ + +#ifndef DIRMNGR_H +#define DIRMNGR_H + +#include "./dirmngr-err.h" +#define map_assuan_err(a) \ + map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a)) +#include +#include +#include + +#include "../common/util.h" +#include "../common/membuf.h" +#include "../common/sysutils.h" /* (gnupg_fd_t) */ +#include "../common/asshelp.h" /* (assuan_context_t) */ +#include "../common/i18n.h" +#include "http.h" /* (parsed_uri_t) */ + +/* This objects keeps information about a particular LDAP server and + is used as item of a single linked list of servers. */ +struct ldap_server_s +{ + struct ldap_server_s* next; + + char *host; + int port; + char *user; + char *pass; + char *base; +}; +typedef struct ldap_server_s *ldap_server_t; + + +/* This objects is used to build a list of URI consisting of the + original and the parsed URI. */ +struct uri_item_s +{ + struct uri_item_s *next; + parsed_uri_t parsed_uri; /* The broken down URI. */ + char uri[1]; /* The original URI. */ +}; +typedef struct uri_item_s *uri_item_t; + + +/* A list of fingerprints. */ +struct fingerprint_list_s; +typedef struct fingerprint_list_s *fingerprint_list_t; +struct fingerprint_list_s +{ + fingerprint_list_t next; + char hexfpr[20+20+1]; +}; + + +/* A large struct named "opt" to keep global flags. */ +struct +{ + unsigned int debug; /* debug flags (DBG_foo_VALUE) */ + int verbose; /* verbosity level */ + int quiet; /* be as quiet as possible */ + int dry_run; /* don't change any persistent data */ + int batch; /* batch mode */ + const char *homedir_cache; /* Dir for cache files (/var/cache/dirmngr). */ + + char *config_filename; /* Name of a config file, which will be + reread on a HUP if it is not NULL. */ + + char *ldap_wrapper_program; /* Override value for the LDAP wrapper + program. */ + char *http_wrapper_program; /* Override value for the HTTP wrapper + program. */ + + int running_detached; /* We are running in detached mode. */ + int use_tor; /* Tor mode has been enabled. */ + int allow_version_check; /* --allow-version-check is active. */ + + int force; /* Force loading outdated CRLs. */ + + int disable_http; /* Do not use HTTP at all. */ + int disable_ldap; /* Do not use LDAP at all. */ + int honor_http_proxy; /* Honor the http_proxy env variable. */ + const char *http_proxy; /* The default HTTP proxy. */ + const char *ldap_proxy; /* Use given LDAP proxy. */ + int only_ldap_proxy; /* Only use the LDAP proxy; no fallback. */ + int ignore_http_dp; /* Ignore HTTP CRL distribution points. */ + int ignore_ldap_dp; /* Ignore LDAP CRL distribution points. */ + int ignore_ocsp_service_url; /* Ignore OCSP service URLs as given in + the certificate. */ + + /* A list of certificate extension OIDs which are ignored so that + one can claim that a critical extension has been handled. One + OID per string. */ + strlist_t ignored_cert_extensions; + + int allow_ocsp; /* Allow using OCSP. */ + + int max_replies; + unsigned int ldaptimeout; + + ldap_server_t ldapservers; + int add_new_ldapservers; + + const char *ocsp_responder; /* Standard OCSP responder's URL. */ + fingerprint_list_t ocsp_signer; /* The list of fingerprints with allowed + standard OCSP signer certificates. */ + + unsigned int ocsp_max_clock_skew; /* Allowed seconds of clocks skew. */ + unsigned int ocsp_max_period; /* Seconds a response is at maximum + considered valid after thisUpdate. */ + unsigned int ocsp_current_period; /* Seconds a response is considered + current after nextUpdate. */ + + strlist_t keyserver; /* List of default keyservers. */ +} opt; + + +#define DBG_X509_VALUE 1 /* debug x.509 parsing */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ +#define DBG_DNS_VALUE 16 /* debug DNS calls. */ +#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ +#define DBG_CACHE_VALUE 64 /* debug the caching */ +#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ +#define DBG_IPC_VALUE 1024 /* debug assuan communication */ +#define DBG_NETWORK_VALUE 2048 /* debug network I/O. */ +#define DBG_LOOKUP_VALUE 8192 /* debug lookup details */ + +#define DBG_X509 (opt.debug & DBG_X509_VALUE) +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) +#define DBG_DNS (opt.debug & DBG_DNS_VALUE) +#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) +#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) +#define DBG_IPC (opt.debug & DBG_IPC_VALUE) +#define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE) +#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) + +/* A simple list of certificate references. */ +struct cert_ref_s +{ + struct cert_ref_s *next; + unsigned char fpr[20]; +}; +typedef struct cert_ref_s *cert_ref_t; + +/* Forward references; access only through server.c. */ +struct server_local_s; + +/* Connection control structure. */ +struct server_control_s +{ + int refcount; /* Count additional references to this object. */ + int no_server; /* We are not running under server control. */ + int status_fd; /* Only for non-server mode. */ + struct server_local_s *server_local; + int force_crl_refresh; /* Always load a fresh CRL. */ + + int check_revocations_nest_level; /* Internal to check_revovations. */ + cert_ref_t ocsp_certs; /* Certificates from the current OCSP + response. */ + + int audit_events; /* Send audit events to client. */ + char *http_proxy; /* The used http_proxy or NULL. */ +}; + + +/*-- dirmngr.c --*/ +void dirmngr_exit( int ); /* Wrapper for exit() */ +void dirmngr_init_default_ctrl (ctrl_t ctrl); +void dirmngr_deinit_default_ctrl (ctrl_t ctrl); +void dirmngr_sighup_action (void); +const char* dirmngr_get_current_socket_name (void); + + +/*-- Various housekeeping functions. --*/ +void ks_hkp_housekeeping (time_t curtime); + + +/*-- server.c --*/ +ldap_server_t get_ldapservers_from_ctrl (ctrl_t ctrl); +ksba_cert_t get_cert_local (ctrl_t ctrl, const char *issuer); +ksba_cert_t get_issuing_cert_local (ctrl_t ctrl, const char *issuer); +ksba_cert_t get_cert_local_ski (ctrl_t ctrl, + const char *name, ksba_sexp_t keyid); +gpg_error_t get_istrusted_from_client (ctrl_t ctrl, const char *hexfpr); +int dirmngr_assuan_log_monitor (assuan_context_t ctx, unsigned int cat, + const char *msg); +void start_command_handler (gnupg_fd_t fd); +gpg_error_t dirmngr_status (ctrl_t ctrl, const char *keyword, ...); +gpg_error_t dirmngr_status_help (ctrl_t ctrl, const char *text); +gpg_error_t dirmngr_tick (ctrl_t ctrl); + + +/*-- loadswdb.c --*/ +gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force); + + +#endif /*DIRMNGR_H*/ diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c new file mode 100644 index 0000000..a0acb8e --- /dev/null +++ b/dirmngr/dirmngr_ldap.c @@ -0,0 +1,732 @@ +/* dirmngr-ldap.c - The LDAP helper for dirmngr. + * Copyright (C) 2004 g10 Code GmbH + * Copyright (C) 2010 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +# include +#endif +#include +#include +#include +#include +#ifndef USE_LDAPWRAPPER +# include +#endif + +#ifdef HAVE_W32_SYSTEM +# include +# include +# include +# include +# include "ldap-url.h" +#else + /* For OpenLDAP, to enable the API that we're using. */ +# define LDAP_DEPRECATED 1 +# include +#endif + + +#include +#include "../common/logging.h" +#include "../common/argparse.h" +#include "../common/stringhelp.h" +#include "../common/mischelp.h" +#include "../common/strlist.h" + +#include "i18n.h" +#include "util.h" +#include "../common/init.h" + +/* With the ldap wrapper, there is no need for the npth_unprotect and leave + functions; thus we redefine them to nops. If we are not using the + ldap wrapper process we need to include the prototype for our + module's main function. */ +#ifdef USE_LDAPWRAPPER +static void npth_unprotect (void) { } +static void npth_protect (void) { } +#else +# include "./ldap-wrapper.h" +#endif + +#ifdef HAVE_W32CE_SYSTEM +# include "w32-ldap-help.h" +# define my_ldap_init(a,b) \ + _dirmngr_ldap_init ((a), (b)) +# define my_ldap_simple_bind_s(a,b,c) \ + _dirmngr_ldap_simple_bind_s ((a),(b),(c)) +# define my_ldap_search_st(a,b,c,d,e,f,g,h) \ + _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h)) +# define my_ldap_first_attribute(a,b,c) \ + _dirmngr_ldap_first_attribute ((a),(b),(c)) +# define my_ldap_next_attribute(a,b,c) \ + _dirmngr_ldap_next_attribute ((a),(b),(c)) +# define my_ldap_get_values_len(a,b,c) \ + _dirmngr_ldap_get_values_len ((a),(b),(c)) +# define my_ldap_free_attr(a) \ + xfree ((a)) +#else +# define my_ldap_init(a,b) ldap_init ((a), (b)) +# define my_ldap_simple_bind_s(a,b,c) ldap_simple_bind_s ((a), (b), (c)) +# define my_ldap_search_st(a,b,c,d,e,f,g,h) \ + ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h)) +# define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c)) +# define my_ldap_next_attribute(a,b,c) ldap_next_attribute ((a),(b),(c)) +# define my_ldap_get_values_len(a,b,c) ldap_get_values_len ((a),(b),(c)) +# define my_ldap_free_attr(a) ldap_memfree ((a)) +#endif + +#ifdef HAVE_W32_SYSTEM + typedef LDAP_TIMEVAL my_ldap_timeval_t; +#else + typedef struct timeval my_ldap_timeval_t; +#endif + +#define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */ + + +/* Constants for the options. */ +enum + { + oQuiet = 'q', + oVerbose = 'v', + + oTimeout = 500, + oMulti, + oProxy, + oHost, + oPort, + oUser, + oPass, + oEnvPass, + oDN, + oFilter, + oAttr, + + oOnlySearchTimeout, + oLogWithPID + }; + + +/* The list of options as used by the argparse.c code. */ +static ARGPARSE_OPTS opts[] = { + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oTimeout, "timeout", 1, N_("|N|set LDAP timeout to N seconds")}, + { oMulti, "multi", 0, N_("return all values in" + " a record oriented format")}, + { oProxy, "proxy", 2, + N_("|NAME|ignore host part and connect through NAME")}, + { oHost, "host", 2, N_("|NAME|connect to host NAME")}, + { oPort, "port", 1, N_("|N|connect to port N")}, + { oUser, "user", 2, N_("|NAME|use user NAME for authentication")}, + { oPass, "pass", 2, N_("|PASS|use password PASS" + " for authentication")}, + { oEnvPass, "env-pass", 0, N_("take password from $DIRMNGR_LDAP_PASS")}, + { oDN, "dn", 2, N_("|STRING|query DN STRING")}, + { oFilter, "filter", 2, N_("|STRING|use STRING as filter expression")}, + { oAttr, "attr", 2, N_("|STRING|return the attribute STRING")}, + { oOnlySearchTimeout, "only-search-timeout", 0, "@"}, + { oLogWithPID,"log-with-pid", 0, "@"}, + { 0, NULL, 0, NULL } +}; + + +/* A structure with module options. This is not a static variable + because if we are not build as a standalone binary, each thread + using this module needs to handle its own values. */ +struct my_opt_s +{ + int quiet; + int verbose; + my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */ + unsigned int alarm_timeout; /* And for the alarm based timeout. */ + int multi; + + estream_t outstream; /* Send output to this stream. */ + + /* Note that we can't use const for the strings because ldap_* are + not defined that way. */ + char *proxy; /* Host and Port override. */ + char *user; /* Authentication user. */ + char *pass; /* Authentication password. */ + char *host; /* Override host. */ + int port; /* Override port. */ + char *dn; /* Override DN. */ + char *filter;/* Override filter. */ + char *attr; /* Override attribute. */ +}; +typedef struct my_opt_s *my_opt_t; + + +/* Prototypes. */ +#ifndef HAVE_W32_SYSTEM +static void catch_alarm (int dummy); +#endif +static int process_url (my_opt_t myopt, const char *url); + + + +/* Function called by argparse.c to display information. */ +#ifdef USE_LDAPWRAPPER +static const char * +my_strusage (int level) +{ + const char *p; + + switch(level) + { + case 11: p = "dirmngr_ldap (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + case 49: p = PACKAGE_BUGREPORT; break; + case 1: + case 40: p = + _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n"); + break; + case 41: p = + _("Syntax: dirmngr_ldap [options] [URL]\n" + "Internal LDAP helper for Dirmngr\n" + "Interface and options may change without notice\n"); + break; + + default: p = NULL; + } + return p; +} +#endif /*!USE_LDAPWRAPPER*/ + + +int +#ifdef USE_LDAPWRAPPER +main (int argc, char **argv) +#else +ldap_wrapper_main (char **argv, estream_t outstream) +#endif +{ +#ifndef USE_LDAPWRAPPER + int argc; +#endif + ARGPARSE_ARGS pargs; + int any_err = 0; + char *p; + int only_search_timeout = 0; + struct my_opt_s my_opt_buffer; + my_opt_t myopt = &my_opt_buffer; + char *malloced_buffer1 = NULL; + + memset (&my_opt_buffer, 0, sizeof my_opt_buffer); + + early_system_init (); + +#ifdef USE_LDAPWRAPPER + set_strusage (my_strusage); + log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX); + + /* Setup I18N and common subsystems. */ + i18n_init(); + + init_common_subsystems (&argc, &argv); + + es_set_binary (es_stdout); + myopt->outstream = es_stdout; +#else /*!USE_LDAPWRAPPER*/ + myopt->outstream = outstream; + for (argc=0; argv[argc]; argc++) + ; +#endif /*!USE_LDAPWRAPPER*/ + + /* LDAP defaults */ + myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT; + myopt->timeout.tv_usec = 0; + myopt->alarm_timeout = 0; + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* Do not remove the args. */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: myopt->verbose++; break; + case oQuiet: myopt->quiet++; break; + case oTimeout: + myopt->timeout.tv_sec = pargs.r.ret_int; + myopt->timeout.tv_usec = 0; + myopt->alarm_timeout = pargs.r.ret_int; + break; + case oOnlySearchTimeout: only_search_timeout = 1; break; + case oMulti: myopt->multi = 1; break; + case oUser: myopt->user = pargs.r.ret_str; break; + case oPass: myopt->pass = pargs.r.ret_str; break; + case oEnvPass: + myopt->pass = getenv ("DIRMNGR_LDAP_PASS"); + break; + case oProxy: myopt->proxy = pargs.r.ret_str; break; + case oHost: myopt->host = pargs.r.ret_str; break; + case oPort: myopt->port = pargs.r.ret_int; break; + case oDN: myopt->dn = pargs.r.ret_str; break; + case oFilter: myopt->filter = pargs.r.ret_str; break; + case oAttr: myopt->attr = pargs.r.ret_str; break; + case oLogWithPID: + { + unsigned int oldflags; + log_get_prefix (&oldflags); + log_set_prefix (NULL, oldflags | GPGRT_LOG_WITH_PID); + } + break; + + default : +#ifdef USE_LDAPWRAPPER + pargs.err = ARGPARSE_PRINT_ERROR; +#else + pargs.err = ARGPARSE_PRINT_WARNING; /* No exit() please. */ +#endif + break; + } + } + + if (only_search_timeout) + myopt->alarm_timeout = 0; + + if (myopt->proxy) + { + malloced_buffer1 = xtrystrdup (myopt->proxy); + if (!malloced_buffer1) + { + log_error ("error copying string: %s\n", strerror (errno)); + return 1; + } + myopt->host = malloced_buffer1; + p = strchr (myopt->host, ':'); + if (p) + { + *p++ = 0; + myopt->port = atoi (p); + } + if (!myopt->port) + myopt->port = 389; /* make sure ports gets overridden. */ + } + + if (myopt->port < 0 || myopt->port > 65535) + log_error (_("invalid port number %d\n"), myopt->port); + +#ifdef USE_LDAPWRAPPER + if (log_get_errorcount (0)) + exit (2); + if (argc < 1) + usage (1); +#else + /* All passed arguments should be fine in this case. */ + assert (argc); +#endif + +#ifdef USE_LDAPWRAPPER + if (myopt->alarm_timeout) + { +#ifndef HAVE_W32_SYSTEM +# if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION) + struct sigaction act; + + act.sa_handler = catch_alarm; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + if (sigaction (SIGALRM,&act,NULL)) +# else + if (signal (SIGALRM, catch_alarm) == SIG_ERR) +# endif + log_fatal ("unable to register timeout handler\n"); +#endif + } +#endif /*USE_LDAPWRAPPER*/ + + for (; argc; argc--, argv++) + if (process_url (myopt, *argv)) + any_err = 1; + + xfree (malloced_buffer1); + return any_err; +} + +#ifndef HAVE_W32_SYSTEM +static void +catch_alarm (int dummy) +{ + (void)dummy; + _exit (10); +} +#endif + +static void +set_timeout (my_opt_t myopt) +{ +#ifdef HAVE_W32_SYSTEM + /* FIXME for W32. */ + (void)myopt; +#else + if (myopt->alarm_timeout) + alarm (myopt->alarm_timeout); +#endif +} + + +/* Helper for fetch_ldap(). */ +static int +print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr) +{ + LDAPMessage *item; + int any = 0; + + for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect (); + item; + npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ()) + { + BerElement *berctx; + char *attr; + + if (myopt->verbose > 1) + log_info (_("scanning result for attribute '%s'\n"), + want_attr? want_attr : "[all]"); + + if (myopt->multi) + { /* Write item marker. */ + if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + return -1; + } + } + + + for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx), + npth_protect (); + attr; + npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx), + npth_protect ()) + { + struct berval **values; + int idx; + + if (myopt->verbose > 1) + log_info (_(" available attribute '%s'\n"), attr); + + set_timeout (myopt); + + /* I case we want only one attribute we do a case + insensitive compare without the optional extension + (i.e. ";binary"). Case insensitive is not really correct + but the best we can do. */ + if (want_attr) + { + char *cp1, *cp2; + int cmpres; + + cp1 = strchr (want_attr, ';'); + if (cp1) + *cp1 = 0; + cp2 = strchr (attr, ';'); + if (cp2) + *cp2 = 0; + cmpres = ascii_strcasecmp (want_attr, attr); + if (cp1) + *cp1 = ';'; + if (cp2) + *cp2 = ';'; + if (cmpres) + { + my_ldap_free_attr (attr); + continue; /* Not found: Try next attribute. */ + } + } + + npth_unprotect (); + values = my_ldap_get_values_len (ld, item, attr); + npth_protect (); + + if (!values) + { + if (myopt->verbose) + log_info (_("attribute '%s' not found\n"), attr); + my_ldap_free_attr (attr); + continue; + } + + if (myopt->verbose) + { + log_info (_("found attribute '%s'\n"), attr); + if (myopt->verbose > 1) + for (idx=0; values[idx]; idx++) + log_info (" length[%d]=%d\n", + idx, (int)values[0]->bv_len); + + } + + if (myopt->multi) + { /* Write attribute marker. */ + unsigned char tmp[5]; + size_t n = strlen (attr); + + tmp[0] = 'A'; + tmp[1] = (n >> 24); + tmp[2] = (n >> 16); + tmp[3] = (n >> 8); + tmp[4] = (n); + if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1 + || es_fwrite (attr, n, 1, myopt->outstream) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + my_ldap_free_attr (attr); + ber_free (berctx, 0); + return -1; + } + } + + for (idx=0; values[idx]; idx++) + { + if (myopt->multi) + { /* Write value marker. */ + unsigned char tmp[5]; + size_t n = values[0]->bv_len; + + tmp[0] = 'V'; + tmp[1] = (n >> 24); + tmp[2] = (n >> 16); + tmp[3] = (n >> 8); + tmp[4] = (n); + + if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + my_ldap_free_attr (attr); + ber_free (berctx, 0); + return -1; + } + } + + if (es_fwrite (values[0]->bv_val, values[0]->bv_len, + 1, myopt->outstream) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + my_ldap_free_attr (attr); + ber_free (berctx, 0); + return -1; + } + + any = 1; + if (!myopt->multi) + break; /* Print only the first value. */ + } + ldap_value_free_len (values); + my_ldap_free_attr (attr); + if (want_attr || !myopt->multi) + break; /* We only want to return the first attribute. */ + } + ber_free (berctx, 0); + } + + if (myopt->verbose > 1 && any) + log_info ("result has been printed\n"); + + return any?0:-1; +} + + + +/* Helper for the URL based LDAP query. */ +static int +fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp) +{ + LDAP *ld; + LDAPMessage *msg; + int rc = 0; + char *host, *dn, *filter, *attrs[2], *attr; + int port; + int ret; + + host = myopt->host? myopt->host : ludp->lud_host; + port = myopt->port? myopt->port : ludp->lud_port; + dn = myopt->dn? myopt->dn : ludp->lud_dn; + filter = myopt->filter? myopt->filter : ludp->lud_filter; + attrs[0] = myopt->attr? myopt->attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL; + attrs[1] = NULL; + attr = attrs[0]; + + if (!port) + port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389; + + if (myopt->verbose) + { + log_info (_("processing url '%s'\n"), url); + if (myopt->user) + log_info (_(" user '%s'\n"), myopt->user); + if (myopt->pass) + log_info (_(" pass '%s'\n"), *myopt->pass?"*****":""); + if (host) + log_info (_(" host '%s'\n"), host); + log_info (_(" port %d\n"), port); + if (dn) + log_info (_(" DN '%s'\n"), dn); + if (filter) + log_info (_(" filter '%s'\n"), filter); + if (myopt->multi && !myopt->attr && ludp->lud_attrs) + { + int i; + for (i=0; ludp->lud_attrs[i]; i++) + log_info (_(" attr '%s'\n"), ludp->lud_attrs[i]); + } + else if (attr) + log_info (_(" attr '%s'\n"), attr); + } + + + if (!host || !*host) + { + log_error (_("no host name in '%s'\n"), url); + return -1; + } + if (!myopt->multi && !attr) + { + log_error (_("no attribute given for query '%s'\n"), url); + return -1; + } + + if (!myopt->multi && !myopt->attr + && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1]) + log_info (_("WARNING: using first attribute only\n")); + + + set_timeout (myopt); + npth_unprotect (); + ld = my_ldap_init (host, port); + npth_protect (); + if (!ld) + { + log_error (_("LDAP init to '%s:%d' failed: %s\n"), + host, port, strerror (errno)); + return -1; + } + npth_unprotect (); + /* Fixme: Can we use MYOPT->user or is it shared with other theeads?. */ + ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass); + npth_protect (); +#ifdef LDAP_VERSION3 + if (ret == LDAP_PROTOCOL_ERROR) + { + /* Protocol error could mean that the server only supports v3. */ + int version = LDAP_VERSION3; + if (myopt->verbose) + log_info ("protocol error; retrying bind with v3 protocol\n"); + npth_unprotect (); + ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version); + ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass); + npth_protect (); + } +#endif + if (ret) + { + log_error (_("binding to '%s:%d' failed: %s\n"), + host, port, ldap_err2string (ret)); + ldap_unbind (ld); + return -1; + } + + set_timeout (myopt); + npth_unprotect (); + rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter, + myopt->multi && !myopt->attr && ludp->lud_attrs? + ludp->lud_attrs:attrs, + 0, + &myopt->timeout, &msg); + npth_protect (); + if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi) + { + if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1) + { + log_error (_("error writing to stdout: %s\n"), strerror (errno)); + return -1; + } + } + else if (rc) + { +#ifdef HAVE_W32CE_SYSTEM + log_error ("searching '%s' failed: %d\n", url, rc); +#else + log_error (_("searching '%s' failed: %s\n"), + url, ldap_err2string (rc)); +#endif + if (rc != LDAP_NO_SUCH_OBJECT) + { + /* FIXME: Need deinit (ld)? */ + /* Hmmm: Do we need to released MSG in case of an error? */ + return -1; + } + } + + rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr); + + ldap_msgfree (msg); + ldap_unbind (ld); + return rc; +} + + + + +/* Main processing. Take the URL and run the LDAP query. The result + is printed to stdout, errors are logged to the log stream. */ +static int +process_url (my_opt_t myopt, const char *url) +{ + int rc; + LDAPURLDesc *ludp = NULL; + + + if (!ldap_is_ldap_url (url)) + { + log_error (_("'%s' is not an LDAP URL\n"), url); + return -1; + } + + if (ldap_url_parse (url, &ludp)) + { + log_error (_("'%s' is an invalid LDAP URL\n"), url); + return -1; + } + + rc = fetch_ldap (myopt, url, ludp); + + ldap_free_urldesc (ludp); + return rc; +} diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c new file mode 100644 index 0000000..491fcce --- /dev/null +++ b/dirmngr/dns-stuff.c @@ -0,0 +1,2012 @@ +/* dns-stuff.c - DNS related code including CERT RR (rfc-4398) + * Copyright (C) 2003, 2005, 2006, 2009 Free Software Foundation, Inc. + * Copyright (C) 2005, 2006, 2009, 2015. 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#ifdef HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +# include +#else +# if HAVE_SYSTEM_RESOLVER +# include +# include +# include +# endif +# include +#endif +#include +#include + + +/* William Ahern's DNS library, included as a source copy. */ +#ifdef USE_LIBDNS +# include "dns.h" +#endif + +/* dns.c has a dns_p_free but it is not exported. We use our own + * wrapper here so that we do not accidentally use xfree which would + * be wrong for dns.c allocated data. */ +#define dns_free(a) free ((a)) + + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +# undef USE_NPTH +#endif +#ifdef USE_NPTH +# include +#endif + +#include "./dirmngr-err.h" +#include "util.h" +#include "host2net.h" +#include "dns-stuff.h" + +#ifdef USE_NPTH +# define my_unprotect() npth_unprotect () +# define my_protect() npth_protect () +#else +# define my_unprotect() do { } while(0) +# define my_protect() do { } while(0) +#endif + +/* We allow the use of 0 instead of AF_UNSPEC - check this assumption. */ +#if AF_UNSPEC != 0 +# error AF_UNSPEC does not have the value 0 +#endif + +/* Windows does not support the AI_ADDRCONFIG flag - use zero instead. */ +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif + +/* Not every installation has gotten around to supporting SRVs or + CERTs yet... */ +#ifndef T_SRV +#define T_SRV 33 +#endif +#ifndef T_CERT +# define T_CERT 37 +#endif + +/* The standard SOCKS and TOR ports. */ +#define SOCKS_PORT 1080 +#define TOR_PORT 9050 +#define TOR_PORT2 9150 /* (Used by the Tor browser) */ + + +/* The default nameserver used in Tor mode. */ +#define DEFAULT_NAMESERVER "8.8.8.8" + +/* The default timeout in seconds for libdns requests. */ +#define DEFAULT_TIMEOUT 30 + + +/* Two flags to enable verbose and debug mode. */ +static int opt_verbose; +static int opt_debug; + +/* The timeout in seconds for libdns requests. */ +static int opt_timeout; + +/* If set force the use of the standard resolver. */ +static int standard_resolver; + +/* If set use recursive resolver when available. */ +static int recursive_resolver; + +/* If set Tor mode shall be used. */ +static int tor_mode; + +/* A string with the nameserver IP address used with Tor. + (40 should be sufficient for v6 but we add some extra for a scope.) */ +static char tor_nameserver[40+20]; + +/* Two strings to hold the credentials presented to Tor. */ +static char tor_socks_user[30]; +static char tor_socks_password[20]; + + +#ifdef USE_LIBDNS +/* Libdns gobal data. */ +struct libdns_s +{ + struct dns_resolv_conf *resolv_conf; + struct dns_hosts *hosts; + struct dns_hints *hints; + + struct sockaddr_storage socks_host; +} libdns; + +/* If this flag is set, libdns shall be reinited for the next use. */ +static int libdns_reinit_pending; + +/* The Tor port to be used. */ +static int libdns_tor_port; + +#endif /*USE_LIBDNS*/ + + +/* Calling this function with YES set to True forces the use of the + * standard resolver even if dirmngr has been built with support for + * an alternative resolver. */ +void +enable_standard_resolver (int yes) +{ + standard_resolver = yes; +} + + +/* Return true if the standard resolver is used. */ +int +standard_resolver_p (void) +{ + return standard_resolver; +} + + +/* Calling this function with YES switches libdns into recursive mode. + * It has no effect on the standard resolver. */ +void +enable_recursive_resolver (int yes) +{ + recursive_resolver = yes; + libdns_reinit_pending = 1; +} + + +/* Return true iff the recursive resolver is used. */ +int +recursive_resolver_p (void) +{ +#if USE_LIBDNS + return !standard_resolver && recursive_resolver; +#else + return 0; +#endif +} + + +/* Sets the module in Tor mode. Returns 0 is this is possible or an + error code. */ +gpg_error_t +enable_dns_tormode (int new_circuit) +{ + if (!*tor_socks_user || new_circuit) + { + static unsigned int counter; + + gpgrt_snprintf (tor_socks_user, sizeof tor_socks_user, + "dirmngr-%lu", (unsigned long)getpid ()); + gpgrt_snprintf (tor_socks_password, sizeof tor_socks_password, + "p%u", counter); + counter++; + } + tor_mode = 1; + return 0; +} + + +/* Set verbosity and debug mode for this module. */ +void +set_dns_verbose (int verbose, int debug) +{ + opt_verbose = verbose; + opt_debug = debug; +} + + +/* Set the timeout for libdns requests to SECONDS. A value of 0 sets + * the default timeout and values are capped at 10 minutes. */ +void +set_dns_timeout (int seconds) +{ + if (!seconds) + seconds = DEFAULT_TIMEOUT; + else if (seconds < 1) + seconds = 1; + else if (seconds > 600) + seconds = 600; + + opt_timeout = seconds; +} + + +/* Change the default IP address of the nameserver to IPADDR. The + address needs to be a numerical IP address and will be used for the + next DNS query. Note that this is only used in Tor mode. */ +void +set_dns_nameserver (const char *ipaddr) +{ + strncpy (tor_nameserver, ipaddr? ipaddr : DEFAULT_NAMESERVER, + sizeof tor_nameserver -1); + tor_nameserver[sizeof tor_nameserver -1] = 0; + libdns_reinit_pending = 1; + libdns_tor_port = 0; /* Start again with the default port. */ +} + + +/* Free an addressinfo linked list as returned by resolve_dns_name. */ +void +free_dns_addrinfo (dns_addrinfo_t ai) +{ + while (ai) + { + dns_addrinfo_t next = ai->next; + xfree (ai); + ai = next; + } +} + + +#ifndef HAVE_W32_SYSTEM +/* Return H_ERRNO mapped to a gpg-error code. Will never return 0. */ +static gpg_error_t +get_h_errno_as_gpg_error (void) +{ + gpg_err_code_t ec; + + switch (h_errno) + { + case HOST_NOT_FOUND: ec = GPG_ERR_UNKNOWN_HOST; break; + case TRY_AGAIN: ec = GPG_ERR_TRY_LATER; break; + case NO_RECOVERY: ec = GPG_ERR_SERVER_FAILED; break; + case NO_DATA: ec = GPG_ERR_NO_DATA; break; + default: ec = GPG_ERR_UNKNOWN_ERRNO; break; + } + return gpg_error (ec); +} +#endif /*!HAVE_W32_SYSTEM*/ + +static gpg_error_t +map_eai_to_gpg_error (int ec) +{ + gpg_error_t err; + + switch (ec) + { + case EAI_AGAIN: err = gpg_error (GPG_ERR_EAGAIN); break; + case EAI_BADFLAGS: err = gpg_error (GPG_ERR_INV_FLAG); break; + case EAI_FAIL: err = gpg_error (GPG_ERR_SERVER_FAILED); break; + case EAI_MEMORY: err = gpg_error (GPG_ERR_ENOMEM); break; +#ifdef EAI_NODATA + case EAI_NODATA: err = gpg_error (GPG_ERR_NO_DATA); break; +#endif + case EAI_NONAME: err = gpg_error (GPG_ERR_NO_NAME); break; + case EAI_SERVICE: err = gpg_error (GPG_ERR_NOT_SUPPORTED); break; + case EAI_FAMILY: err = gpg_error (GPG_ERR_EAFNOSUPPORT); break; + case EAI_SOCKTYPE: err = gpg_error (GPG_ERR_ESOCKTNOSUPPORT); break; +#ifndef HAVE_W32_SYSTEM +# ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY:err = gpg_error (GPG_ERR_EADDRNOTAVAIL); break; +# endif + case EAI_SYSTEM: err = gpg_error_from_syserror (); break; +#endif + default: err = gpg_error (GPG_ERR_UNKNOWN_ERRNO); break; + } + return err; +} + + +#ifdef USE_LIBDNS +static gpg_error_t +libdns_error_to_gpg_error (int serr) +{ + gpg_err_code_t ec; + + switch (serr) + { + case 0: ec = 0; break; + + case DNS_ENOBUFS: ec = GPG_ERR_BUFFER_TOO_SHORT; break; + case DNS_EILLEGAL: ec = GPG_ERR_INV_OBJ; break; + case DNS_EORDER: ec = GPG_ERR_INV_ORDER; break; + case DNS_ESECTION: ec = GPG_ERR_DNS_SECTION; break; + case DNS_EUNKNOWN: ec = GPG_ERR_DNS_UNKNOWN; break; + case DNS_EADDRESS: ec = GPG_ERR_DNS_ADDRESS; break; + case DNS_ENOQUERY: ec = GPG_ERR_DNS_NO_QUERY; break; + case DNS_ENOANSWER:ec = GPG_ERR_DNS_NO_ANSWER; break; + case DNS_EFETCHED: ec = GPG_ERR_ALREADY_FETCHED; break; + case DNS_ESERVICE: ec = GPG_ERR_NOT_SUPPORTED; break; + case DNS_ENONAME: ec = GPG_ERR_NO_NAME; break; + case DNS_EFAIL: ec = GPG_ERR_SERVER_FAILED; break; + case DNS_ECONNFIN: ec = GPG_ERR_DNS_CLOSED; break; + case DNS_EVERIFY: ec = GPG_ERR_DNS_VERIFY; break; + + default: + if (serr >= 0) + ec = gpg_err_code_from_errno (serr); + else + ec = GPG_ERR_DNS_UNKNOWN; + break; + } + return gpg_error (ec); +} +#endif /*USE_LIBDNS*/ + + +#ifdef USE_LIBDNS +/* Initialize libdns. Returns 0 on success; prints a diagnostic and + * returns an error code on failure. */ +static gpg_error_t +libdns_init (void) +{ + gpg_error_t err; + struct libdns_s ld; + int derr; + char *cfgstr = NULL; + + if (libdns.resolv_conf) + return 0; /* Already initialized. */ + + memset (&ld, 0, sizeof ld); + + ld.resolv_conf = dns_resconf_open (&derr); + if (!ld.resolv_conf) + { + err = libdns_error_to_gpg_error (derr); + log_error ("failed to allocate DNS resconf object: %s\n", + gpg_strerror (err)); + goto leave; + } + + if (tor_mode) + { + if (!*tor_nameserver) + set_dns_nameserver (NULL); + + if (!libdns_tor_port) + libdns_tor_port = TOR_PORT; + + cfgstr = xtryasprintf ("[%s]:53", tor_nameserver); + if (!cfgstr) + err = gpg_error_from_syserror (); + else + err = libdns_error_to_gpg_error + (dns_resconf_pton (&ld.resolv_conf->nameserver[0], cfgstr)); + if (err) + log_error ("failed to set nameserver '%s': %s\n", + cfgstr, gpg_strerror (err)); + if (err) + goto leave; + + ld.resolv_conf->options.tcp = DNS_RESCONF_TCP_SOCKS; + + xfree (cfgstr); + cfgstr = xtryasprintf ("[%s]:%d", "127.0.0.1", libdns_tor_port); + if (!cfgstr) + err = gpg_error_from_syserror (); + else + err = libdns_error_to_gpg_error + (dns_resconf_pton (&ld.socks_host, cfgstr)); + if (err) + { + log_error ("failed to set socks server '%s': %s\n", + cfgstr, gpg_strerror (err)); + goto leave; + } + } + else + { +#ifdef HAVE_W32_SYSTEM + ULONG ninfo_len; + PFIXED_INFO ninfo; + PIP_ADDR_STRING pip; + int idx; + + ninfo_len = 2048; + ninfo = xtrymalloc (ninfo_len); + if (!ninfo) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (GetNetworkParams (ninfo, &ninfo_len)) + { + log_error ("GetNetworkParms failed: %s\n", w32_strerror (-1)); + err = gpg_error (GPG_ERR_GENERAL); + xfree (ninfo); + goto leave; + } + + for (idx=0, pip = &(ninfo->DnsServerList); + pip && idx < DIM (ld.resolv_conf->nameserver); + pip = pip->Next) + { + if (opt_debug) + log_debug ("dns: dnsserver[%d] '%s'\n", idx, pip->IpAddress.String); + err = libdns_error_to_gpg_error + (dns_resconf_pton (&ld.resolv_conf->nameserver[idx], + pip->IpAddress.String)); + if (err) + log_error ("failed to set nameserver[%d] '%s': %s\n", + idx, pip->IpAddress.String, gpg_strerror (err)); + else + idx++; + } + xfree (ninfo); + +#else /* Unix */ + const char *fname; + + fname = "/etc/resolv.conf"; + err = libdns_error_to_gpg_error + (dns_resconf_loadpath (ld.resolv_conf, fname)); + if (err) + { + log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err)); + goto leave; + } + + fname = "/etc/nsswitch.conf"; + err = libdns_error_to_gpg_error + (dns_nssconf_loadpath (ld.resolv_conf, fname)); + if (err) + { + log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err)); + goto leave; + } + +#endif /* Unix */ + } + + ld.hosts = dns_hosts_open (&derr); + if (!ld.hosts) + { + log_error ("failed to load hosts file: %s\n", gpg_strerror (err)); + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* dns_hints_local for stub mode, dns_hints_root for recursive. */ + ld.hints = (recursive_resolver + ? dns_hints_root (ld.resolv_conf, &derr) + : dns_hints_local (ld.resolv_conf, &derr)); + if (!ld.hints) + { + log_error ("failed to load DNS hints: %s\n", gpg_strerror (err)); + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* All fine. Make the data global. */ + libdns = ld; + + leave: + xfree (cfgstr); + return err; +} +#endif /*USE_LIBDNS*/ + + +#ifdef USE_LIBDNS +/* Deinitialize libdns. */ +static void +libdns_deinit (void) +{ + struct libdns_s ld; + + if (!libdns.resolv_conf) + return; /* Not initialized. */ + + ld = libdns; + memset (&libdns, 0, sizeof libdns); + dns_hints_close (ld.hints); + dns_hosts_close (ld.hosts); + dns_resconf_close (ld.resolv_conf); +} +#endif /*USE_LIBDNS*/ + + +/* SIGHUP action handler for this module. With FORCE set objects are + * all immediately released. */ +void +reload_dns_stuff (int force) +{ + if (force) + { +#ifdef USE_LIBDNS + libdns_deinit (); +#endif + libdns_reinit_pending = 0; + } + else + libdns_reinit_pending = 1; +} + + +#ifdef USE_LIBDNS +/* + * Initialize libdns if needed and open a dns_resolver context. + * Returns 0 on success and stores the new context at R_RES. On + * failure an error code is returned and NULL stored at R_RES. + */ +static gpg_error_t +libdns_res_open (struct dns_resolver **r_res) +{ + gpg_error_t err; + struct dns_resolver *res; + int derr; + + *r_res = NULL; + + if (libdns_reinit_pending) + { + libdns_reinit_pending = 0; + libdns_deinit (); + } + + err = libdns_init (); + if (err) + return err; + + if (!opt_timeout) + set_dns_timeout (0); + + res = dns_res_open (libdns.resolv_conf, libdns.hosts, libdns.hints, NULL, + dns_opts (.socks_host = &libdns.socks_host, + .socks_user = tor_socks_user, + .socks_password = tor_socks_password ), + &derr); + if (!res) + return libdns_error_to_gpg_error (derr); + + *r_res = res; + return 0; +} +#endif /*USE_LIBDNS*/ + + +#ifdef USE_LIBDNS +/* Helper to test whether we need totry again after having swicthed + * the Tor port. */ +static int +libdns_switch_port_p (gpg_error_t err) +{ + if (tor_mode && gpg_err_code (err) == GPG_ERR_ECONNREFUSED + && libdns_tor_port == TOR_PORT) + { + /* Switch port and try again. */ + if (opt_debug) + log_debug ("dns: switching from SOCKS port %d to %d\n", + TOR_PORT, TOR_PORT2); + libdns_tor_port = TOR_PORT2; + libdns_reinit_pending = 1; + return 1; + } + return 0; +} +#endif /*USE_LIBDNS*/ + + +#ifdef USE_LIBDNS +/* Wrapper around dns_res_submit. */ +static gpg_error_t +libdns_res_submit (struct dns_resolver *res, const char *qname, + enum dns_type qtype, enum dns_class qclass) +{ + return libdns_error_to_gpg_error (dns_res_submit (res, qname, qtype, qclass)); +} +#endif /*USE_LIBDNS*/ + + +#ifdef USE_LIBDNS +/* Standard event handling loop. */ +gpg_error_t +libdns_res_wait (struct dns_resolver *res) +{ + gpg_error_t err; + + while ((err = libdns_error_to_gpg_error (dns_res_check (res))) + && gpg_err_code (err) == GPG_ERR_EAGAIN) + { + if (dns_res_elapsed (res) > opt_timeout) + { + err = gpg_error (GPG_ERR_DNS_TIMEOUT); + break; + } + + my_unprotect (); + dns_res_poll (res, 1); + my_protect (); + } + + return err; +} +#endif /*USE_LIBDNS*/ + + +#ifdef USE_LIBDNS +static gpg_error_t +resolve_name_libdns (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_dai, char **r_canonname) +{ + gpg_error_t err; + dns_addrinfo_t daihead = NULL; + dns_addrinfo_t dai; + struct dns_resolver *res = NULL; + struct dns_addrinfo *ai = NULL; + struct addrinfo hints; + struct addrinfo *ent; + char portstr_[21]; + char *portstr = NULL; + int derr; + + *r_dai = NULL; + if (r_canonname) + *r_canonname = NULL; + + memset (&hints, 0, sizeof hints); + hints.ai_family = want_family; + hints.ai_socktype = want_socktype; + hints.ai_flags = AI_ADDRCONFIG; + if (r_canonname) + hints.ai_flags |= AI_CANONNAME; + + if (port) + { + snprintf (portstr_, sizeof portstr_, "%hu", port); + portstr = portstr_; + } + + err = libdns_res_open (&res); + if (err) + goto leave; + + ai = dns_ai_open (name, portstr, 0, &hints, res, &derr); + if (!ai) + { + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* Loop over all records. */ + for (;;) + { + err = libdns_error_to_gpg_error (dns_ai_nextent (&ent, ai)); + if (gpg_err_code (err) == GPG_ERR_ENOENT) + { + if (daihead) + err = 0; /* We got some results, we're good. */ + break; /* Ready. */ + } + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + { + if (dns_ai_elapsed (ai) > opt_timeout) + { + err = gpg_error (GPG_ERR_DNS_TIMEOUT); + goto leave; + } + + my_unprotect (); + dns_ai_poll (ai, 1); + my_protect (); + continue; + } + if (err) + goto leave; + + if (r_canonname && ! *r_canonname && ent && ent->ai_canonname) + { + *r_canonname = xtrystrdup (ent->ai_canonname); + if (!*r_canonname) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + dai = xtrymalloc (sizeof *dai + ent->ai_addrlen -1); + if (dai == NULL) + { + err = gpg_error_from_syserror (); + goto leave; + } + + dai->family = ent->ai_family; + dai->socktype = ent->ai_socktype; + dai->protocol = ent->ai_protocol; + dai->addrlen = ent->ai_addrlen; + memcpy (dai->addr, ent->ai_addr, ent->ai_addrlen); + dai->next = daihead; + daihead = dai; + + xfree (ent); + } + + leave: + dns_ai_close (ai); + dns_res_close (res); + + if (err) + { + if (r_canonname) + { + xfree (*r_canonname); + *r_canonname = NULL; + } + free_dns_addrinfo (daihead); + } + else + *r_dai = daihead; + + return err; +} +#endif /*USE_LIBDNS*/ + + +/* Resolve a name using the standard system function. */ +static gpg_error_t +resolve_name_standard (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_dai, char **r_canonname) +{ + gpg_error_t err = 0; + dns_addrinfo_t daihead = NULL; + dns_addrinfo_t dai; + struct addrinfo *aibuf = NULL; + struct addrinfo hints, *ai; + char portstr[21]; + int ret; + + *r_dai = NULL; + if (r_canonname) + *r_canonname = NULL; + + memset (&hints, 0, sizeof hints); + hints.ai_family = want_family; + hints.ai_socktype = want_socktype; + hints.ai_flags = AI_ADDRCONFIG; + if (r_canonname) + hints.ai_flags |= AI_CANONNAME; + + if (port) + snprintf (portstr, sizeof portstr, "%hu", port); + else + *portstr = 0; + + /* We can't use the the AI_IDN flag because that does the conversion + using the current locale. However, GnuPG always used UTF-8. To + support IDN we would need to make use of the libidn API. */ + ret = getaddrinfo (name, *portstr? portstr : NULL, &hints, &aibuf); + if (ret) + { + aibuf = NULL; + err = map_eai_to_gpg_error (ret); + if (gpg_err_code (err) == GPG_ERR_NO_NAME) + { + /* There seems to be a bug in the glibc getaddrinfo function + if the CNAME points to a long list of A and AAAA records + in which case the function return NO_NAME. Let's do the + CNAME redirection again. */ + char *cname; + + if (get_dns_cname (name, &cname)) + goto leave; /* Still no success. */ + + ret = getaddrinfo (cname, *portstr? portstr : NULL, &hints, &aibuf); + xfree (cname); + if (ret) + { + aibuf = NULL; + err = map_eai_to_gpg_error (ret); + goto leave; + } + err = 0; /* Yep, now it worked. */ + } + else + goto leave; + } + + if (r_canonname && aibuf && aibuf->ai_canonname) + { + *r_canonname = xtrystrdup (aibuf->ai_canonname); + if (!*r_canonname) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + for (ai = aibuf; ai; ai = ai->ai_next) + { + if (ai->ai_family != AF_INET6 && ai->ai_family != AF_INET) + continue; + + dai = xtrymalloc (sizeof *dai + ai->ai_addrlen - 1); + dai->family = ai->ai_family; + dai->socktype = ai->ai_socktype; + dai->protocol = ai->ai_protocol; + dai->addrlen = ai->ai_addrlen; + memcpy (dai->addr, ai->ai_addr, ai->ai_addrlen); + dai->next = daihead; + daihead = dai; + } + + leave: + if (aibuf) + freeaddrinfo (aibuf); + if (err) + { + if (r_canonname) + { + xfree (*r_canonname); + *r_canonname = NULL; + } + free_dns_addrinfo (daihead); + } + else + *r_dai = daihead; + return err; +} + + +/* Resolve an address using the standard system function. */ +static gpg_error_t +resolve_addr_standard (const struct sockaddr *addr, int addrlen, + unsigned int flags, char **r_name) +{ + gpg_error_t err; + int ec; + char *buffer, *p; + int buflen; + + *r_name = NULL; + + buflen = NI_MAXHOST; + buffer = xtrymalloc (buflen + 2 + 1); + if (!buffer) + return gpg_error_from_syserror (); + + if ((flags & DNS_NUMERICHOST) || tor_mode) + ec = EAI_NONAME; + else + ec = getnameinfo (addr, addrlen, buffer, buflen, NULL, 0, NI_NAMEREQD); + + if (!ec && *buffer == '[') + ec = EAI_FAIL; /* A name may never start with a bracket. */ + else if (ec == EAI_NONAME) + { + p = buffer; + if (addr->sa_family == AF_INET6 && (flags & DNS_WITHBRACKET)) + { + *p++ = '['; + buflen -= 2; + } + ec = getnameinfo (addr, addrlen, p, buflen, NULL, 0, NI_NUMERICHOST); + if (!ec && addr->sa_family == AF_INET6 && (flags & DNS_WITHBRACKET)) + strcat (buffer, "]"); + } + + if (ec) + err = map_eai_to_gpg_error (ec); + else + { + p = xtryrealloc (buffer, strlen (buffer)+1); + if (!p) + err = gpg_error_from_syserror (); + else + { + buffer = p; + err = 0; + } + } + + if (err) + xfree (buffer); + else + *r_name = buffer; + + return err; +} + + +/* This a wrapper around getaddrinfo with slightly different semantics. + NAME is the name to resolve. + PORT is the requested port or 0. + WANT_FAMILY is either 0 (AF_UNSPEC), AF_INET6, or AF_INET4. + WANT_SOCKETTYPE is either SOCK_STREAM or SOCK_DGRAM. + + On success the result is stored in a linked list with the head + stored at the address R_AI; the caller must call gpg_addrinfo_free + on this. If R_CANONNAME is not NULL the official name of the host + is stored there as a malloced string; if that name is not available + NULL is stored. */ +gpg_error_t +resolve_dns_name (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_ai, char **r_canonname) +{ + gpg_error_t err; + +#ifdef USE_LIBDNS + if (!standard_resolver) + { + err = resolve_name_libdns (name, port, want_family, want_socktype, + r_ai, r_canonname); + if (err && libdns_switch_port_p (err)) + err = resolve_name_libdns (name, port, want_family, want_socktype, + r_ai, r_canonname); + } + else +#endif /*USE_LIBDNS*/ + err = resolve_name_standard (name, port, want_family, want_socktype, + r_ai, r_canonname); + if (opt_debug) + log_debug ("dns: resolve_dns_name(%s): %s\n", name, gpg_strerror (err)); + return err; +} + + +gpg_error_t +resolve_dns_addr (const struct sockaddr *addr, int addrlen, + unsigned int flags, char **r_name) +{ + return resolve_addr_standard (addr, addrlen, flags, r_name); +} + + +/* Check whether NAME is an IP address. Returns true if it is either + an IPv6 or IPv4 numerical address. */ +int +is_ip_address (const char *name) +{ + const char *s; + int ndots, dblcol, n; + + if (*name == '[') + return 1; /* yes: A legal DNS name may not contain this character; + this mut be bracketed v6 address. */ + if (*name == '.') + return 0; /* No. A leading dot is not a valid IP address. */ + + /* Check whether this is a v6 address. */ + ndots = n = dblcol = 0; + for (s=name; *s; s++) + { + if (*s == ':') + { + ndots++; + if (s[1] == ':') + { + ndots++; + if (dblcol) + return 0; /* No: Only one "::" allowed. */ + dblcol++; + if (s[1]) + s++; + } + n = 0; + } + else if (*s == '.') + goto legacy; + else if (!strchr ("0123456789abcdefABCDEF", *s)) + return 0; /* No: Not a hex digit. */ + else if (++n > 4) + return 0; /* To many digits in a group. */ + } + if (ndots > 7) + return 0; /* No: Too many colons. */ + else if (ndots > 1) + return 1; /* Yes: At least 2 colons indicate an v6 address. */ + + legacy: + /* Check whether it is legacy IP address. */ + ndots = n = 0; + for (s=name; *s; s++) + { + if (*s == '.') + { + if (s[1] == '.') + return 0; /* No: Douple dot. */ + if (atoi (s+1) > 255) + return 0; /* No: Ipv4 byte value too large. */ + ndots++; + n = 0; + } + else if (!strchr ("0123456789", *s)) + return 0; /* No: Not a digit. */ + else if (++n > 3) + return 0; /* No: More than 3 digits. */ + } + return !!(ndots == 3); +} + + +/* Return true if NAME is an onion address. */ +int +is_onion_address (const char *name) +{ + size_t len; + + len = name? strlen (name) : 0; + if (len < 8 || strcmp (name + len - 6, ".onion")) + return 0; + /* Note that we require at least 2 characters before the suffix. */ + return 1; /* Yes. */ +} + + +/* libdns version of get_dns_cert. */ +#ifdef USE_LIBDNS +static gpg_error_t +get_dns_cert_libdns (const char *name, int want_certtype, + void **r_key, size_t *r_keylen, + unsigned char **r_fpr, size_t *r_fprlen, char **r_url) +{ + gpg_error_t err; + struct dns_resolver *res = NULL; + struct dns_packet *ans = NULL; + struct dns_rr rr; + struct dns_rr_i rri; + char host[DNS_D_MAXNAME + 1]; + int derr; + int qtype; + + /* Gte the query type from WANT_CERTTYPE (which in general indicates + * the subtype we want). */ + qtype = (want_certtype < DNS_CERTTYPE_RRBASE + ? T_CERT + : (want_certtype - DNS_CERTTYPE_RRBASE)); + + + err = libdns_res_open (&res); + if (err) + goto leave; + + if (dns_d_anchor (host, sizeof host, name, strlen (name)) >= sizeof host) + { + err = gpg_error (GPG_ERR_ENAMETOOLONG); + goto leave; + } + + err = libdns_res_submit (res, name, qtype, DNS_C_IN); + if (err) + goto leave; + + err = libdns_res_wait (res); + if (err) + goto leave; + + ans = dns_res_fetch (res, &derr); + if (!ans) + { + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* Check the rcode. */ + switch (dns_p_rcode (ans)) + { + case DNS_RC_NOERROR: break; + case DNS_RC_NXDOMAIN: err = gpg_error (GPG_ERR_NO_NAME); break; + default: err = GPG_ERR_SERVER_FAILED; break; + } + if (err) + goto leave; + + memset (&rri, 0, sizeof rri); + dns_rr_i_init (&rri, ans); + rri.section = DNS_S_ALL & ~DNS_S_QD; + rri.name = host; + rri.type = qtype; + + err = gpg_error (GPG_ERR_NOT_FOUND); + while (dns_rr_grep (&rr, 1, &rri, ans, &derr)) + { + unsigned char *rp = ans->data + rr.rd.p; + unsigned short len = rr.rd.len; + u16 subtype; + + if (!len) + { + /* Definitely too short - skip. */ + } + else if (want_certtype >= DNS_CERTTYPE_RRBASE + && rr.type == (want_certtype - DNS_CERTTYPE_RRBASE) + && r_key) + { + *r_key = xtrymalloc (len); + if (!*r_key) + err = gpg_error_from_syserror (); + else + { + memcpy (*r_key, rp, len); + *r_keylen = len; + err = 0; + } + goto leave; + } + else if (want_certtype >= DNS_CERTTYPE_RRBASE) + { + /* We did not found the requested RR - skip. */ + } + else if (rr.type == T_CERT && len > 5) + { + /* We got a CERT type. */ + subtype = buf16_to_u16 (rp); + rp += 2; len -= 2; + + /* Skip the CERT key tag and algo which we don't need. */ + rp += 3; len -= 3; + + if (want_certtype && want_certtype != subtype) + ; /* Not the requested subtype - skip. */ + else if (subtype == DNS_CERTTYPE_PGP && len && r_key && r_keylen) + { + /* PGP subtype */ + *r_key = xtrymalloc (len); + if (!*r_key) + err = gpg_error_from_syserror (); + else + { + memcpy (*r_key, rp, len); + *r_keylen = len; + err = 0; + } + goto leave; + } + else if (subtype == DNS_CERTTYPE_IPGP + && len && len < 1023 && len >= rp[0] + 1) + { + /* IPGP type */ + *r_fprlen = rp[0]; + if (*r_fprlen) + { + *r_fpr = xtrymalloc (*r_fprlen); + if (!*r_fpr) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (*r_fpr, rp+1, *r_fprlen); + } + else + *r_fpr = NULL; + + if (len > *r_fprlen + 1) + { + *r_url = xtrymalloc (len - (*r_fprlen + 1) + 1); + if (!*r_url) + { + err = gpg_error_from_syserror (); + xfree (*r_fpr); + *r_fpr = NULL; + goto leave; + } + memcpy (*r_url, rp + *r_fprlen + 1, len - (*r_fprlen + 1)); + (*r_url)[len - (*r_fprlen + 1)] = 0; + } + else + *r_url = NULL; + + err = 0; + goto leave; + } + else + { + /* Unknown subtype or record too short - skip. */ + } + } + else + { + /* Not a requested type - skip. */ + } + } + + leave: + dns_free (ans); + dns_res_close (res); + return err; +} +#endif /*USE_LIBDNS*/ + + +/* Standard resolver version of get_dns_cert. */ +static gpg_error_t +get_dns_cert_standard (const char *name, int want_certtype, + void **r_key, size_t *r_keylen, + unsigned char **r_fpr, size_t *r_fprlen, char **r_url) +{ +#ifdef HAVE_SYSTEM_RESOLVER + gpg_error_t err; + unsigned char *answer; + int r; + u16 count; + + /* Allocate a 64k buffer which is the limit for an DNS response. */ + answer = xtrymalloc (65536); + if (!answer) + return gpg_error_from_syserror (); + + err = gpg_error (GPG_ERR_NOT_FOUND); + r = res_query (name, C_IN, + (want_certtype < DNS_CERTTYPE_RRBASE + ? T_CERT + : (want_certtype - DNS_CERTTYPE_RRBASE)), + answer, 65536); + /* Not too big, not too small, no errors and at least 1 answer. */ + if (r >= sizeof (HEADER) && r <= 65536 + && (((HEADER *)(void *) answer)->rcode) == NOERROR + && (count = ntohs (((HEADER *)(void *) answer)->ancount))) + { + int rc; + unsigned char *pt, *emsg; + + emsg = &answer[r]; + + pt = &answer[sizeof (HEADER)]; + + /* Skip over the query */ + + rc = dn_skipname (pt, emsg); + if (rc == -1) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + pt += rc + QFIXEDSZ; + + /* There are several possible response types for a CERT request. + We're interested in the PGP (a key) and IPGP (a URI) types. + Skip all others. TODO: A key is better than a URI since + we've gone through all this bother to fetch it, so favor that + if we have both PGP and IPGP? */ + + while (count-- > 0 && pt < emsg) + { + u16 type, class, dlen, ctype; + + rc = dn_skipname (pt, emsg); /* the name we just queried for */ + if (rc == -1) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + + pt += rc; + + /* Truncated message? 15 bytes takes us to the point where + we start looking at the ctype. */ + if ((emsg - pt) < 15) + break; + + type = buf16_to_u16 (pt); + pt += 2; + + class = buf16_to_u16 (pt); + pt += 2; + + if (class != C_IN) + break; + + /* ttl */ + pt += 4; + + /* data length */ + dlen = buf16_to_u16 (pt); + pt += 2; + + /* Check the type and parse. */ + if (want_certtype >= DNS_CERTTYPE_RRBASE + && type == (want_certtype - DNS_CERTTYPE_RRBASE) + && r_key) + { + *r_key = xtrymalloc (dlen); + if (!*r_key) + err = gpg_error_from_syserror (); + else + { + memcpy (*r_key, pt, dlen); + *r_keylen = dlen; + err = 0; + } + goto leave; + } + else if (want_certtype >= DNS_CERTTYPE_RRBASE) + { + /* We did not found the requested RR. */ + pt += dlen; + } + else if (type == T_CERT) + { + /* We got a CERT type. */ + ctype = buf16_to_u16 (pt); + pt += 2; + + /* Skip the CERT key tag and algo which we don't need. */ + pt += 3; + + dlen -= 5; + + /* 15 bytes takes us to here */ + if (want_certtype && want_certtype != ctype) + ; /* Not of the requested certtype. */ + else if (ctype == DNS_CERTTYPE_PGP && dlen && r_key && r_keylen) + { + /* PGP type */ + *r_key = xtrymalloc (dlen); + if (!*r_key) + err = gpg_error_from_syserror (); + else + { + memcpy (*r_key, pt, dlen); + *r_keylen = dlen; + err = 0; + } + goto leave; + } + else if (ctype == DNS_CERTTYPE_IPGP + && dlen && dlen < 1023 && dlen >= pt[0] + 1) + { + /* IPGP type */ + *r_fprlen = pt[0]; + if (*r_fprlen) + { + *r_fpr = xtrymalloc (*r_fprlen); + if (!*r_fpr) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (*r_fpr, &pt[1], *r_fprlen); + } + else + *r_fpr = NULL; + + if (dlen > *r_fprlen + 1) + { + *r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1); + if (!*r_url) + { + err = gpg_error_from_syserror (); + xfree (*r_fpr); + *r_fpr = NULL; + goto leave; + } + memcpy (*r_url, &pt[*r_fprlen + 1], + dlen - (*r_fprlen + 1)); + (*r_url)[dlen - (*r_fprlen + 1)] = '\0'; + } + else + *r_url = NULL; + + err = 0; + goto leave; + } + + /* No subtype matches, so continue with the next answer. */ + pt += dlen; + } + else + { + /* Not a requested type - might be a CNAME. Try next item. */ + pt += dlen; + } + } + } + + leave: + xfree (answer); + return err; + +#else /*!HAVE_SYSTEM_RESOLVER*/ + + (void)name; + (void)want_certtype; + (void)r_key; + (void)r_keylen; + (void)r_fpr; + (void)r_fprlen; + (void)r_url; + return gpg_error (GPG_ERR_NOT_SUPPORTED); + +#endif /*!HAVE_SYSTEM_RESOLVER*/ +} + + +/* Returns 0 on success or an error code. If a PGP CERT record was + found, the malloced data is returned at (R_KEY, R_KEYLEN) and + the other return parameters are set to NULL/0. If an IPGP CERT + record was found the fingerprint is stored as an allocated block at + R_FPR and its length at R_FPRLEN; an URL is is allocated as a + string and returned at R_URL. If WANT_CERTTYPE is 0 this function + returns the first CERT found with a supported type; it is expected + that only one CERT record is used. If WANT_CERTTYPE is one of the + supported certtypes only records with this certtype are considered + and the first found is returned. (R_KEY,R_KEYLEN) are optional. */ +gpg_error_t +get_dns_cert (const char *name, int want_certtype, + void **r_key, size_t *r_keylen, + unsigned char **r_fpr, size_t *r_fprlen, char **r_url) +{ + gpg_error_t err; + + if (r_key) + *r_key = NULL; + if (r_keylen) + *r_keylen = 0; + *r_fpr = NULL; + *r_fprlen = 0; + *r_url = NULL; + +#ifdef USE_LIBDNS + if (!standard_resolver) + { + err = get_dns_cert_libdns (name, want_certtype, r_key, r_keylen, + r_fpr, r_fprlen, r_url); + if (err && libdns_switch_port_p (err)) + err = get_dns_cert_libdns (name, want_certtype, r_key, r_keylen, + r_fpr, r_fprlen, r_url); + } + else +#endif /*USE_LIBDNS*/ + err = get_dns_cert_standard (name, want_certtype, r_key, r_keylen, + r_fpr, r_fprlen, r_url); + + if (opt_debug) + log_debug ("dns: get_dns_cert(%s): %s\n", name, gpg_strerror (err)); + return err; +} + + +static int +priosort(const void *a,const void *b) +{ + const struct srventry *sa=a,*sb=b; + if(sa->priority>sb->priority) + return 1; + else if(sa->prioritypriority) + return -1; + else + return 0; +} + + +/* Libdns based helper for getsrv. Note that it is expected that NULL + * is stored at the address of LIST and 0 is stored at the address of + * R_COUNT. */ +#ifdef USE_LIBDNS +static gpg_error_t +getsrv_libdns (const char *name, struct srventry **list, unsigned int *r_count) +{ + gpg_error_t err; + struct dns_resolver *res = NULL; + struct dns_packet *ans = NULL; + struct dns_rr rr; + struct dns_rr_i rri; + char host[DNS_D_MAXNAME + 1]; + int derr; + unsigned int srvcount = 0; + + err = libdns_res_open (&res); + if (err) + goto leave; + + if (dns_d_anchor (host, sizeof host, name, strlen (name)) >= sizeof host) + { + err = gpg_error (GPG_ERR_ENAMETOOLONG); + goto leave; + } + + err = libdns_res_submit (res, name, DNS_T_SRV, DNS_C_IN); + if (err) + goto leave; + + err = libdns_res_wait (res); + if (err) + goto leave; + + ans = dns_res_fetch (res, &derr); + if (!ans) + { + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* Check the rcode. */ + switch (dns_p_rcode (ans)) + { + case DNS_RC_NOERROR: break; + case DNS_RC_NXDOMAIN: err = gpg_error (GPG_ERR_NO_NAME); break; + default: err = GPG_ERR_SERVER_FAILED; break; + } + if (err) + goto leave; + + memset (&rri, 0, sizeof rri); + dns_rr_i_init (&rri, ans); + rri.section = DNS_S_ALL & ~DNS_S_QD; + rri.name = host; + rri.type = DNS_T_SRV; + + while (dns_rr_grep (&rr, 1, &rri, ans, &derr)) + { + struct dns_srv dsrv; + struct srventry *srv; + struct srventry *newlist; + + err = libdns_error_to_gpg_error (dns_srv_parse(&dsrv, &rr, ans)); + if (err) + goto leave; + + newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry)); + if (!newlist) + { + err = gpg_error_from_syserror (); + goto leave; + } + *list = newlist; + memset (&(*list)[srvcount], 0, sizeof(struct srventry)); + srv = &(*list)[srvcount]; + srvcount++; + srv->priority = dsrv.priority; + srv->weight = dsrv.weight; + srv->port = dsrv.port; + mem2str (srv->target, dsrv.target, sizeof srv->target); + } + + *r_count = srvcount; + + leave: + if (err) + { + xfree (*list); + *list = NULL; + } + dns_free (ans); + dns_res_close (res); + return err; +} +#endif /*USE_LIBDNS*/ + + +/* Standard resolver based helper for getsrv. Note that it is + * expected that NULL is stored at the address of LIST and 0 is stored + * at the address of R_COUNT. */ +static gpg_error_t +getsrv_standard (const char *name, + struct srventry **list, unsigned int *r_count) +{ +#ifdef HAVE_SYSTEM_RESOLVER + union { + unsigned char ans[2048]; + HEADER header[1]; + } res; + unsigned char *answer = res.ans; + HEADER *header = res.header; + unsigned char *pt, *emsg; + int r, rc; + u16 dlen; + unsigned int srvcount = 0; + u16 count; + + /* Do not allow a query using the standard resolver in Tor mode. */ + if (tor_mode) + return gpg_error (GPG_ERR_NOT_ENABLED); + + my_unprotect (); + r = res_query (name, C_IN, T_SRV, answer, sizeof res.ans); + my_protect (); + if (r < 0) + return get_h_errno_as_gpg_error (); + if (r < sizeof (HEADER)) + return gpg_error (GPG_ERR_SERVER_FAILED); + if (r > sizeof res.ans) + return gpg_error (GPG_ERR_SYSTEM_BUG); + if (header->rcode != NOERROR || !(count=ntohs (header->ancount))) + return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */ + + emsg = &answer[r]; + pt = &answer[sizeof(HEADER)]; + + /* Skip over the query */ + rc = dn_skipname (pt, emsg); + if (rc == -1) + goto fail; + + pt += rc + QFIXEDSZ; + + while (count-- > 0 && pt < emsg) + { + struct srventry *srv; + u16 type, class; + struct srventry *newlist; + + newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry)); + if (!newlist) + goto fail; + *list = newlist; + memset (&(*list)[srvcount], 0, sizeof(struct srventry)); + srv = &(*list)[srvcount]; + srvcount++; + + rc = dn_skipname (pt, emsg); /* The name we just queried for. */ + if (rc == -1) + goto fail; + pt += rc; + + /* Truncated message? */ + if ((emsg-pt) < 16) + goto fail; + + type = buf16_to_u16 (pt); + pt += 2; + /* We asked for SRV and got something else !? */ + if (type != T_SRV) + goto fail; + + class = buf16_to_u16 (pt); + pt += 2; + /* We asked for IN and got something else !? */ + if (class != C_IN) + goto fail; + + pt += 4; /* ttl */ + dlen = buf16_to_u16 (pt); + pt += 2; + + srv->priority = buf16_to_ushort (pt); + pt += 2; + srv->weight = buf16_to_ushort (pt); + pt += 2; + srv->port = buf16_to_ushort (pt); + pt += 2; + + /* Get the name. 2782 doesn't allow name compression, but + * dn_expand still works to pull the name out of the packet. */ + rc = dn_expand (answer, emsg, pt, srv->target, sizeof srv->target); + if (rc == 1 && srv->target[0] == 0) /* "." */ + { + xfree(*list); + *list = NULL; + return 0; + } + if (rc == -1) + goto fail; + pt += rc; + /* Corrupt packet? */ + if (dlen != rc+6) + goto fail; + } + + *r_count = srvcount; + return 0; + + fail: + xfree (*list); + *list = NULL; + return gpg_error (GPG_ERR_GENERAL); + +#else /*!HAVE_SYSTEM_RESOLVER*/ + + (void)name; + (void)list; + (void)r_count; + return gpg_error (GPG_ERR_NOT_SUPPORTED); + +#endif /*!HAVE_SYSTEM_RESOLVER*/ +} + + +/* Note that we do not return NONAME but simply store 0 at R_COUNT. */ +gpg_error_t +get_dns_srv (const char *name, struct srventry **list, unsigned int *r_count) +{ + gpg_error_t err; + unsigned int srvcount; + int i; + + *list = NULL; + *r_count = 0; + srvcount = 0; +#ifdef USE_LIBDNS + if (!standard_resolver) + { + err = getsrv_libdns (name, list, &srvcount); + if (err && libdns_switch_port_p (err)) + err = getsrv_libdns (name, list, &srvcount); + } + else +#endif /*USE_LIBDNS*/ + err = getsrv_standard (name, list, &srvcount); + + if (err) + { + if (gpg_err_code (err) == GPG_ERR_NO_NAME) + err = 0; + goto leave; + } + + /* Now we have an array of all the srv records. */ + + /* Order by priority */ + qsort(*list,srvcount,sizeof(struct srventry),priosort); + + /* For each priority, move the zero-weighted items first. */ + for (i=0; i < srvcount; i++) + { + int j; + + for (j=i;j < srvcount && (*list)[i].priority == (*list)[j].priority; j++) + { + if((*list)[j].weight==0) + { + /* Swap j with i */ + if(j!=i) + { + struct srventry temp; + + memcpy (&temp,&(*list)[j],sizeof(struct srventry)); + memcpy (&(*list)[j],&(*list)[i],sizeof(struct srventry)); + memcpy (&(*list)[i],&temp,sizeof(struct srventry)); + } + + break; + } + } + } + + /* Run the RFC-2782 weighting algorithm. We don't need very high + quality randomness for this, so regular libc srand/rand is + sufficient. */ + + { + static int done; + if (!done) + { + done = 1; + srand (time (NULL)*getpid()); + } + } + + for (i=0; i < srvcount; i++) + { + int j; + float prio_count=0,chose; + + for (j=i; j < srvcount && (*list)[i].priority == (*list)[j].priority; j++) + { + prio_count+=(*list)[j].weight; + (*list)[j].run_count=prio_count; + } + + chose=prio_count*rand()/RAND_MAX; + + for (j=i;j %u records\n", name, srvcount); + } + if (!err) + *r_count = srvcount; + return err; +} + + + +#ifdef USE_LIBDNS +/* libdns version of get_dns_cname. */ +gpg_error_t +get_dns_cname_libdns (const char *name, char **r_cname) +{ + gpg_error_t err; + struct dns_resolver *res; + struct dns_packet *ans = NULL; + struct dns_cname cname; + int derr; + + err = libdns_res_open (&res); + if (err) + goto leave; + + err = libdns_res_submit (res, name, DNS_T_CNAME, DNS_C_IN); + if (err) + goto leave; + + err = libdns_res_wait (res); + if (err) + goto leave; + + ans = dns_res_fetch (res, &derr); + if (!ans) + { + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* Check the rcode. */ + switch (dns_p_rcode (ans)) + { + case DNS_RC_NOERROR: break; + case DNS_RC_NXDOMAIN: err = gpg_error (GPG_ERR_NO_NAME); break; + default: err = GPG_ERR_SERVER_FAILED; break; + } + if (err) + goto leave; + + /* Parse the result into CNAME. */ + err = libdns_error_to_gpg_error (dns_p_study (ans)); + if (err) + goto leave; + + if (!dns_d_cname (&cname, sizeof cname, name, strlen (name), ans, &derr)) + { + err = libdns_error_to_gpg_error (derr); + goto leave; + } + + /* Copy result. */ + *r_cname = xtrystrdup (cname.host); + if (!*r_cname) + err = gpg_error_from_syserror (); + + leave: + dns_free (ans); + dns_res_close (res); + return err; +} +#endif /*USE_LIBDNS*/ + + +/* Standard resolver version of get_dns_cname. */ +gpg_error_t +get_dns_cname_standard (const char *name, char **r_cname) +{ +#ifdef HAVE_SYSTEM_RESOLVER + gpg_error_t err; + int rc; + union { + unsigned char ans[2048]; + HEADER header[1]; + } res; + unsigned char *answer = res.ans; + HEADER *header = res.header; + unsigned char *pt, *emsg; + int r; + char *cname; + int cnamesize = 1025; + u16 count; + + /* Do not allow a query using the standard resolver in Tor mode. */ + if (tor_mode) + return -1; + + my_unprotect (); + r = res_query (name, C_IN, T_CERT, answer, sizeof res.ans); + my_protect (); + if (r < 0) + return get_h_errno_as_gpg_error (); + if (r < sizeof (HEADER)) + return gpg_error (GPG_ERR_SERVER_FAILED); + if (r > sizeof res.ans) + return gpg_error (GPG_ERR_SYSTEM_BUG); + if (header->rcode != NOERROR || !(count=ntohs (header->ancount))) + return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */ + if (count != 1) + return gpg_error (GPG_ERR_SERVER_FAILED); + + emsg = &answer[r]; + pt = &answer[sizeof(HEADER)]; + rc = dn_skipname (pt, emsg); + if (rc == -1) + return gpg_error (GPG_ERR_SERVER_FAILED); + + pt += rc + QFIXEDSZ; + if (pt >= emsg) + return gpg_error (GPG_ERR_SERVER_FAILED); + + rc = dn_skipname (pt, emsg); + if (rc == -1) + return gpg_error (GPG_ERR_SERVER_FAILED); + pt += rc + 2 + 2 + 4; + if (pt+2 >= emsg) + return gpg_error (GPG_ERR_SERVER_FAILED); + pt += 2; /* Skip rdlen */ + + cname = xtrymalloc (cnamesize); + if (!cname) + return gpg_error_from_syserror (); + + rc = dn_expand (answer, emsg, pt, cname, cnamesize -1); + if (rc == -1) + { + xfree (cname); + return gpg_error (GPG_ERR_SERVER_FAILED); + } + *r_cname = xtryrealloc (cname, strlen (cname)+1); + if (!*r_cname) + { + err = gpg_error_from_syserror (); + xfree (cname); + return err; + } + return 0; + +#else /*!HAVE_SYSTEM_RESOLVER*/ + + (void)name; + (void)r_cname; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + +#endif /*!HAVE_SYSTEM_RESOLVER*/ +} + + +gpg_error_t +get_dns_cname (const char *name, char **r_cname) +{ + gpg_error_t err; + + *r_cname = NULL; + +#ifdef USE_LIBDNS + if (!standard_resolver) + { + err = get_dns_cname_libdns (name, r_cname); + if (err && libdns_switch_port_p (err)) + err = get_dns_cname_libdns (name, r_cname); + return err; + } +#endif /*USE_LIBDNS*/ + + err = get_dns_cname_standard (name, r_cname); + if (opt_debug) + log_debug ("get_dns_cname(%s)%s%s\n", name, + err ? ": " : " -> ", + err ? gpg_strerror (err) : *r_cname); + return err; +} diff --git a/dirmngr/dns-stuff.h b/dirmngr/dns-stuff.h new file mode 100644 index 0000000..0a4a4de --- /dev/null +++ b/dirmngr/dns-stuff.h @@ -0,0 +1,159 @@ +/* dns-stuff.c - DNS related code including CERT RR (rfc-4398) + * Copyright (C) 2006 Free Software Foundation, Inc. + * Copyright (C) 2006, 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ +#ifndef GNUPG_DIRMNGR_DNS_STUFF_H +#define GNUPG_DIRMNGR_DNS_STUFF_H + +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else +# include +# include +#endif + +/* + * Flags used with resolve_dns_addr. + */ +#define DNS_NUMERICHOST 1 /* Force numeric output format. */ +#define DNS_WITHBRACKET 2 /* Put brackets around numeric v6 + addresses. */ + +/* + * Constants for use with get_dns_cert. + */ +#define DNS_CERTTYPE_ANY 0 /* Internal catch all type. */ +/* Certificate types according to RFC-4398: */ +#define DNS_CERTTYPE_PKIX 1 /* X.509 as per PKIX. */ +#define DNS_CERTTYPE_SPKI 2 /* SPKI certificate. */ +#define DNS_CERTTYPE_PGP 3 /* OpenPGP packet. */ +#define DNS_CERTTYPE_IPKIX 4 /* The URL of an X.509 data object. */ +#define DNS_CERTTYPE_ISPKI 5 /* The URL of an SPKI certificate. */ +#define DNS_CERTTYPE_IPGP 6 /* The fingerprint + and URL of an OpenPGP packet. */ +#define DNS_CERTTYPE_ACPKIX 7 /* Attribute Certificate. */ +#define DNS_CERTTYPE_IACPKIX 8 /* The URL of an Attribute Certificate. */ +#define DNS_CERTTYPE_URI 253 /* URI private. */ +#define DNS_CERTTYPE_OID 254 /* OID private. */ +/* Hacks for our implementation. */ +#define DNS_CERTTYPE_RRBASE 1024 /* Base of special constants. */ +#define DNS_CERTTYPE_RR61 (DNS_CERTTYPE_RRBASE + 61) + + + +struct dns_addrinfo_s; +typedef struct dns_addrinfo_s *dns_addrinfo_t; +struct dns_addrinfo_s +{ + dns_addrinfo_t next; + int family; + int socktype; + int protocol; + int addrlen; + struct sockaddr addr[1]; +}; + + +struct srventry +{ + unsigned short priority; + unsigned short weight; + unsigned short port; + int run_count; + char target[1025]; +}; + + +/* Set verbosity and debug mode for this module. */ +void set_dns_verbose (int verbose, int debug); + +/* Set the timeout for libdns requests to SECONDS. */ +void set_dns_timeout (int seconds); + +/* Calling this function with YES set to True forces the use of the + * standard resolver even if dirmngr has been built with support for + * an alternative resolver. */ +void enable_standard_resolver (int yes); + +/* Return true if the standard resolver is used. */ +int standard_resolver_p (void); + +/* Calling this function with YES switches libdns into recursive mode. + * It has no effect on the standard resolver. */ +void enable_recursive_resolver (int yes); + +/* Return true iff the recursive resolver is used. */ +int recursive_resolver_p (void); + +/* Calling this function switches the DNS code into Tor mode if + possibe. Return 0 on success. */ +gpg_error_t enable_dns_tormode (int new_circuit); + +/* Change the default IP address of the nameserver to IPADDR. The + address needs to be a numerical IP address and will be used for the + next DNS query. Note that this is only used in Tor mode. */ +void set_dns_nameserver (const char *ipaddr); + +/* SIGHUP action handler for this module. */ +void reload_dns_stuff (int force); + +void free_dns_addrinfo (dns_addrinfo_t ai); + +/* Function similar to getaddrinfo. */ +gpg_error_t resolve_dns_name (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_dai, char **r_canonname); + +/* Function similar to getnameinfo. */ +gpg_error_t resolve_dns_addr (const struct sockaddr *addr, int addrlen, + unsigned int flags, char **r_name); + +/* Return true if NAME is a numerical IP address. */ +int is_ip_address (const char *name); + +/* Return true if NAME is an onion address. */ +int is_onion_address (const char *name); + +/* Get the canonical name for NAME. */ +gpg_error_t get_dns_cname (const char *name, char **r_cname); + +/* Return a CERT record or an arbitray RR. */ +gpg_error_t get_dns_cert (const char *name, int want_certtype, + void **r_key, size_t *r_keylen, + unsigned char **r_fpr, size_t *r_fprlen, + char **r_url); + +/* Return an array of SRV records. */ +gpg_error_t get_dns_srv (const char *name, + struct srventry **list, unsigned int *r_count); + + +#endif /*GNUPG_DIRMNGR_DNS_STUFF_H*/ diff --git a/dirmngr/dns.c b/dirmngr/dns.c new file mode 100644 index 0000000..4b61b72 --- /dev/null +++ b/dirmngr/dns.c @@ -0,0 +1,11246 @@ +/* ========================================================================== + * dns.c - Recursive, Reentrant DNS Resolver. + * -------------------------------------------------------------------------- + * Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * ========================================================================== + */ +#if HAVE_CONFIG_H +#include "config.h" +#elif !defined _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +#include /* INT_MAX */ +#include /* va_list va_start va_end */ +#include /* offsetof() */ +#ifdef _WIN32 +/* JW: This breaks our mingw build: #define uint32_t unsigned int */ +#else +#include /* uint32_t */ +#endif +#include /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */ +#include /* FILE fopen(3) fclose(3) getc(3) rewind(3) vsnprintf(3) */ +#include /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */ +#include /* strcasecmp(3) strncasecmp(3) */ +#include /* isspace(3) isdigit(3) */ +#include /* time_t time(2) difftime(3) */ +#include /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */ +#include /* errno EINVAL ENOENT */ +#undef NDEBUG +#include /* assert(3) */ + +#if _WIN32 +#ifndef FD_SETSIZE +#define FD_SETSIZE 1024 +#endif +#include +#include +#else +#include /* gettimeofday(2) */ +#include /* FD_SETSIZE socklen_t */ +#include /* FD_ZERO FD_SET fd_set select(2) */ +#include /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */ +#if defined(AF_UNIX) +#include /* struct sockaddr_un */ +#endif +#include /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */ +#include /* _POSIX_THREADS gethostname(3) close(2) */ +#include /* POLLIN POLLOUT */ +#include /* struct sockaddr_in struct sockaddr_in6 */ +#include /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */ +#include /* struct addrinfo */ +#endif + +#include "dns.h" + + +/* + * C O M P I L E R V E R S I O N & F E A T U R E D E T E C T I O N + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) +#define DNS_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && DNS_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= DNS_GNUC_2VER((M), (m), (p))) + +#define DNS_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p)) +#define DNS_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= DNS_MSC_2VER((M), (m), (p))) + +#define DNS_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p) + +#if defined __has_builtin +#define dns_has_builtin(x) __has_builtin(x) +#else +#define dns_has_builtin(x) 0 +#endif + +#if defined __has_extension +#define dns_has_extension(x) __has_extension(x) +#else +#define dns_has_extension(x) 0 +#endif + +#ifndef HAVE___ASSUME +#define HAVE___ASSUME DNS_MSC_PREREQ(8,0,0) +#endif + +#ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P +#define HAVE___BUILTIN_TYPES_COMPATIBLE_P (DNS_GNUC_PREREQ(3,1,1) || __clang__) +#endif + +#ifndef HAVE___BUILTIN_UNREACHABLE +#define HAVE___BUILTIN_UNREACHABLE (DNS_GNUC_PREREQ(4,5,0) || dns_has_builtin(__builtin_unreachable)) +#endif + +#ifndef HAVE_PRAGMA_MESSAGE +#define HAVE_PRAGMA_MESSAGE (DNS_GNUC_PREREQ(4,4,0) || __clang__ || _MSC_VER) +#endif + + +/* + * C O M P I L E R A N N O T A T I O N S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if __GNUC__ +#define DNS_NOTUSED __attribute__((unused)) +#define DNS_NORETURN __attribute__((noreturn)) +#else +#define DNS_NOTUSED +#define DNS_NORETURN +#endif + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#elif DNS_GNUC_PREREQ(4,6,0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + +/* + * S T A N D A R D M A C R O S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if HAVE___BUILTIN_TYPES_COMPATIBLE_P +#define dns_same_type(a, b, def) __builtin_types_compatible_p(__typeof__ (a), __typeof__ (b)) +#else +#define dns_same_type(a, b, def) (def) +#endif +#define dns_isarray(a) (!dns_same_type((a), (&(a)[0]), 0)) +/* NB: "_" field silences Sun Studio "zero-sized struct/union" error diagnostic */ +#define dns_inline_assert(cond) ((void)(sizeof (struct { int:-!(cond); int _; }))) + +#if HAVE___ASSUME +#define dns_assume(cond) __assume(cond) +#elif HAVE___BUILTIN_UNREACHABLE +#define dns_assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) +#else +#define dns_assume(cond) do { (void)(cond); } while (0) +#endif + +#ifndef lengthof +#define lengthof(a) (dns_inline_assert(dns_isarray(a)), (sizeof (a) / sizeof (a)[0])) +#endif + +#ifndef endof +#define endof(a) (dns_inline_assert(dns_isarray(a)), &(a)[lengthof((a))]) +#endif + + +/* + * M I S C E L L A N E O U S C O M P A T + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if _WIN32 || _WIN64 +#define PRIuZ "Iu" +#else +#define PRIuZ "zu" +#endif + +#ifndef DNS_THREAD_SAFE +#if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 +#define DNS_THREAD_SAFE 1 +#else +#define DNS_THREAD_SAFE 0 +#endif +#endif + +#ifndef HAVE__STATIC_ASSERT +#define HAVE__STATIC_ASSERT \ + (dns_has_extension(c_static_assert) || DNS_GNUC_PREREQ(4,6,0) || \ + __C11FEATURES__ || __STDC_VERSION__ >= 201112L) +#endif + +#ifndef HAVE_STATIC_ASSERT +#if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0) +#define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */ +#else +#define HAVE_STATIC_ASSERT (defined static_assert) +#endif +#endif + +#if HAVE_STATIC_ASSERT +#define dns_static_assert(cond, msg) static_assert(cond, msg) +#elif HAVE__STATIC_ASSERT +#define dns_static_assert(cond, msg) _Static_assert(cond, msg) +#else +#define dns_static_assert(cond, msg) extern char DNS_PP_XPASTE(dns_assert_, __LINE__)[sizeof (int[1 - 2*!(cond)])] +#endif + + +/* + * D E B U G M A C R O S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int *dns_debug_p(void) { + static int debug; + + return &debug; +} /* dns_debug_p() */ + +#if DNS_DEBUG + +#undef DNS_DEBUG +#define DNS_DEBUG dns_debug + +#define DNS_SAY_(fmt, ...) \ + do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0) +#define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n") +#define DNS_HAI DNS_SAY("HAI") + +#define DNS_SHOW_(P, fmt, ...) do { \ + if (DNS_DEBUG > 1) { \ + fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \ + fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \ + dns_p_dump((P), stderr); \ + fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \ + } \ +} while (0) + +#define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "") + +#else /* !DNS_DEBUG */ + +#undef DNS_DEBUG +#define DNS_DEBUG 0 + +#define DNS_SAY(...) +#define DNS_HAI +#define DNS_SHOW(...) + +#endif /* DNS_DEBUG */ + +#define DNS_CARP(...) DNS_SAY(__VA_ARGS__) + + +/* + * V E R S I O N R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const char *dns_vendor(void) { + return DNS_VENDOR; +} /* dns_vendor() */ + + +int dns_v_rel(void) { + return DNS_V_REL; +} /* dns_v_rel() */ + + +int dns_v_abi(void) { + return DNS_V_ABI; +} /* dns_v_abi() */ + + +int dns_v_api(void) { + return DNS_V_API; +} /* dns_v_api() */ + + +/* + * E R R O R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if _WIN32 + +#define DNS_EINTR WSAEINTR +#define DNS_EINPROGRESS WSAEINPROGRESS +#define DNS_EISCONN WSAEISCONN +#define DNS_EWOULDBLOCK WSAEWOULDBLOCK +#define DNS_EALREADY WSAEALREADY +#define DNS_EAGAIN EAGAIN +#define DNS_ETIMEDOUT WSAETIMEDOUT + +#define dns_syerr() ((int)GetLastError()) +#define dns_soerr() ((int)WSAGetLastError()) + +#else + +#define DNS_EINTR EINTR +#define DNS_EINPROGRESS EINPROGRESS +#define DNS_EISCONN EISCONN +#define DNS_EWOULDBLOCK EWOULDBLOCK +#define DNS_EALREADY EALREADY +#define DNS_EAGAIN EAGAIN +#define DNS_ETIMEDOUT ETIMEDOUT + +#define dns_syerr() errno +#define dns_soerr() errno + +#endif + + +const char *dns_strerror(int error) { + switch (error) { + case DNS_ENOBUFS: + return "DNS packet buffer too small"; + case DNS_EILLEGAL: + return "Illegal DNS RR name or data"; + case DNS_EORDER: + return "Attempt to push RR out of section order"; + case DNS_ESECTION: + return "Invalid section specified"; + case DNS_EUNKNOWN: + return "Unknown DNS error"; + case DNS_EADDRESS: + return "Invalid textual address form"; + case DNS_ENOQUERY: + return "Bad execution state (missing query packet)"; + case DNS_ENOANSWER: + return "Bad execution state (missing answer packet)"; + case DNS_EFETCHED: + return "Answer already fetched"; + case DNS_ESERVICE: + return "The service passed was not recognized for the specified socket type"; + case DNS_ENONAME: + return "The name does not resolve for the supplied parameters"; + case DNS_EFAIL: + return "A non-recoverable error occurred when attempting to resolve the name"; + case DNS_ECONNFIN: + return "Connection closed"; + case DNS_EVERIFY: + return "Reply failed verification"; + default: + return strerror(error); + } /* switch() */ +} /* dns_strerror() */ + + +/* + * A T O M I C R O U T I N E S + * + * Use GCC's __atomic built-ins if possible. Unlike the __sync built-ins, we + * can use the preprocessor to detect API and, more importantly, ISA + * support. We want to avoid linking headaches where the API depends on an + * external library if the ISA (e.g. i386) doesn't support lockless + * operation. + * + * TODO: Support C11's atomic API. Although that may require some finesse + * with how we define some public types, such as dns_atomic_t and struct + * dns_resolv_conf. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HAVE___ATOMIC_FETCH_ADD +#define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED) +#endif + +#ifndef HAVE___ATOMIC_FETCH_SUB +#define HAVE___ATOMIC_FETCH_SUB HAVE___ATOMIC_FETCH_ADD +#endif + +#ifndef DNS_ATOMIC_FETCH_ADD +#if HAVE___ATOMIC_FETCH_ADD && __GCC_ATOMIC_LONG_LOCK_FREE == 2 +#define DNS_ATOMIC_FETCH_ADD(i) __atomic_fetch_add((i), 1, __ATOMIC_RELAXED) +#else +#pragma message("no atomic_fetch_add available") +#define DNS_ATOMIC_FETCH_ADD(i) ((*(i))++) +#endif +#endif + +#ifndef DNS_ATOMIC_FETCH_SUB +#if HAVE___ATOMIC_FETCH_SUB && __GCC_ATOMIC_LONG_LOCK_FREE == 2 +#define DNS_ATOMIC_FETCH_SUB(i) __atomic_fetch_sub((i), 1, __ATOMIC_RELAXED) +#else +#pragma message("no atomic_fetch_sub available") +#define DNS_ATOMIC_FETCH_SUB(i) ((*(i))--) +#endif +#endif + +static inline unsigned dns_atomic_fetch_add(dns_atomic_t *i) { + return DNS_ATOMIC_FETCH_ADD(i); +} /* dns_atomic_fetch_add() */ + + +static inline unsigned dns_atomic_fetch_sub(dns_atomic_t *i) { + return DNS_ATOMIC_FETCH_SUB(i); +} /* dns_atomic_fetch_sub() */ + + +/* + * C R Y P T O R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * P R N G + */ + +#ifndef DNS_RANDOM +#if defined(HAVE_ARC4RANDOM) \ + || defined(__OpenBSD__) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__APPLE__) +#define DNS_RANDOM arc4random +#elif __linux +#define DNS_RANDOM random +#else +#define DNS_RANDOM rand +#endif +#endif + +#define DNS_RANDOM_arc4random 1 +#define DNS_RANDOM_random 2 +#define DNS_RANDOM_rand 3 +#define DNS_RANDOM_RAND_bytes 4 + +#define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM)) + +#if DNS_RANDOM_OPENSSL +#include +#endif + +static unsigned dns_random_(void) { +#if DNS_RANDOM_OPENSSL + unsigned r; + _Bool ok; + + ok = (1 == RAND_bytes((unsigned char *)&r, sizeof r)); + assert(ok && "1 == RAND_bytes()"); + + return r; +#else + return DNS_RANDOM(); +#endif +} /* dns_random_() */ + +dns_random_f **dns_random_p(void) { + static dns_random_f *random_f = &dns_random_; + + return &random_f; +} /* dns_random_p() */ + + +/* + * P E R M U T A T I O N G E N E R A T O R + */ + +#define DNS_K_TEA_KEY_SIZE 16 +#define DNS_K_TEA_BLOCK_SIZE 8 +#define DNS_K_TEA_CYCLES 32 +#define DNS_K_TEA_MAGIC 0x9E3779B9U + +struct dns_k_tea { + uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; + unsigned cycles; +}; /* struct dns_k_tea */ + + +static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) { + memcpy(tea->key, key, sizeof tea->key); + + tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES; +} /* dns_k_tea_init() */ + + +static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) { + uint32_t y, z, sum, n; + + y = v[0]; + z = v[1]; + sum = 0; + + for (n = 0; n < tea->cycles; n++) { + sum += DNS_K_TEA_MAGIC; + y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]); + z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]); + } + + w[0] = y; + w[1] = z; + + return /* void */; +} /* dns_k_tea_encrypt() */ + + +/* + * Permutation generator, based on a Luby-Rackoff Feistel construction. + * + * Specifically, this is a generic balanced Feistel block cipher using TEA + * (another block cipher) as the pseudo-random function, F. At best it's as + * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or + * perhaps Bernstein's Salsa20 core; I am naively trying to keep things + * simple. + * + * The generator can create a permutation of any set of numbers, as long as + * the size of the set is an even power of 2. This limitation arises either + * out of an inherent property of balanced Feistel constructions, or by my + * own ignorance. I'll tackle an unbalanced construction after I wrap my + * head around Schneier and Kelsey's paper. + * + * CAVEAT EMPTOR. IANAC. + */ +#define DNS_K_PERMUTOR_ROUNDS 8 + +struct dns_k_permutor { + unsigned stepi, length, limit; + unsigned shift, mask, rounds; + + struct dns_k_tea tea; +}; /* struct dns_k_permutor */ + + +static inline unsigned dns_k_permutor_powof(unsigned n) { + unsigned m, i = 0; + + for (m = 1; m < n; m <<= 1, i++) + ;; + + return i; +} /* dns_k_permutor_powof() */ + +static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) { + uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; + unsigned width, i; + + p->stepi = 0; + + p->length = (high - low) + 1; + p->limit = high; + + width = dns_k_permutor_powof(p->length); + width += width % 2; + + p->shift = width / 2; + p->mask = (1U << p->shift) - 1; + p->rounds = DNS_K_PERMUTOR_ROUNDS; + + for (i = 0; i < lengthof(key); i++) + key[i] = dns_random(); + + dns_k_tea_init(&p->tea, key, 0); + + return /* void */; +} /* dns_k_permutor_init() */ + + +static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) { + uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)]; + + memset(in, '\0', sizeof in); + + in[0] = k; + in[1] = x; + + dns_k_tea_encrypt(&p->tea, in, out); + + return p->mask & out[0]; +} /* dns_k_permutor_F() */ + + +static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) { + unsigned l[2], r[2]; + unsigned i; + + i = 0; + l[i] = p->mask & (n >> p->shift); + r[i] = p->mask & (n >> 0); + + do { + l[(i + 1) % 2] = r[i % 2]; + r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]); + + i++; + } while (i < p->rounds - 1); + + return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); +} /* dns_k_permutor_E() */ + + +DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) { + unsigned l[2], r[2]; + unsigned i; + + i = p->rounds - 1; + l[i % 2] = p->mask & (n >> p->shift); + r[i % 2] = p->mask & (n >> 0); + + do { + i--; + + r[i % 2] = l[(i + 1) % 2]; + l[i % 2] = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]); + } while (i > 0); + + return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); +} /* dns_k_permutor_D() */ + + +static unsigned dns_k_permutor_step(struct dns_k_permutor *p) { + unsigned n; + + do { + n = dns_k_permutor_E(p, p->stepi++); + } while (n >= p->length); + + return n + (p->limit + 1 - p->length); +} /* dns_k_permutor_step() */ + + +/* + * Simple permutation box. Useful for shuffling rrsets from an iterator. + * Uses AES s-box to provide good diffusion. + * + * Seems to pass muster under runs test. + * + * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done + * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }' + * library(lawstat) + * runs.test(scan(file="/tmp/out")) + * EOF + */ +static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) { + static const unsigned char sbox[256] = + { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; + unsigned char a, b; + unsigned i; + + a = 0xff & (n >> 0); + b = 0xff & (n >> 8); + + for (i = 0; i < 4; i++) { + a ^= 0xff & s; + a = sbox[a] ^ b; + b = sbox[b] ^ a; + s >>= 8; + } + + return ((0xff00 & (a << 8)) | (0x00ff & (b << 0))); +} /* dns_k_shuffle16() */ + +/* + * S T A T E M A C H I N E R O U T I N E S + * + * Application code should define DNS_SM_RESTORE and DNS_SM_SAVE, and the + * local variable pc. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_SM_ENTER \ + do { \ + static const int pc0 = __LINE__; \ + DNS_SM_RESTORE; \ + switch (pc0 + pc) { \ + case __LINE__: (void)0 + +#define DNS_SM_SAVE_AND_DO(do_statement) \ + do { \ + pc = __LINE__ - pc0; \ + DNS_SM_SAVE; \ + do_statement; \ + case __LINE__: (void)0; \ + } while (0) + +#define DNS_SM_YIELD(rv) \ + DNS_SM_SAVE_AND_DO(return (rv)) + +#define DNS_SM_EXIT \ + do { goto leave; } while (0) + +#define DNS_SM_LEAVE \ + leave: (void)0; \ + DNS_SM_SAVE_AND_DO(break); \ + } \ + } while (0) + +/* + * U T I L I T Y R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_MAXINTERVAL 300 + +struct dns_clock { + time_t sample, elapsed; +}; /* struct dns_clock */ + +static void dns_begin(struct dns_clock *clk) { + clk->sample = time(0); + clk->elapsed = 0; +} /* dns_begin() */ + +static time_t dns_elapsed(struct dns_clock *clk) { + time_t curtime; + + if ((time_t)-1 == time(&curtime)) + return clk->elapsed; + + if (curtime > clk->sample) + clk->elapsed += (time_t)DNS_PP_MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL); + + clk->sample = curtime; + + return clk->elapsed; +} /* dns_elapsed() */ + + +DNS_NOTUSED static size_t dns_strnlen(const char *src, size_t m) { + size_t n = 0; + + while (*src++ && n < m) + ++n; + + return n; +} /* dns_strnlen() */ + + +DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, size_t max) { + size_t len = dns_strnlen(src, max), n; + + if (lim > 0) { + n = DNS_PP_MIN(lim - 1, len); + memcpy(dst, src, n); + dst[n] = '\0'; + } + + return len; +} /* dns_strnlcpy() */ + + +#define DNS_HAVE_SOCKADDR_UN (defined AF_UNIX && !defined _WIN32) + +static size_t dns_af_len(int af) { + static const size_t table[AF_MAX] = { + [AF_INET6] = sizeof (struct sockaddr_in6), + [AF_INET] = sizeof (struct sockaddr_in), +#if DNS_HAVE_SOCKADDR_UN + [AF_UNIX] = sizeof (struct sockaddr_un), +#endif + }; + + return table[af]; +} /* dns_af_len() */ + +#define dns_sa_family(sa) (((struct sockaddr *)(sa))->sa_family) + +#define dns_sa_len(sa) dns_af_len(dns_sa_family(sa)) + + +#define DNS_SA_NOPORT &dns_sa_noport +static unsigned short dns_sa_noport; + +static unsigned short *dns_sa_port(int af, void *sa) { + switch (af) { + case AF_INET6: + return &((struct sockaddr_in6 *)sa)->sin6_port; + case AF_INET: + return &((struct sockaddr_in *)sa)->sin_port; + default: + return DNS_SA_NOPORT; + } +} /* dns_sa_port() */ + + +static void *dns_sa_addr(int af, const void *sa, socklen_t *size) { + switch (af) { + case AF_INET6: { + struct in6_addr *in6 = &((struct sockaddr_in6 *)sa)->sin6_addr; + + if (size) + *size = sizeof *in6; + + return in6; + } + case AF_INET: { + struct in_addr *in = &((struct sockaddr_in *)sa)->sin_addr; + + if (size) + *size = sizeof *in; + + return in; + } + default: + if (size) + *size = 0; + + return 0; + } +} /* dns_sa_addr() */ + + +#if DNS_HAVE_SOCKADDR_UN +#define DNS_SUNPATHMAX (sizeof ((struct sockaddr_un *)0)->sun_path) +#endif + +DNS_NOTUSED static void *dns_sa_path(void *sa, socklen_t *size) { + switch (dns_sa_family(sa)) { +#if DNS_HAVE_SOCKADDR_UN + case AF_UNIX: { + char *path = ((struct sockaddr_un *)sa)->sun_path; + + if (size) + *size = dns_strnlen(path, DNS_SUNPATHMAX); + + return path; + } +#endif + default: + if (size) + *size = 0; + + return NULL; + } +} /* dns_sa_path() */ + + +static int dns_sa_cmp(void *a, void *b) { + int cmp, af; + + if ((cmp = dns_sa_family(a) - dns_sa_family(b))) + return cmp; + + switch ((af = dns_sa_family(a))) { + case AF_INET: { + struct in_addr *a4, *b4; + + if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) + return cmp; + + a4 = dns_sa_addr(af, a, NULL); + b4 = dns_sa_addr(af, b, NULL); + + if (ntohl(a4->s_addr) < ntohl(b4->s_addr)) + return -1; + if (ntohl(a4->s_addr) > ntohl(b4->s_addr)) + return 1; + + return 0; + } + case AF_INET6: { + struct in6_addr *a6, *b6; + size_t i; + + if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) + return cmp; + + a6 = dns_sa_addr(af, a, NULL); + b6 = dns_sa_addr(af, b, NULL); + + /* XXX: do we need to use in6_clearscope()? */ + for (i = 0; i < sizeof a6->s6_addr; i++) { + if ((cmp = a6->s6_addr[i] - b6->s6_addr[i])) + return cmp; + } + + return 0; + } +#if DNS_HAVE_SOCKADDR_UN + case AF_UNIX: { + char a_path[DNS_SUNPATHMAX + 1], b_path[sizeof a_path]; + + dns_strnlcpy(a_path, sizeof a_path, dns_sa_path(a, NULL), DNS_SUNPATHMAX); + dns_strnlcpy(b_path, sizeof b_path, dns_sa_path(b, NULL), DNS_SUNPATHMAX); + + return strcmp(a_path, b_path); + } +#endif + default: + return -1; + } +} /* dns_sa_cmp() */ + + +#if _WIN32 +static int dns_inet_pton(int af, const void *src, void *dst) { + union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; + + u.sin.sin_family = af; + + if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u })) + return -1; + + switch (af) { + case AF_INET6: + *(struct in6_addr *)dst = u.sin6.sin6_addr; + + return 1; + case AF_INET: + *(struct in_addr *)dst = u.sin.sin_addr; + + return 1; + default: + return 0; + } +} /* dns_inet_pton() */ + +static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) { + union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; + + /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */ + memset(&u, 0, sizeof u); + + u.sin.sin_family = af; + + switch (af) { + case AF_INET6: + u.sin6.sin6_addr = *(struct in6_addr *)src; + break; + case AF_INET: + u.sin.sin_addr = *(struct in_addr *)src; + + break; + default: + return 0; + } + + if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim)) + return 0; + + return dst; +} /* dns_inet_ntop() */ +#else +#define dns_inet_pton(...) inet_pton(__VA_ARGS__) +#define dns_inet_ntop(...) inet_ntop(__VA_ARGS__) +#endif + + +static dns_error_t dns_pton(int af, const void *src, void *dst) { + switch (dns_inet_pton(af, src, dst)) { + case 1: + return 0; + case -1: + return dns_soerr(); + default: + return DNS_EADDRESS; + } +} /* dns_pton() */ + + +static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) { + return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr(); +} /* dns_ntop() */ + + +size_t dns_strlcpy(char *dst, const char *src, size_t lim) { + char *d = dst; + char *e = &dst[lim]; + const char *s = src; + + if (d < e) { + do { + if ('\0' == (*d++ = *s++)) + return s - src - 1; + } while (d < e); + + d[-1] = '\0'; + } + + while (*s++ != '\0') + ;; + + return s - src - 1; +} /* dns_strlcpy() */ + + +size_t dns_strlcat(char *dst, const char *src, size_t lim) { + char *d = memchr(dst, '\0', lim); + char *e = &dst[lim]; + const char *s = src; + const char *p; + + if (d && d < e) { + do { + if ('\0' == (*d++ = *s++)) + return d - dst - 1; + } while (d < e); + + d[-1] = '\0'; + } + + p = s; + + while (*s++ != '\0') + ;; + + return lim + (s - p - 1); +} /* dns_strlcat() */ + + +static void *dns_reallocarray(void *p, size_t nmemb, size_t size, dns_error_t *error) { + void *rp; + + if (nmemb > 0 && SIZE_MAX / nmemb < size) { + *error = EOVERFLOW; + return NULL; + } + + if (!(rp = realloc(p, nmemb * size))) + *error = (errno)? errno : EINVAL; + + return rp; +} /* dns_reallocarray() */ + + +#if _WIN32 + +static char *dns_strsep(char **sp, const char *delim) { + char *p; + + if (!(p = *sp)) + return 0; + + *sp += strcspn(p, delim); + + if (**sp != '\0') { + **sp = '\0'; + ++*sp; + } else + *sp = NULL; + + return p; +} /* dns_strsep() */ + +#else +#define dns_strsep(...) strsep(__VA_ARGS__) +#endif + + +#if _WIN32 +#define strcasecmp(...) _stricmp(__VA_ARGS__) +#define strncasecmp(...) _strnicmp(__VA_ARGS__) +#endif + + +static inline _Bool dns_isalpha(unsigned char c) { + return isalpha(c); +} /* dns_isalpha() */ + +static inline _Bool dns_isdigit(unsigned char c) { + return isdigit(c); +} /* dns_isdigit() */ + +static inline _Bool dns_isalnum(unsigned char c) { + return isalnum(c); +} /* dns_isalnum() */ + +static inline _Bool dns_isspace(unsigned char c) { + return isspace(c); +} /* dns_isspace() */ + +static inline _Bool dns_isgraph(unsigned char c) { + return isgraph(c); +} /* dns_isgraph() */ + + +static int dns_poll(int fd, short events, int timeout) { + fd_set rset, wset; + + if (!events) + return 0; + + if (fd < 0 || (unsigned)fd >= FD_SETSIZE) + return EINVAL; + + FD_ZERO(&rset); + FD_ZERO(&wset); + + if (events & DNS_POLLIN) + FD_SET(fd, &rset); + + if (events & DNS_POLLOUT) + FD_SET(fd, &wset); + + select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL); + + return 0; +} /* dns_poll() */ + + +#if !_WIN32 +DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) { +#if DNS_THREAD_SAFE + return pthread_sigmask(how, set, oset); +#else + return (0 == sigprocmask(how, set, oset))? 0 : errno; +#endif +} /* dns_sigmask() */ +#endif + + +static size_t dns_send(int fd, const void *src, size_t len, int flags, dns_error_t *error) { + long n = send(fd, src, len, flags); + + if (n < 0) { + *error = dns_soerr(); + return 0; + } else { + *error = 0; + return n; + } +} /* dns_send() */ + +static size_t dns_recv(int fd, void *dst, size_t lim, int flags, dns_error_t *error) { + long n = recv(fd, dst, lim, flags); + + if (n < 0) { + *error = dns_soerr(); + return 0; + } else if (n == 0) { + *error = (lim > 0)? DNS_ECONNFIN : EINVAL; + return 0; + } else { + *error = 0; + return n; + } +} /* dns_recv() */ + +static size_t dns_send_nopipe(int fd, const void *src, size_t len, int flags, dns_error_t *_error) { +#if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE + return dns_send(fd, src, len, flags, _error); +#elif defined MSG_NOSIGNAL + return dns_send(fd, src, len, (flags|MSG_NOSIGNAL), _error); +#elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */ + /* + * SIGPIPE handling similar to the approach described in + * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html + */ + sigset_t pending, blocked, piped; + size_t count; + int error; + + sigemptyset(&pending); + sigpending(&pending); + + if (!sigismember(&pending, SIGPIPE)) { + sigemptyset(&piped); + sigaddset(&piped, SIGPIPE); + sigemptyset(&blocked); + + if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked))) + goto error; + } + + count = dns_send(fd, src, len, flags, &error); + + if (!sigismember(&pending, SIGPIPE)) { + int saved = error; + + if (!count && error == EPIPE) { + while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR) + ;; + } + + if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL))) + goto error; + + error = saved; + } + + *_error = error; + return count; +error: + *_error = error; + return 0; +#else +#error "unable to suppress SIGPIPE" + return dns_send(fd, src, len, flags, _error); +#endif +} /* dns_send_nopipe() */ + + +static dns_error_t dns_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) { + if (0 != connect(fd, addr, addrlen)) + return dns_soerr(); + return 0; +} /* dns_connect() */ + + +#define DNS_FOPEN_STDFLAGS "rwabt+" + +static dns_error_t dns_fopen_addflag(char *dst, const char *src, size_t lim, int fc) { + char *p = dst, *pe = dst + lim; + + /* copy standard flags */ + while (*src && strchr(DNS_FOPEN_STDFLAGS, *src)) { + if (!(p < pe)) + return ENOMEM; + *p++ = *src++; + } + + /* append flag to standard flags */ + if (!(p < pe)) + return ENOMEM; + *p++ = fc; + + /* copy remaining mode string, including '\0' */ + do { + if (!(p < pe)) + return ENOMEM; + } while ((*p++ = *src++)); + + return 0; +} /* dns_fopen_addflag() */ + +static FILE *dns_fopen(const char *path, const char *mode, dns_error_t *_error) { + FILE *fp; + char mode_cloexec[32]; + int error; + + assert(path && mode && *mode); + if (!*path) { + error = EINVAL; + goto error; + } + +#if _WIN32 || _WIN64 + if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'N'))) + goto error; + if (!(fp = fopen(path, mode_cloexec))) + goto syerr; +#else + if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'e'))) + goto error; + if (!(fp = fopen(path, mode_cloexec))) { + if (errno != EINVAL) + goto syerr; + if (!(fp = fopen(path, mode))) + goto syerr; + } +#endif + + return fp; +syerr: + error = dns_syerr(); +error: + *_error = error; + + return NULL; +} /* dns_fopen() */ + + +struct dns_hxd_lines_i { + int pc; + size_t p; +}; + +#define DNS_SM_RESTORE \ + do { \ + pc = state->pc; \ + sp = src + state->p; \ + se = src + len; \ + } while (0) +#define DNS_SM_SAVE \ + do { \ + state->p = sp - src; \ + state->pc = pc; \ + } while (0) + +static size_t dns_hxd_lines(void *dst, size_t lim, const unsigned char *src, size_t len, struct dns_hxd_lines_i *state) { + static const unsigned char hex[] = "0123456789abcdef"; + static const unsigned char tmpl[] = " | |\n"; + unsigned char ln[sizeof tmpl]; + const unsigned char *sp, *se; + unsigned char *h, *g; + unsigned i, n; + int pc; + + DNS_SM_ENTER; + + while (sp < se) { + memcpy(ln, tmpl, sizeof ln); + + h = &ln[2]; + g = &ln[53]; + + for (n = 0; n < 2; n++) { + for (i = 0; i < 8 && se - sp > 0; i++, sp++) { + h[0] = hex[0x0f & (*sp >> 4)]; + h[1] = hex[0x0f & (*sp >> 0)]; + h += 3; + + *g++ = (dns_isgraph(*sp))? *sp : '.'; + } + + h++; + } + + n = dns_strlcpy(dst, (char *)ln, lim); + DNS_SM_YIELD(n); + } + + DNS_SM_EXIT; + DNS_SM_LEAVE; + + return 0; +} + +#undef DNS_SM_SAVE +#undef DNS_SM_RESTORE + +/* + * A R I T H M E T I C R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_CHECK_OVERFLOW(error, r, f, ...) \ + do { \ + uintmax_t _r; \ + *(error) = f(&_r, __VA_ARGS__); \ + *(r) = _r; \ + } while (0) + +static dns_error_t dns_clamp_overflow(uintmax_t *r, uintmax_t n, uintmax_t clamp) { + if (n > clamp) { + *r = clamp; + return ERANGE; + } else { + *r = n; + return 0; + } +} /* dns_clamp_overflow() */ + +static dns_error_t dns_add_overflow(uintmax_t *r, uintmax_t a, uintmax_t b, uintmax_t clamp) { + if (~a < b) { + *r = DNS_PP_MIN(clamp, ~UINTMAX_C(0)); + return ERANGE; + } else { + return dns_clamp_overflow(r, a + b, clamp); + } +} /* dns_add_overflow() */ + +static dns_error_t dns_mul_overflow(uintmax_t *r, uintmax_t a, uintmax_t b, uintmax_t clamp) { + if (a > 0 && UINTMAX_MAX / a < b) { + *r = DNS_PP_MIN(clamp, ~UINTMAX_C(0)); + return ERANGE; + } else { + return dns_clamp_overflow(r, a * b, clamp); + } +} /* dns_mul_overflow() */ + +/* + * F I X E D - S I Z E D B U F F E R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_B_INIT(src, n) { \ + (unsigned char *)(src), \ + (unsigned char *)(src), \ + (unsigned char *)(src) + (n), \ +} + +#define DNS_B_FROM(src, n) DNS_B_INIT((src), (n)) +#define DNS_B_INTO(src, n) DNS_B_INIT((src), (n)) + +struct dns_buf { + const unsigned char *base; + unsigned char *p; + const unsigned char *pe; + dns_error_t error; + size_t overflow; +}; /* struct dns_buf */ + +static inline size_t +dns_b_tell(struct dns_buf *b) +{ + return b->p - b->base; +} + +static inline dns_error_t +dns_b_setoverflow(struct dns_buf *b, size_t n, dns_error_t error) +{ + b->overflow += n; + return b->error = error; +} + +DNS_NOTUSED static struct dns_buf * +dns_b_into(struct dns_buf *b, void *src, size_t n) +{ + *b = (struct dns_buf)DNS_B_INTO(src, n); + + return b; +} + +static dns_error_t +dns_b_putc(struct dns_buf *b, unsigned char uc) +{ + if (!(b->p < b->pe)) + return dns_b_setoverflow(b, 1, DNS_ENOBUFS); + + *b->p++ = uc; + + return 0; +} + +static dns_error_t +dns_b_pputc(struct dns_buf *b, unsigned char uc, size_t p) +{ + size_t pe = b->pe - b->base; + if (pe <= p) + return dns_b_setoverflow(b, p - pe + 1, DNS_ENOBUFS); + + *((unsigned char *)b->base + p) = uc; + + return 0; +} + +static inline dns_error_t +dns_b_put16(struct dns_buf *b, uint16_t u) +{ + return dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); +} + +static inline dns_error_t +dns_b_pput16(struct dns_buf *b, uint16_t u, size_t p) +{ + if (dns_b_pputc(b, u >> 8, p) || dns_b_pputc(b, u >> 0, p + 1)) + return b->error; + + return 0; +} + +DNS_NOTUSED static inline dns_error_t +dns_b_put32(struct dns_buf *b, uint32_t u) +{ + return dns_b_putc(b, u >> 24), dns_b_putc(b, u >> 16), + dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); +} + +static dns_error_t +dns_b_put(struct dns_buf *b, const void *src, size_t len) +{ + size_t n = DNS_PP_MIN((size_t)(b->pe - b->p), len); + + memcpy(b->p, src, n); + b->p += n; + + if (n < len) + return dns_b_setoverflow(b, len - n, DNS_ENOBUFS); + + return 0; +} + +static dns_error_t +dns_b_puts(struct dns_buf *b, const void *src) +{ + return dns_b_put(b, src, strlen(src)); +} + +DNS_NOTUSED static inline dns_error_t +dns_b_fmtju(struct dns_buf *b, const uintmax_t u, const unsigned width) +{ + size_t digits, padding, overflow; + uintmax_t r; + unsigned char *tp, *te, tc; + + digits = 0; + r = u; + do { + digits++; + r /= 10; + } while (r); + + padding = width - DNS_PP_MIN(digits, width); + overflow = (digits + padding) - DNS_PP_MIN((size_t)(b->pe - b->p), (digits + padding)); + + while (padding--) { + dns_b_putc(b, '0'); + } + + digits = 0; + tp = b->p; + r = u; + do { + if (overflow < ++digits) + dns_b_putc(b, '0' + (r % 10)); + r /= 10; + } while (r); + + te = b->p; + while (tp < te) { + tc = *--te; + *te = *tp; + *tp++ = tc; + } + + return b->error; +} + +static void +dns_b_popc(struct dns_buf *b) +{ + if (b->overflow && !--b->overflow) + b->error = 0; + if (b->p > b->base) + b->p--; +} + +static inline const char * +dns_b_tolstring(struct dns_buf *b, size_t *n) +{ + if (b->p < b->pe) { + *b->p = '\0'; + *n = b->p - b->base; + + return (const char *)b->base; + } else if (b->p > b->base) { + if (b->p[-1] != '\0') { + dns_b_setoverflow(b, 1, DNS_ENOBUFS); + b->p[-1] = '\0'; + } + *n = &b->p[-1] - b->base; + + return (const char *)b->base; + } else { + *n = 0; + + return ""; + } +} + +static inline const char * +dns_b_tostring(struct dns_buf *b) +{ + size_t n; + return dns_b_tolstring(b, &n); +} + +static inline size_t +dns_b_strlen(struct dns_buf *b) +{ + size_t n; + dns_b_tolstring(b, &n); + return n; +} + +static inline size_t +dns_b_strllen(struct dns_buf *b) +{ + size_t n = dns_b_strlen(b); + return n + b->overflow; +} + +DNS_NOTUSED static const struct dns_buf * +dns_b_from(const struct dns_buf *b, const void *src, size_t n) +{ + *(struct dns_buf *)b = (struct dns_buf)DNS_B_FROM(src, n); + + return b; +} + +static inline int +dns_b_getc(const struct dns_buf *_b, const int eof) +{ + struct dns_buf *b = (struct dns_buf *)_b; + + if (!(b->p < b->pe)) + return dns_b_setoverflow(b, 1, DNS_EILLEGAL), eof; + + return *b->p++; +} + +static inline intmax_t +dns_b_get16(const struct dns_buf *b, const intmax_t eof) +{ + intmax_t n; + + n = (dns_b_getc(b, 0) << 8); + n |= (dns_b_getc(b, 0) << 0); + + return (!b->overflow)? n : eof; +} + +DNS_NOTUSED static inline intmax_t +dns_b_get32(const struct dns_buf *b, const intmax_t eof) +{ + intmax_t n; + + n = (dns_b_get16(b, 0) << 16); + n |= (dns_b_get16(b, 0) << 0); + + return (!b->overflow)? n : eof; +} + +static inline dns_error_t +dns_b_move(struct dns_buf *dst, const struct dns_buf *_src, size_t n) +{ + struct dns_buf *src = (struct dns_buf *)_src; + size_t src_n = DNS_PP_MIN((size_t)(src->pe - src->p), n); + size_t src_r = n - src_n; + + dns_b_put(dst, src->p, src_n); + src->p += src_n; + + if (src_r) + return dns_b_setoverflow(src, src_r, DNS_EILLEGAL); + + return dst->error; +} + +/* + * T I M E R O U T I N E S + * + * Most functions still rely on the older time routines defined in the + * utility routines section, above. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_TIME_C(n) UINT64_C(n) +#define DNS_TIME_INF (~DNS_TIME_C(0)) + +typedef uint64_t dns_time_t; +typedef dns_time_t dns_microseconds_t; + +static dns_error_t dns_time_add(dns_time_t *r, dns_time_t a, dns_time_t b) { + int error; + DNS_CHECK_OVERFLOW(&error, r, dns_add_overflow, a, b, DNS_TIME_INF); + return error; +} + +static dns_error_t dns_time_mul(dns_time_t *r, dns_time_t a, dns_time_t b) { + int error; + DNS_CHECK_OVERFLOW(&error, r, dns_mul_overflow, a, b, DNS_TIME_INF); + return error; +} + +static dns_error_t dns_time_diff(dns_time_t *r, dns_time_t a, dns_time_t b) { + if (a < b) { + *r = DNS_TIME_C(0); + return ERANGE; + } else { + *r = a - b; + return 0; + } +} + +static dns_microseconds_t dns_ts2us(const struct timespec *ts, _Bool rup) { + if (ts) { + dns_time_t sec = DNS_PP_MAX(0, ts->tv_sec); + dns_time_t nsec = DNS_PP_MAX(0, ts->tv_nsec); + dns_time_t usec = nsec / 1000; + dns_microseconds_t r; + + if (rup && nsec % 1000 > 0) + usec++; + dns_time_mul(&r, sec, DNS_TIME_C(1000000)); + dns_time_add(&r, r, usec); + + return r; + } else { + return DNS_TIME_INF; + } +} /* dns_ts2us() */ + +static struct timespec *dns_tv2ts(struct timespec *ts, const struct timeval *tv) { + if (tv) { + ts->tv_sec = tv->tv_sec; + ts->tv_nsec = tv->tv_usec * 1000; + + return ts; + } else { + return NULL; + } +} /* dns_tv2ts() */ + +static size_t dns_utime_print(void *_dst, size_t lim, dns_microseconds_t us) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_fmtju(&dst, us / 1000000, 1); + dns_b_putc(&dst, '.'); + dns_b_fmtju(&dst, us % 1000000, 6); + + return dns_b_strllen(&dst); +} /* dns_utime_print() */ + +/* + * P A C K E T R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +unsigned dns_p_count(struct dns_packet *P, enum dns_section section) { + unsigned count; + + switch (section) { + case DNS_S_QD: + return ntohs(dns_header(P)->qdcount); + case DNS_S_AN: + return ntohs(dns_header(P)->ancount); + case DNS_S_NS: + return ntohs(dns_header(P)->nscount); + case DNS_S_AR: + return ntohs(dns_header(P)->arcount); + default: + count = 0; + + if (section & DNS_S_QD) + count += ntohs(dns_header(P)->qdcount); + if (section & DNS_S_AN) + count += ntohs(dns_header(P)->ancount); + if (section & DNS_S_NS) + count += ntohs(dns_header(P)->nscount); + if (section & DNS_S_AR) + count += ntohs(dns_header(P)->arcount); + + return count; + } +} /* dns_p_count() */ + + +struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) { + if (!P) + return 0; + + assert(size >= offsetof(struct dns_packet, data) + 12); + + memset(P, 0, sizeof *P); + P->size = size - offsetof(struct dns_packet, data); + P->end = 12; + + memset(P->data, '\0', 12); + + return P; +} /* dns_p_init() */ + + +static struct dns_packet *dns_p_reset(struct dns_packet *P) { + return dns_p_init(P, offsetof(struct dns_packet, data) + P->size); +} /* dns_p_reset() */ + + +static unsigned short dns_p_qend(struct dns_packet *P) { + unsigned short qend = 12; + unsigned i, count = dns_p_count(P, DNS_S_QD); + + for (i = 0; i < count && qend < P->end; i++) { + if (P->end == (qend = dns_d_skip(qend, P))) + goto invalid; + + if (P->end - qend < 4) + goto invalid; + + qend += 4; + } + + return DNS_PP_MIN(qend, P->end); +invalid: + return P->end; +} /* dns_p_qend() */ + + +struct dns_packet *dns_p_make(size_t len, int *error) { + struct dns_packet *P; + size_t size = dns_p_calcsize(len); + + if (!(P = dns_p_init(malloc(size), size))) + *error = dns_syerr(); + + return P; +} /* dns_p_make() */ + + +static void dns_p_free(struct dns_packet *P) { + free(P); +} /* dns_p_free() */ + + +/* convience routine to free any existing packet before storing new packet */ +static struct dns_packet *dns_p_setptr(struct dns_packet **dst, struct dns_packet *src) { + dns_p_free(*dst); + + *dst = src; + + return src; +} /* dns_p_setptr() */ + + +static struct dns_packet *dns_p_movptr(struct dns_packet **dst, struct dns_packet **src) { + dns_p_setptr(dst, *src); + + *src = NULL; + + return *dst; +} /* dns_p_movptr() */ + + +int dns_p_grow(struct dns_packet **P) { + struct dns_packet *tmp; + size_t size; + int error; + + if (!*P) { + if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error))) + return error; + + return 0; + } + + size = dns_p_sizeof(*P); + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size++; + + if (size > 65536) + return DNS_ENOBUFS; + + if (!(tmp = realloc(*P, dns_p_calcsize(size)))) + return dns_syerr(); + + tmp->size = size; + *P = tmp; + + return 0; +} /* dns_p_grow() */ + + +struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) { + if (!P) + return 0; + + P->end = DNS_PP_MIN(P->size, P0->end); + + memcpy(P->data, P0->data, P->end); + + return P; +} /* dns_p_copy() */ + + +struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) { + size_t bufsiz = DNS_PP_MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0)); + struct dns_packet *M; + enum dns_section section; + struct dns_rr rr, mr; + int error, copy; + + if (!A && B) { + A = B; + Amask = Bmask; + B = 0; + } + +merge: + if (!(M = dns_p_make(bufsiz, &error))) + goto error; + + for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) { + if (A && (section & Amask)) { + dns_rr_foreach(&rr, A, .section = section) { + if ((error = dns_rr_copy(M, &rr, A))) + goto error; + } + } + + if (B && (section & Bmask)) { + dns_rr_foreach(&rr, B, .section = section) { + copy = 1; + + dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) { + if (!(copy = dns_rr_cmp(&rr, B, &mr, M))) + break; + } + + if (copy && (error = dns_rr_copy(M, &rr, B))) + goto error; + } + } + } + + return M; +error: + dns_p_setptr(&M, NULL); + + if (error == DNS_ENOBUFS && bufsiz < 65535) { + bufsiz = DNS_PP_MIN(65535, bufsiz * 2); + + goto merge; + } + + *error_ = error; + + return 0; +} /* dns_p_merge() */ + + +static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t); + +void dns_p_dictadd(struct dns_packet *P, unsigned short dn) { + unsigned short lp, lptr, i; + + lp = dn; + + while (lp < P->end) { + if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) { + lptr = ((0x3f & P->data[lp + 0]) << 8) + | ((0xff & P->data[lp + 1]) << 0); + + for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { + if (P->dict[i] == lptr) { + P->dict[i] = dn; + + return; + } + } + } + + lp = dns_l_skip(lp, P->data, P->end); + } + + for (i = 0; i < lengthof(P->dict); i++) { + if (!P->dict[i]) { + P->dict[i] = dn; + + break; + } + } +} /* dns_p_dictadd() */ + + +int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) { + size_t end = P->end; + int error; + + if ((error = dns_d_push(P, dn, dnlen))) + goto error; + + if (P->size - P->end < 4) + goto nobufs; + + P->data[P->end++] = 0xff & (type >> 8); + P->data[P->end++] = 0xff & (type >> 0); + + P->data[P->end++] = 0xff & (class >> 8); + P->data[P->end++] = 0xff & (class >> 0); + + if (section == DNS_S_QD) + goto update; + + if (P->size - P->end < 6) + goto nobufs; + + if (type != DNS_T_OPT) + ttl = DNS_PP_MIN(ttl, 0x7fffffffU); + P->data[P->end++] = ttl >> 24; + P->data[P->end++] = ttl >> 16; + P->data[P->end++] = ttl >> 8; + P->data[P->end++] = ttl >> 0; + + if ((error = dns_any_push(P, (union dns_any *)any, type))) + goto error; + +update: + switch (section) { + case DNS_S_QD: + if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR)) + goto order; + + if (!P->memo.qd.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1); + + P->memo.qd.end = P->end; + P->memo.an.base = P->end; + P->memo.an.end = P->end; + P->memo.ns.base = P->end; + P->memo.ns.end = P->end; + P->memo.ar.base = P->end; + P->memo.ar.end = P->end; + + break; + case DNS_S_AN: + if (dns_p_count(P, DNS_S_NS|DNS_S_AR)) + goto order; + + if (!P->memo.an.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1); + + P->memo.an.end = P->end; + P->memo.ns.base = P->end; + P->memo.ns.end = P->end; + P->memo.ar.base = P->end; + P->memo.ar.end = P->end; + + break; + case DNS_S_NS: + if (dns_p_count(P, DNS_S_AR)) + goto order; + + if (!P->memo.ns.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1); + + P->memo.ns.end = P->end; + P->memo.ar.base = P->end; + P->memo.ar.end = P->end; + + break; + case DNS_S_AR: + if (!P->memo.ar.base && (error = dns_p_study(P))) + goto error; + + dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1); + + P->memo.ar.end = P->end; + + if (type == DNS_T_OPT && !P->memo.opt.p) { + P->memo.opt.p = end; + P->memo.opt.maxudp = class; + P->memo.opt.ttl = ttl; + } + + break; + default: + error = DNS_ESECTION; + + goto error; + } /* switch() */ + + return 0; +nobufs: + error = DNS_ENOBUFS; + + goto error; +order: + error = DNS_EORDER; + + goto error; +error: + P->end = end; + + return error; +} /* dns_p_push() */ + +#define DNS_SM_RESTORE do { pc = state->pc; error = state->error; } while (0) +#define DNS_SM_SAVE do { state->error = error; state->pc = pc; } while (0) + +struct dns_p_lines_i { + int pc; + enum dns_section section; + struct dns_rr rr; + int error; +}; + +static size_t dns_p_lines_fmt(void *dst, size_t lim, dns_error_t *_error, const char *fmt, ...) { + va_list ap; + int error = 0, n; + + va_start(ap, fmt); + if ((n = vsnprintf(dst, lim, fmt, ap)) < 0) + error = errno; + va_end(ap); + + *_error = error; + return DNS_PP_MAX(n, 0); +} /* dns_p_lines_fmt() */ + +#define DNS_P_LINE(...) \ + do { \ + len = dns_p_lines_fmt(dst, lim, &error, __VA_ARGS__); \ + if (len == 0 && error) \ + goto error; \ + DNS_SM_YIELD(len); \ + } while (0) + +static size_t dns_p_lines(void *dst, size_t lim, dns_error_t *_error, struct dns_packet *P, struct dns_rr_i *I, struct dns_p_lines_i *state) { + int error, pc; + size_t len; + + *_error = 0; + + DNS_SM_ENTER; + + DNS_P_LINE(";; [HEADER]\n"); + DNS_P_LINE(";; qid : %d\n", ntohs(dns_header(P)->qid)); + DNS_P_LINE(";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); + DNS_P_LINE(";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); + DNS_P_LINE(";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); + DNS_P_LINE(";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); + DNS_P_LINE(";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); + DNS_P_LINE(";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); + DNS_P_LINE(";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); + + while (dns_rr_grep(&state->rr, 1, I, P, &error)) { + if (state->section != state->rr.section) { + DNS_P_LINE("\n"); + DNS_P_LINE(";; [%s:%d]\n", dns_strsection(state->rr.section), dns_p_count(P, state->rr.section)); + } + + if (!(len = dns_rr_print(dst, lim, &state->rr, P, &error))) + goto error; + dns_strlcat(dst, "\n", lim); + DNS_SM_YIELD(len + 1); + + state->section = state->rr.section; + } + + if (error) + goto error; + + DNS_SM_EXIT; +error: + for (;;) { + *_error = error; + DNS_SM_YIELD(0); + } + + DNS_SM_LEAVE; + + *_error = 0; + return 0; +} /* dns_p_lines() */ + +#undef DNS_P_LINE +#undef DNS_SM_SAVE +#undef DNS_SM_RESTORE + +static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { + struct dns_p_lines_i lines = { 0 }; + char line[sizeof (union dns_any) * 2]; + size_t len; + int error; + + while ((len = dns_p_lines(line, sizeof line, &error, P, I, &lines))) { + if (len < sizeof line) { + fwrite(line, 1, len, fp); + } else { + fwrite(line, 1, sizeof line - 1, fp); + fputc('\n', fp); + } + } +} /* dns_p_dump3() */ + + +void dns_p_dump(struct dns_packet *P, FILE *fp) { + dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp); +} /* dns_p_dump() */ + + +static void dns_s_unstudy(struct dns_s_memo *m) + { m->base = 0; m->end = 0; } + +static void dns_m_unstudy(struct dns_p_memo *m) { + dns_s_unstudy(&m->qd); + dns_s_unstudy(&m->an); + dns_s_unstudy(&m->ns); + dns_s_unstudy(&m->ar); + m->opt.p = 0; + m->opt.maxudp = 0; + m->opt.ttl = 0; +} /* dns_m_unstudy() */ + +static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned short base, struct dns_packet *P) { + unsigned short count, rp; + + count = dns_p_count(P, section); + + for (rp = base; count && rp < P->end; count--) + rp = dns_rr_skip(rp, P); + + m->base = base; + m->end = rp; + + return 0; +} /* dns_s_study() */ + +static int dns_m_study(struct dns_p_memo *m, struct dns_packet *P) { + struct dns_rr rr; + int error; + + if ((error = dns_s_study(&m->qd, DNS_S_QD, 12, P))) + goto error; + if ((error = dns_s_study(&m->an, DNS_S_AN, m->qd.end, P))) + goto error; + if ((error = dns_s_study(&m->ns, DNS_S_NS, m->an.end, P))) + goto error; + if ((error = dns_s_study(&m->ar, DNS_S_AR, m->ns.end, P))) + goto error; + + m->opt.p = 0; + m->opt.maxudp = 0; + m->opt.ttl = 0; + dns_rr_foreach(&rr, P, .type = DNS_T_OPT, .section = DNS_S_AR) { + m->opt.p = rr.dn.p; + m->opt.maxudp = rr.class; + m->opt.ttl = rr.ttl; + break; + } + + return 0; +error: + dns_m_unstudy(m); + + return error; +} /* dns_m_study() */ + +int dns_p_study(struct dns_packet *P) { + return dns_m_study(&P->memo, P); +} /* dns_p_study() */ + + +enum dns_rcode dns_p_rcode(struct dns_packet *P) { + return 0xfff & ((P->memo.opt.ttl >> 20) | dns_header(P)->rcode); +} /* dns_p_rcode() */ + + +/* + * Q U E R Y P A C K E T R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_Q_RD 0x1 /* recursion desired */ +#define DNS_Q_EDNS0 0x2 /* include OPT RR */ + +static dns_error_t +dns_q_make2(struct dns_packet **_Q, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass, int qflags) +{ + struct dns_packet *Q = NULL; + int error; + + if (dns_p_movptr(&Q, _Q)) { + dns_p_reset(Q); + } else if (!(Q = dns_p_make(DNS_P_QBUFSIZ, &error))) { + goto error; + } + + if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, qtype, qclass, 0, 0))) + goto error; + + dns_header(Q)->rd = !!(qflags & DNS_Q_RD); + + if (qflags & DNS_Q_EDNS0) { + struct dns_opt opt = DNS_OPT_INIT(&opt); + + opt.version = 0; /* RFC 6891 version */ + opt.maxudp = 4096; + + if ((error = dns_p_push(Q, DNS_S_AR, ".", 1, DNS_T_OPT, dns_opt_class(&opt), dns_opt_ttl(&opt), &opt))) + goto error; + } + + *_Q = Q; + + return 0; +error: + dns_p_free(Q); + + return error; +} + +static dns_error_t +dns_q_make(struct dns_packet **Q, const char *qname, enum dns_type qtype, enum dns_class qclass, int qflags) +{ + return dns_q_make2(Q, qname, strlen(qname), qtype, qclass, qflags); +} + +static dns_error_t +dns_q_remake(struct dns_packet **Q, int qflags) +{ + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + struct dns_rr rr; + int error; + + assert(Q && *Q); + if ((error = dns_rr_parse(&rr, 12, *Q))) + return error; + if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, *Q, &error))) + return error; + if (qlen >= sizeof qname) + return DNS_EILLEGAL; + return dns_q_make2(Q, qname, qlen, rr.type, rr.class, qflags); +} + +/* + * D O M A I N N A M E R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DNS_D_MAXPTRS +#define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */ +#endif + +static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) { + unsigned short len; + unsigned nptrs = 0; + +retry: + if (src >= end) + goto invalid; + + switch (0x03 & (data[src] >> 6)) { + case 0x00: + len = (0x3f & (data[src++])); + + if (end - src < len) + goto invalid; + + if (lim > 0) { + memcpy(dst, &data[src], DNS_PP_MIN(lim, len)); + + dst[DNS_PP_MIN(lim - 1, len)] = '\0'; + } + + *nxt = src + len; + + return len; + case 0x01: + goto invalid; + case 0x02: + goto invalid; + case 0x03: + if (++nptrs > DNS_D_MAXPTRS) + goto invalid; + + if (end - src < 2) + goto invalid; + + src = ((0x3f & data[src + 0]) << 8) + | ((0xff & data[src + 1]) << 0); + + goto retry; + } /* switch() */ + + /* NOT REACHED */ +invalid: + *nxt = end; + + return 0; +} /* dns_l_expand() */ + + +static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) { + unsigned short len; + + if (src >= end) + goto invalid; + + switch (0x03 & (data[src] >> 6)) { + case 0x00: + len = (0x3f & (data[src++])); + + if (end - src < len) + goto invalid; + + return (len)? src + len : end; + case 0x01: + goto invalid; + case 0x02: + goto invalid; + case 0x03: + return end; + } /* switch() */ + + /* NOT REACHED */ +invalid: + return end; +} /* dns_l_skip() */ + + +static _Bool dns_d_isanchored(const void *_src, size_t len) { + const unsigned char *src = _src; + return len > 0 && src[len - 1] == '.'; +} /* dns_d_isanchored() */ + + +static size_t dns_d_ndots(const void *_src, size_t len) { + const unsigned char *p = _src, *pe = p + len; + size_t ndots = 0; + + while ((p = memchr(p, '.', pe - p))) { + ndots++; + p++; + } + + return ndots; +} /* dns_d_ndots() */ + + +static size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) { + unsigned char *dst = dst_; + const unsigned char *src = src_; + size_t dp = 0, sp = 0; + int lc; + + /* trim any leading dot(s) */ + while (sp < len && src[sp] == '.') + sp++; + + for (lc = 0; sp < len; lc = src[sp++]) { + /* trim extra dot(s) */ + if (src[sp] == '.' && lc == '.') + continue; + + if (dp < lim) + dst[dp] = src[sp]; + + dp++; + } + + if ((flags & DNS_D_ANCHOR) && lc != '.') { + if (dp < lim) + dst[dp] = '.'; + + dp++; + } + + if (lim > 0) + dst[DNS_PP_MIN(dp, lim - 1)] = '\0'; + + return dp; +} /* dns_d_trim() */ + + +char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) { + if (flags & DNS_D_TRIM) { + dns_d_trim(dst, lim, src, len, flags); + } if (flags & DNS_D_ANCHOR) { + dns_d_anchor(dst, lim, src, len); + } else { + memmove(dst, src, DNS_PP_MIN(lim, len)); + + if (lim > 0) + ((char *)dst)[DNS_PP_MIN(len, lim - 1)] = '\0'; + } + + return dst; +} /* dns_d_init() */ + + +size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) { + if (len == 0) + return 0; + + memmove(dst, src, DNS_PP_MIN(lim, len)); + + if (((const char *)src)[len - 1] != '.') { + if (len < lim) + ((char *)dst)[len] = '.'; + len++; + } + + if (lim > 0) + ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; + + return len; +} /* dns_d_anchor() */ + + +size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) { + const char *dot; + + /* XXX: Skip any leading dot. Handles cleaving root ".". */ + if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1))) + return 0; + + len -= dot - (const char *)src; + + /* XXX: Unless root, skip the label's trailing dot. */ + if (len > 1) { + src = ++dot; + len--; + } else + src = dot; + + memmove(dst, src, DNS_PP_MIN(lim, len)); + + if (lim > 0) + ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; + + return len; +} /* dns_d_cleave() */ + + +size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error) { + struct { unsigned char *b; size_t p, x; } dst, src; + unsigned char ch = '.'; + + dst.b = dst_; + dst.p = 0; + dst.x = 1; + + src.b = (unsigned char *)src_; + src.p = 0; + src.x = 0; + + while (src.x < len) { + ch = src.b[src.x]; + + if (ch == '.') { + if (dst.p < lim) + dst.b[dst.p] = (0x3f & (src.x - src.p)); + + dst.p = dst.x++; + src.p = ++src.x; + } else { + if (dst.x < lim) + dst.b[dst.x] = ch; + + dst.x++; + src.x++; + } + } /* while() */ + + if (src.x > src.p) { + if (dst.p < lim) + dst.b[dst.p] = (0x3f & (src.x - src.p)); + + dst.p = dst.x; + } + + if (dst.p > 1) { + if (dst.p < lim) + dst.b[dst.p] = 0x00; + + dst.p++; + } + +#if 1 + if (dst.p < lim) { + struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b; + unsigned i; + + a.p = 0; + + while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) { + for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { + b.p = P->dict[i]; + + while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) { + a.y = a.x; + b.y = b.x; + + while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) { + a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim); + b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end); + } + + if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) { + dst.b[a.p++] = 0xc0 + | (0x3f & (b.p >> 8)); + dst.b[a.p++] = (0xff & (b.p >> 0)); + + /* silence static analyzers */ + dns_assume(a.p > 0); + + return a.p; + } + + b.p = b.x; + } /* while() */ + } /* for() */ + + a.p = a.x; + } /* while() */ + } /* if () */ +#endif + + if (!dst.p) + *error = DNS_EILLEGAL; + + return dst.p; +} /* dns_d_comp() */ + + +unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) { + unsigned short len; + + while (src < P->end) { + switch (0x03 & (P->data[src] >> 6)) { + case 0x00: /* FOLLOWS */ + len = (0x3f & P->data[src++]); + + if (0 == len) { +/* success ==> */ return src; + } else if (P->end - src > len) { + src += len; + + break; + } else + goto invalid; + + /* NOT REACHED */ + case 0x01: /* RESERVED */ + goto invalid; + case 0x02: /* RESERVED */ + goto invalid; + case 0x03: /* POINTER */ + if (P->end - src < 2) + goto invalid; + + src += 2; + +/* success ==> */ return src; + } /* switch() */ + } /* while() */ + +invalid: + return P->end; +} /* dns_d_skip() */ + + +#include + +size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) { + size_t dstp = 0; + unsigned nptrs = 0; + unsigned char len; + + while (src < P->end) { + switch ((0x03 & (P->data[src] >> 6))) { + case 0x00: /* FOLLOWS */ + len = (0x3f & P->data[src]); + + if (0 == len) { + if (dstp == 0) { + if (dstp < lim) + ((unsigned char *)dst)[dstp] = '.'; + + dstp++; + } + + /* NUL terminate */ + if (lim > 0) + ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; + +/* success ==> */ return dstp; + } + + src++; + + if (P->end - src < len) + goto toolong; + + if (dstp < lim) + memcpy(&((unsigned char *)dst)[dstp], &P->data[src], DNS_PP_MIN(len, lim - dstp)); + + src += len; + dstp += len; + + if (dstp < lim) + ((unsigned char *)dst)[dstp] = '.'; + + dstp++; + + nptrs = 0; + + continue; + case 0x01: /* RESERVED */ + goto reserved; + case 0x02: /* RESERVED */ + goto reserved; + case 0x03: /* POINTER */ + if (++nptrs > DNS_D_MAXPTRS) + goto toolong; + + if (P->end - src < 2) + goto toolong; + + src = ((0x3f & P->data[src + 0]) << 8) + | ((0xff & P->data[src + 1]) << 0); + + continue; + } /* switch() */ + } /* while() */ + +toolong: + *error = DNS_EILLEGAL; + + if (lim > 0) + ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; + + return 0; +reserved: + *error = DNS_EILLEGAL; + + if (lim > 0) + ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; + + return 0; +} /* dns_d_expand() */ + + +int dns_d_push(struct dns_packet *P, const void *dn, size_t len) { + size_t lim = P->size - P->end; + unsigned dp = P->end; + int error = DNS_EILLEGAL; /* silence compiler */ + + len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error); + + if (len == 0) + return error; + if (len > lim) + return DNS_ENOBUFS; + + P->end += len; + + dns_p_dictadd(P, dp); + + return 0; +} /* dns_d_push() */ + + +size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) { + char host[DNS_D_MAXNAME + 1]; + struct dns_rr_i i; + struct dns_rr rr; + unsigned depth; + int error; + + if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len)) + { error = ENAMETOOLONG; goto error; } + + for (depth = 0; depth < 7; depth++) { + dns_rr_i_init(memset(&i, 0, sizeof i), P); + + i.section = DNS_S_ALL & ~DNS_S_QD; + i.name = host; + i.type = DNS_T_CNAME; + + if (!dns_rr_grep(&rr, 1, &i, P, &error)) + break; + + if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P))) + goto error; + } + + return dns_strlcpy(dst, host, lim); +error: + *error_ = error; + + return 0; +} /* dns_d_cname() */ + + +/* + * R E S O U R C E R E C O R D R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) { + unsigned char dn[DNS_D_MAXNAME + 1]; + union dns_any any; + size_t len; + int error; + + if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error))) + return error; + else if (len >= sizeof dn) + return DNS_EILLEGAL; + + if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q))) + return error; + + return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any); +} /* dns_rr_copy() */ + + +int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) { + unsigned short p = src; + + if (src >= P->end) + goto invalid; + + rr->dn.p = p; + rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p; + + if (P->end - p < 4) + goto invalid; + + rr->type = ((0xff & P->data[p + 0]) << 8) + | ((0xff & P->data[p + 1]) << 0); + + rr->class = ((0xff & P->data[p + 2]) << 8) + | ((0xff & P->data[p + 3]) << 0); + + p += 4; + + if (src < dns_p_qend(P)) { + rr->section = DNS_S_QUESTION; + + rr->ttl = 0; + rr->rd.p = 0; + rr->rd.len = 0; + + return 0; + } + + if (P->end - p < 4) + goto invalid; + + rr->ttl = ((0xff & P->data[p + 0]) << 24) + | ((0xff & P->data[p + 1]) << 16) + | ((0xff & P->data[p + 2]) << 8) + | ((0xff & P->data[p + 3]) << 0); + if (rr->type != DNS_T_OPT) + rr->ttl = DNS_PP_MIN(rr->ttl, 0x7fffffffU); + + p += 4; + + if (P->end - p < 2) + goto invalid; + + rr->rd.len = ((0xff & P->data[p + 0]) << 8) + | ((0xff & P->data[p + 1]) << 0); + rr->rd.p = p + 2; + + p += 2; + + if (P->end - p < rr->rd.len) + goto invalid; + + return 0; +invalid: + return DNS_EILLEGAL; +} /* dns_rr_parse() */ + + +static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) { + unsigned short rp, rdlen; + + rp = dns_d_skip(src, P); + + if (P->end - rp < 4) + return P->end - src; + + rp += 4; /* TYPE, CLASS */ + + if (rp <= dns_p_qend(P)) + return rp - src; + + if (P->end - rp < 6) + return P->end - src; + + rp += 6; /* TTL, RDLEN */ + + rdlen = ((0xff & P->data[rp - 2]) << 8) + | ((0xff & P->data[rp - 1]) << 0); + + if (P->end - rp < rdlen) + return P->end - src; + + rp += rdlen; + + return rp - src; +} /* dns_rr_len() */ + + +unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) { + return src + dns_rr_len(src, P); +} /* dns_rr_skip() */ + + +static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) { + enum dns_section section; + unsigned count, index; + unsigned short rp; + + if (src >= P->memo.qd.base && src < P->memo.qd.end) + return DNS_S_QD; + if (src >= P->memo.an.base && src < P->memo.an.end) + return DNS_S_AN; + if (src >= P->memo.ns.base && src < P->memo.ns.end) + return DNS_S_NS; + if (src >= P->memo.ar.base && src < P->memo.ar.end) + return DNS_S_AR; + + /* NOTE: Possibly bad memoization. Try it the hard-way. */ + + for (rp = 12, index = 0; rp < src && rp < P->end; index++) + rp = dns_rr_skip(rp, P); + + section = DNS_S_QD; + count = dns_p_count(P, section); + + while (index >= count && section <= DNS_S_AR) { + section <<= 1; + count += dns_p_count(P, section); + } + + return DNS_S_ALL & section; +} /* dns_rr_section() */ + + +static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) { + struct dns_rr rr; + int error; + + if ((error = dns_rr_parse(&rr, src, P))) + return 0; + + return rr.type; +} /* dns_rr_type() */ + + +int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) { + char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1]; + union dns_any any0, any1; + int cmp, error; + size_t len; + + if ((cmp = r0->type - r1->type)) + return cmp; + + if ((cmp = r0->class - r1->class)) + return cmp; + + /* + * FIXME: Do label-by-label comparison to handle illegally long names? + */ + + if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error)) + || len >= sizeof host0) + return -1; + + if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error)) + || len >= sizeof host1) + return 1; + + if ((cmp = strcasecmp(host0, host1))) + return cmp; + + if (DNS_S_QD & (r0->section | r1->section)) { + if (r0->section == r1->section) + return 0; + + return (r0->section == DNS_S_QD)? -1 : 1; + } + + if ((error = dns_any_parse(&any0, r0, P0))) + return -1; + + if ((error = dns_any_parse(&any1, r1, P1))) + return 1; + + return dns_any_cmp(&any0, r0->type, &any1, r1->type); +} /* dns_rr_cmp() */ + + +static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) { + struct dns_rr rr1; + + dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) { + if (0 == dns_rr_cmp(rr0, P0, &rr1, P1)) + return 1; + } + + return 0; +} /* dns_rr_exists() */ + + +static unsigned short dns_rr_offset(struct dns_rr *rr) { + return rr->dn.p; +} /* dns_rr_offset() */ + + +static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) { + if (i->section && !(rr->section & i->section)) + return 0; + + if (i->type && rr->type != i->type && i->type != DNS_T_ALL) + return 0; + + if (i->class && rr->class != i->class && i->class != DNS_C_ANY) + return 0; + + if (i->name) { + char dn[DNS_D_MAXNAME + 1]; + size_t len; + int error; + + if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error)) + || len >= sizeof dn) + return 0; + + if (0 != strcasecmp(dn, i->name)) + return 0; + } + + if (i->data && i->type && rr->section > DNS_S_QD) { + union dns_any rd; + int error; + + if ((error = dns_any_parse(&rd, rr, P))) + return 0; + + if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type)) + return 0; + } + + return 1; +} /* dns_rr_i_match() */ + + +static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) { + unsigned short rp; + struct dns_rr r0, rr; + int error; + + if ((i->section & DNS_S_QD) && P->memo.qd.base) + rp = P->memo.qd.base; + else if ((i->section & DNS_S_AN) && P->memo.an.base) + rp = P->memo.an.base; + else if ((i->section & DNS_S_NS) && P->memo.ns.base) + rp = P->memo.ns.base; + else if ((i->section & DNS_S_AR) && P->memo.ar.base) + rp = P->memo.ar.base; + else + rp = 12; + + for (; rp < P->end; rp = dns_rr_skip(rp, P)) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + r0 = rr; + + goto lower; + } + + return P->end; +lower: + if (i->sort == &dns_rr_i_packet) + return dns_rr_offset(&r0); + + while ((rp = dns_rr_skip(rp, P)) < P->end) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + if (i->sort(&rr, &r0, i, P) < 0) + r0 = rr; + } + + return dns_rr_offset(&r0); +} /* dns_rr_i_start() */ + + +static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) { + struct dns_rr r0, r1, rr; + int error; + + if ((error = dns_rr_parse(&r0, rp, P))) + return P->end; + + r0.section = dns_rr_section(rp, P); + + rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12; + + for (; rp < P->end; rp = dns_rr_skip(rp, P)) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + if (i->sort(&rr, &r0, i, P) <= 0) + continue; + + r1 = rr; + + goto lower; + } + + return P->end; +lower: + if (i->sort == &dns_rr_i_packet) + return dns_rr_offset(&r1); + + while ((rp = dns_rr_skip(rp, P)) < P->end) { + if ((error = dns_rr_parse(&rr, rp, P))) + continue; + + rr.section = dns_rr_section(rp, P); + + if (!dns_rr_i_match(&rr, i, P)) + continue; + + if (i->sort(&rr, &r0, i, P) <= 0) + continue; + + if (i->sort(&rr, &r1, i, P) >= 0) + continue; + + r1 = rr; + } + + return dns_rr_offset(&r1); +} /* dns_rr_i_skip() */ + + +int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + (void)i; + (void)P; + + return (int)a->dn.p - (int)b->dn.p; +} /* dns_rr_i_packet() */ + + +int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + int cmp; + + (void)i; + + if ((cmp = a->section - b->section)) + return cmp; + + if (a->type != b->type) + return (int)a->dn.p - (int)b->dn.p; + + return dns_rr_cmp(a, P, b, P); +} /* dns_rr_i_order() */ + + +int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + int cmp; + + (void)i; + (void)P; + + while (!i->state.regs[0]) + i->state.regs[0] = dns_random(); + + if ((cmp = a->section - b->section)) + return cmp; + + return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]); +} /* dns_rr_i_shuffle() */ + + +struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) { + static const struct dns_rr_i i_initializer; + + (void)P; + + i->state = i_initializer.state; + i->saved = i->state; + + return i; +} /* dns_rr_i_init() */ + + +unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) { + unsigned count = 0; + int error; + + switch (i->state.exec) { + case 0: + if (!i->sort) + i->sort = &dns_rr_i_packet; + + i->state.next = dns_rr_i_start(i, P); + i->state.exec++; + + /* FALL THROUGH */ + case 1: + while (count < lim && i->state.next < P->end) { + if ((error = dns_rr_parse(rr, i->state.next, P))) + goto error; + + rr->section = dns_rr_section(i->state.next, P); + + rr++; + count++; + i->state.count++; + + i->state.next = dns_rr_i_skip(i->state.next, i, P); + } /* while() */ + + break; + } /* switch() */ + + return count; +error: + *error_ = error; + + return count; +} /* dns_rr_grep() */ + + +size_t dns_rr_print(void *_dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *_error) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + union dns_any any; + size_t n; + int error; + + if (rr->section == DNS_S_QD) + dns_b_putc(&dst, ';'); + + if (!(n = dns_d_expand(any.ns.host, sizeof any.ns.host, rr->dn.p, P, &error))) + goto error; + dns_b_put(&dst, any.ns.host, DNS_PP_MIN(n, sizeof any.ns.host - 1)); + + if (rr->section != DNS_S_QD) { + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, rr->ttl, 0); + } + + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, dns_strclass(rr->class)); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, dns_strtype(rr->type)); + + if (rr->section == DNS_S_QD) + goto epilog; + + dns_b_putc(&dst, ' '); + + if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P))) + goto error; + + n = dns_any_print(dst.p, dst.pe - dst.p, &any, rr->type); + dst.p += DNS_PP_MIN(n, (size_t)(dst.pe - dst.p)); +epilog: + return dns_b_strllen(&dst); +error: + *_error = error; + + return 0; +} /* dns_rr_print() */ + + +int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) { + unsigned long addr; + + if (rr->rd.len != 4) + return DNS_EILLEGAL; + + addr = ((0xffU & P->data[rr->rd.p + 0]) << 24) + | ((0xffU & P->data[rr->rd.p + 1]) << 16) + | ((0xffU & P->data[rr->rd.p + 2]) << 8) + | ((0xffU & P->data[rr->rd.p + 3]) << 0); + + a->addr.s_addr = htonl(addr); + + return 0; +} /* dns_a_parse() */ + + +int dns_a_push(struct dns_packet *P, struct dns_a *a) { + unsigned long addr; + + if (P->size - P->end < 6) + return DNS_ENOBUFS; + + P->data[P->end++] = 0x00; + P->data[P->end++] = 0x04; + + addr = ntohl(a->addr.s_addr); + + P->data[P->end++] = 0xffU & (addr >> 24); + P->data[P->end++] = 0xffU & (addr >> 16); + P->data[P->end++] = 0xffU & (addr >> 8); + P->data[P->end++] = 0xffU & (addr >> 0); + + return 0; +} /* dns_a_push() */ + + +size_t dns_a_arpa(void *_dst, size_t lim, const struct dns_a *a) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned long octets = ntohl(a->addr.s_addr); + unsigned i; + + for (i = 0; i < 4; i++) { + dns_b_fmtju(&dst, 0xff & octets, 0); + dns_b_putc(&dst, '.'); + octets >>= 8; + } + + dns_b_puts(&dst, "in-addr.arpa."); + + return dns_b_strllen(&dst); +} /* dns_a_arpa() */ + + +int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) { + if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr)) + return -1; + if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr)) + return 1; + + return 0; +} /* dns_a_cmp() */ + + +size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) { + char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0"; + + dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr); + + return dns_strlcpy(dst, addr, lim); +} /* dns_a_print() */ + + +int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) { + if (rr->rd.len != sizeof aaaa->addr.s6_addr) + return DNS_EILLEGAL; + + memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr); + + return 0; +} /* dns_aaaa_parse() */ + + +int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) { + if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr) + return DNS_ENOBUFS; + + P->data[P->end++] = 0x00; + P->data[P->end++] = 0x10; + + memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr); + + P->end += sizeof aaaa->addr.s6_addr; + + return 0; +} /* dns_aaaa_push() */ + + +int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) { + unsigned i; + int cmp; + + for (i = 0; i < lengthof(a->addr.s6_addr); i++) { + if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i]))) + return cmp; + } + + return 0; +} /* dns_aaaa_cmp() */ + + +size_t dns_aaaa_arpa(void *_dst, size_t lim, const struct dns_aaaa *aaaa) { + static const unsigned char hex[16] = "0123456789abcdef"; + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned nyble; + int i, j; + + for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) { + nyble = aaaa->addr.s6_addr[i]; + + for (j = 0; j < 2; j++) { + dns_b_putc(&dst, hex[0x0f & nyble]); + dns_b_putc(&dst, '.'); + nyble >>= 4; + } + } + + dns_b_puts(&dst, "ip6.arpa."); + + return dns_b_strllen(&dst); +} /* dns_aaaa_arpa() */ + + +size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) { + char addr[INET6_ADDRSTRLEN + 1] = "::"; + + dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr); + + return dns_strlcpy(dst, addr, lim); +} /* dns_aaaa_print() */ + + +int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) { + size_t len; + int error; + + if (rr->rd.len < 3) + return DNS_EILLEGAL; + + mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8)) + | (0x00ff & (P->data[rr->rd.p + 1] << 0)); + + if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error))) + return error; + else if (len >= sizeof mx->host) + return DNS_EILLEGAL; + + return 0; +} /* dns_mx_parse() */ + + +int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) { + size_t end, len; + int error; + + if (P->size - P->end < 5) + return DNS_ENOBUFS; + + end = P->end; + P->end += 2; + + P->data[P->end++] = 0xff & (mx->preference >> 8); + P->data[P->end++] = 0xff & (mx->preference >> 0); + + if ((error = dns_d_push(P, mx->host, strlen(mx->host)))) + goto error; + + len = P->end - end - 2; + + P->data[end + 0] = 0xff & (len >> 8); + P->data[end + 1] = 0xff & (len >> 0); + + return 0; +error: + P->end = end; + + return error; +} /* dns_mx_push() */ + + +int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) { + int cmp; + + if ((cmp = a->preference - b->preference)) + return cmp; + + return strcasecmp(a->host, b->host); +} /* dns_mx_cmp() */ + + +size_t dns_mx_print(void *_dst, size_t lim, struct dns_mx *mx) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_fmtju(&dst, mx->preference, 0); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, mx->host); + + return dns_b_strllen(&dst); +} /* dns_mx_print() */ + + +size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) { + return dns_strlcpy(dst, mx->host, lim); +} /* dns_mx_cname() */ + + +int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) { + size_t len; + int error; + + if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error))) + return error; + else if (len >= sizeof ns->host) + return DNS_EILLEGAL; + + return 0; +} /* dns_ns_parse() */ + + +int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) { + size_t end, len; + int error; + + if (P->size - P->end < 3) + return DNS_ENOBUFS; + + end = P->end; + P->end += 2; + + if ((error = dns_d_push(P, ns->host, strlen(ns->host)))) + goto error; + + len = P->end - end - 2; + + P->data[end + 0] = 0xff & (len >> 8); + P->data[end + 1] = 0xff & (len >> 0); + + return 0; +error: + P->end = end; + + return error; +} /* dns_ns_push() */ + + +int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) { + return strcasecmp(a->host, b->host); +} /* dns_ns_cmp() */ + + +size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) { + return dns_strlcpy(dst, ns->host, lim); +} /* dns_ns_print() */ + + +size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) { + return dns_strlcpy(dst, ns->host, lim); +} /* dns_ns_cname() */ + + +int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) { + return dns_ns_parse((struct dns_ns *)cname, rr, P); +} /* dns_cname_parse() */ + + +int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) { + return dns_ns_push(P, (struct dns_ns *)cname); +} /* dns_cname_push() */ + + +int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) { + return strcasecmp(a->host, b->host); +} /* dns_cname_cmp() */ + + +size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) { + return dns_ns_print(dst, lim, (struct dns_ns *)cname); +} /* dns_cname_print() */ + + +size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) { + return dns_strlcpy(dst, cname->host, lim); +} /* dns_cname_cname() */ + + +int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) { + struct { void *dst; size_t lim; } dn[] = + { { soa->mname, sizeof soa->mname }, + { soa->rname, sizeof soa->rname } }; + unsigned *ts[] = + { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum }; + unsigned short rp; + unsigned i, j, n; + int error; + + /* MNAME / RNAME */ + if ((rp = rr->rd.p) >= P->end) + return DNS_EILLEGAL; + + for (i = 0; i < lengthof(dn); i++) { + if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error))) + return error; + else if (n >= dn[i].lim) + return DNS_EILLEGAL; + + if ((rp = dns_d_skip(rp, P)) >= P->end) + return DNS_EILLEGAL; + } + + /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ + for (i = 0; i < lengthof(ts); i++) { + for (j = 0; j < 4; j++, rp++) { + if (rp >= P->end) + return DNS_EILLEGAL; + + *ts[i] <<= 8; + *ts[i] |= (0xff & P->data[rp]); + } + } + + return 0; +} /* dns_soa_parse() */ + + +int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) { + void *dn[] = { soa->mname, soa->rname }; + unsigned ts[] = { (0xffffffff & soa->serial), + (0x7fffffff & soa->refresh), + (0x7fffffff & soa->retry), + (0x7fffffff & soa->expire), + (0xffffffff & soa->minimum) }; + unsigned i, j; + size_t end, len; + int error; + + end = P->end; + + if ((P->end += 2) >= P->size) + goto toolong; + + /* MNAME / RNAME */ + for (i = 0; i < lengthof(dn); i++) { + if ((error = dns_d_push(P, dn[i], strlen(dn[i])))) + goto error; + } + + /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ + for (i = 0; i < lengthof(ts); i++) { + if ((P->end += 4) >= P->size) + goto toolong; + + for (j = 1; j <= 4; j++) { + P->data[P->end - j] = (0xff & ts[i]); + ts[i] >>= 8; + } + } + + len = P->end - end - 2; + P->data[end + 0] = (0xff & (len >> 8)); + P->data[end + 1] = (0xff & (len >> 0)); + + return 0; +toolong: + error = DNS_ENOBUFS; + + /* FALL THROUGH */ +error: + P->end = end; + + return error; +} /* dns_soa_push() */ + + +int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) { + int cmp; + + if ((cmp = strcasecmp(a->mname, b->mname))) + return cmp; + + if ((cmp = strcasecmp(a->rname, b->rname))) + return cmp; + + if (a->serial > b->serial) + return -1; + else if (a->serial < b->serial) + return 1; + + if (a->refresh > b->refresh) + return -1; + else if (a->refresh < b->refresh) + return 1; + + if (a->retry > b->retry) + return -1; + else if (a->retry < b->retry) + return 1; + + if (a->expire > b->expire) + return -1; + else if (a->expire < b->expire) + return 1; + + if (a->minimum > b->minimum) + return -1; + else if (a->minimum < b->minimum) + return 1; + + return 0; +} /* dns_soa_cmp() */ + + +size_t dns_soa_print(void *_dst, size_t lim, struct dns_soa *soa) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_puts(&dst, soa->mname); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, soa->rname); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->serial, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->refresh, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->retry, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->expire, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, soa->minimum, 0); + + return dns_b_strllen(&dst); +} /* dns_soa_print() */ + + +int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) { + unsigned short rp; + unsigned i; + size_t n; + int error; + + memset(srv, '\0', sizeof *srv); + + rp = rr->rd.p; + + if (rr->rd.len < 7) + return DNS_EILLEGAL; + + for (i = 0; i < 2; i++, rp++) { + srv->priority <<= 8; + srv->priority |= (0xff & P->data[rp]); + } + + for (i = 0; i < 2; i++, rp++) { + srv->weight <<= 8; + srv->weight |= (0xff & P->data[rp]); + } + + for (i = 0; i < 2; i++, rp++) { + srv->port <<= 8; + srv->port |= (0xff & P->data[rp]); + } + + if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error))) + return error; + else if (n >= sizeof srv->target) + return DNS_EILLEGAL; + + return 0; +} /* dns_srv_parse() */ + + +int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) { + size_t end, len; + int error; + + end = P->end; + + if (P->size - P->end < 2) + goto toolong; + + P->end += 2; + + if (P->size - P->end < 6) + goto toolong; + + P->data[P->end++] = 0xff & (srv->priority >> 8); + P->data[P->end++] = 0xff & (srv->priority >> 0); + + P->data[P->end++] = 0xff & (srv->weight >> 8); + P->data[P->end++] = 0xff & (srv->weight >> 0); + + P->data[P->end++] = 0xff & (srv->port >> 8); + P->data[P->end++] = 0xff & (srv->port >> 0); + + if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error))) + goto error; + else if (P->size - P->end < len) + goto toolong; + + P->end += len; + + if (P->end > 65535) + goto toolong; + + len = P->end - end - 2; + + P->data[end + 0] = 0xff & (len >> 8); + P->data[end + 1] = 0xff & (len >> 0); + + return 0; +toolong: + error = DNS_ENOBUFS; + + /* FALL THROUGH */ +error: + P->end = end; + + return error; +} /* dns_srv_push() */ + + +int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) { + int cmp; + + if ((cmp = a->priority - b->priority)) + return cmp; + + /* + * FIXME: We need some sort of random seed to implement the dynamic + * weighting required by RFC 2782. + */ + if ((cmp = a->weight - b->weight)) + return cmp; + + if ((cmp = a->port - b->port)) + return cmp; + + return strcasecmp(a->target, b->target); +} /* dns_srv_cmp() */ + + +size_t dns_srv_print(void *_dst, size_t lim, struct dns_srv *srv) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + + dns_b_fmtju(&dst, srv->priority, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, srv->weight, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, srv->port, 0); + dns_b_putc(&dst, ' '); + dns_b_puts(&dst, srv->target); + + return dns_b_strllen(&dst); +} /* dns_srv_print() */ + + +size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) { + return dns_strlcpy(dst, srv->target, lim); +} /* dns_srv_cname() */ + + +unsigned int dns_opt_ttl(const struct dns_opt *opt) { + unsigned int ttl = 0; + + ttl |= (0xffU & opt->rcode) << 24; + ttl |= (0xffU & opt->version) << 16; + ttl |= (0xffffU & opt->flags) << 0; + + return ttl; +} /* dns_opt_ttl() */ + + +unsigned short dns_opt_class(const struct dns_opt *opt) { + return opt->maxudp; +} /* dns_opt_class() */ + + +struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) { + assert(size >= offsetof(struct dns_opt, data)); + + opt->size = size - offsetof(struct dns_opt, data); + opt->len = 0; + + opt->rcode = 0; + opt->version = 0; + opt->maxudp = 0; + + return opt; +} /* dns_opt_init() */ + + +static union dns_any *dns_opt_initany(union dns_any *any, size_t size) { + return dns_opt_init(&any->opt, size), any; +} /* dns_opt_initany() */ + + +int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) { + const struct dns_buf src = DNS_B_FROM(&P->data[rr->rd.p], rr->rd.len); + struct dns_buf dst = DNS_B_INTO(opt->data, opt->size); + int error; + + opt->rcode = 0xfff & ((rr->ttl >> 20) | dns_header(P)->rcode); + opt->version = 0xff & (rr->ttl >> 16); + opt->flags = 0xffff & rr->ttl; + opt->maxudp = 0xffff & rr->class; + + while (src.p < src.pe) { + int code, len; + + if (-1 == (code = dns_b_get16(&src, -1))) + return src.error; + if (-1 == (len = dns_b_get16(&src, -1))) + return src.error; + + switch (code) { + default: + dns_b_put16(&dst, code); + dns_b_put16(&dst, len); + if ((error = dns_b_move(&dst, &src, len))) + return error; + break; + } + } + + return 0; +} /* dns_opt_parse() */ + + +int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) { + const struct dns_buf src = DNS_B_FROM(opt->data, opt->len); + struct dns_buf dst = DNS_B_INTO(&P->data[P->end], (P->size - P->end)); + int error; + + /* rdata length (see below) */ + if ((error = dns_b_put16(&dst, 0))) + goto error; + + /* ... push known options here */ + + /* push opaque option data */ + if ((error = dns_b_move(&dst, &src, (size_t)(src.pe - src.p)))) + goto error; + + /* rdata length */ + if ((error = dns_b_pput16(&dst, dns_b_tell(&dst) - 2, 0))) + goto error; + +#if !DNS_DEBUG_OPT_FORMERR + P->end += dns_b_tell(&dst); +#endif + + return 0; +error: + return error; +} /* dns_opt_push() */ + + +int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) { + (void)a; + (void)b; + + return -1; +} /* dns_opt_cmp() */ + + +size_t dns_opt_print(void *_dst, size_t lim, struct dns_opt *opt) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + size_t p; + + dns_b_putc(&dst, '"'); + + for (p = 0; p < opt->len; p++) { + dns_b_putc(&dst, '\\'); + dns_b_fmtju(&dst, opt->data[p], 3); + } + + dns_b_putc(&dst, '"'); + + return dns_b_strllen(&dst); +} /* dns_opt_print() */ + + +int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) { + return dns_ns_parse((struct dns_ns *)ptr, rr, P); +} /* dns_ptr_parse() */ + + +int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) { + return dns_ns_push(P, (struct dns_ns *)ptr); +} /* dns_ptr_push() */ + + +size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) { + switch (af) { + case AF_INET6: + return dns_aaaa_arpa(dst, lim, addr); + case AF_INET: + return dns_a_arpa(dst, lim, addr); + default: { + struct dns_a a; + a.addr.s_addr = INADDR_NONE; + return dns_a_arpa(dst, lim, &a); + } + } +} /* dns_ptr_qname() */ + + +int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) { + return strcasecmp(a->host, b->host); +} /* dns_ptr_cmp() */ + + +size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) { + return dns_ns_print(dst, lim, (struct dns_ns *)ptr); +} /* dns_ptr_print() */ + + +size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) { + return dns_strlcpy(dst, ptr->host, lim); +} /* dns_ptr_cname() */ + + +int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) { + unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len; + + if (pe - p < 2) + return DNS_EILLEGAL; + + fp->algo = P->data[p++]; + fp->type = P->data[p++]; + + switch (fp->type) { + case DNS_SSHFP_SHA1: + if (pe - p < sizeof fp->digest.sha1) + return DNS_EILLEGAL; + + memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1); + + break; + default: + break; + } /* switch() */ + + return 0; +} /* dns_sshfp_parse() */ + + +int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) { + unsigned p = P->end, pe = P->size, n; + + if (pe - p < 4) + return DNS_ENOBUFS; + + p += 2; + P->data[p++] = 0xff & fp->algo; + P->data[p++] = 0xff & fp->type; + + switch (fp->type) { + case DNS_SSHFP_SHA1: + if (pe - p < sizeof fp->digest.sha1) + return DNS_ENOBUFS; + + memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1); + p += sizeof fp->digest.sha1; + + break; + default: + return DNS_EILLEGAL; + } /* switch() */ + + n = p - P->end - 2; + P->data[P->end++] = 0xff & (n >> 8); + P->data[P->end++] = 0xff & (n >> 0); + P->end = p; + + return 0; +} /* dns_sshfp_push() */ + + +int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) { + int cmp; + + if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type)) + return cmp; + + switch (a->type) { + case DNS_SSHFP_SHA1: + return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1); + default: + return 0; + } /* switch() */ + + /* NOT REACHED */ +} /* dns_sshfp_cmp() */ + + +size_t dns_sshfp_print(void *_dst, size_t lim, struct dns_sshfp *fp) { + static const unsigned char hex[16] = "0123456789abcdef"; + struct dns_buf dst = DNS_B_INTO(_dst, lim); + size_t i; + + dns_b_fmtju(&dst, fp->algo, 0); + dns_b_putc(&dst, ' '); + dns_b_fmtju(&dst, fp->type, 0); + dns_b_putc(&dst, ' '); + + switch (fp->type) { + case DNS_SSHFP_SHA1: + for (i = 0; i < sizeof fp->digest.sha1; i++) { + dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 4)]); + dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 0)]); + } + + break; + default: + dns_b_putc(&dst, '0'); + + break; + } /* switch() */ + + return dns_b_strllen(&dst); +} /* dns_sshfp_print() */ + + +struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) { + assert(size > offsetof(struct dns_txt, data)); + + txt->size = size - offsetof(struct dns_txt, data); + txt->len = 0; + + return txt; +} /* dns_txt_init() */ + + +static union dns_any *dns_txt_initany(union dns_any *any, size_t size) { + /* NB: union dns_any is already initialized as struct dns_txt */ + (void)size; + return any; +} /* dns_txt_initany() */ + + +int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) { + struct { unsigned char *b; size_t p, end; } dst, src; + unsigned n; + + dst.b = txt->data; + dst.p = 0; + dst.end = txt->size; + + src.b = P->data; + src.p = rr->rd.p; + src.end = src.p + rr->rd.len; + + while (src.p < src.end) { + n = 0xff & P->data[src.p++]; + + if (src.end - src.p < n || dst.end - dst.p < n) + return DNS_EILLEGAL; + + memcpy(&dst.b[dst.p], &src.b[src.p], n); + + dst.p += n; + src.p += n; + } + + txt->len = dst.p; + + return 0; +} /* dns_txt_parse() */ + + +int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) { + struct { unsigned char *b; size_t p, end; } dst, src; + unsigned n; + + dst.b = P->data; + dst.p = P->end; + dst.end = P->size; + + src.b = txt->data; + src.p = 0; + src.end = txt->len; + + if (dst.end - dst.p < 2) + return DNS_ENOBUFS; + + n = txt->len + ((txt->len + 254) / 255); + + dst.b[dst.p++] = 0xff & (n >> 8); + dst.b[dst.p++] = 0xff & (n >> 0); + + while (src.p < src.end) { + n = DNS_PP_MIN(255, src.end - src.p); + + if (dst.p >= dst.end) + return DNS_ENOBUFS; + + dst.b[dst.p++] = n; + + if (dst.end - dst.p < n) + return DNS_ENOBUFS; + + memcpy(&dst.b[dst.p], &src.b[src.p], n); + + dst.p += n; + src.p += n; + } + + P->end = dst.p; + + return 0; +} /* dns_txt_push() */ + + +int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) { + (void)a; + (void)b; + + return -1; +} /* dns_txt_cmp() */ + + +size_t dns_txt_print(void *_dst, size_t lim, struct dns_txt *txt) { + struct dns_buf src = DNS_B_FROM(txt->data, txt->len); + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + if (src.p < src.pe) { + do { + dns_b_putc(&dst, '"'); + + for (i = 0; i < 256 && src.p < src.pe; i++, src.p++) { + if (*src.p < 32 || *src.p > 126 || *src.p == '"' || *src.p == '\\') { + dns_b_putc(&dst, '\\'); + dns_b_fmtju(&dst, *src.p, 3); + } else { + dns_b_putc(&dst, *src.p); + } + } + + dns_b_putc(&dst, '"'); + dns_b_putc(&dst, ' '); + } while (src.p < src.pe); + + dns_b_popc(&dst); + } else { + dns_b_putc(&dst, '"'); + dns_b_putc(&dst, '"'); + } + + return dns_b_strllen(&dst); +} /* dns_txt_print() */ + + +static const struct dns_rrtype { + enum dns_type type; + const char *name; + union dns_any *(*init)(union dns_any *, size_t); + int (*parse)(); + int (*push)(); + int (*cmp)(); + size_t (*print)(); + size_t (*cname)(); +} dns_rrtypes[] = { + { DNS_T_A, "A", 0, &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0, }, + { DNS_T_AAAA, "AAAA", 0, &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0, }, + { DNS_T_MX, "MX", 0, &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname, }, + { DNS_T_NS, "NS", 0, &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname, }, + { DNS_T_CNAME, "CNAME", 0, &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname, }, + { DNS_T_SOA, "SOA", 0, &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0, }, + { DNS_T_SRV, "SRV", 0, &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname, }, + { DNS_T_OPT, "OPT", &dns_opt_initany, &dns_opt_parse, &dns_opt_push, &dns_opt_cmp, &dns_opt_print, 0, }, + { DNS_T_PTR, "PTR", 0, &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname, }, + { DNS_T_TXT, "TXT", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, + { DNS_T_SPF, "SPF", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, + { DNS_T_SSHFP, "SSHFP", 0, &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0, }, + { DNS_T_AXFR, "AXFR", 0, 0, 0, 0, 0, 0, }, +}; /* dns_rrtypes[] */ + +static const struct dns_rrtype *dns_rrtype(enum dns_type type) { + const struct dns_rrtype *t; + + for (t = dns_rrtypes; t < endof(dns_rrtypes); t++) { + if (t->type == type && t->parse) { + return t; + } + } + + return NULL; +} /* dns_rrtype() */ + + +union dns_any *dns_any_init(union dns_any *any, size_t size) { + dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); + return (union dns_any *)dns_txt_init(&any->rdata, size); +} /* dns_any_init() */ + + +static size_t dns_any_sizeof(union dns_any *any) { + dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); + return offsetof(struct dns_txt, data) + any->rdata.size; +} /* dns_any_sizeof() */ + +static union dns_any *dns_any_reinit(union dns_any *any, const struct dns_rrtype *t) { + return (t->init)? t->init(any, dns_any_sizeof(any)) : any; +} /* dns_any_reinit() */ + +int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) { + const struct dns_rrtype *t; + + if ((t = dns_rrtype(rr->type))) + return t->parse(dns_any_reinit(any, t), rr, P); + + if (rr->rd.len > any->rdata.size) + return DNS_EILLEGAL; + + memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len); + any->rdata.len = rr->rd.len; + + return 0; +} /* dns_any_parse() */ + + +int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) { + const struct dns_rrtype *t; + + if ((t = dns_rrtype(type))) + return t->push(P, any); + + if (P->size - P->end < any->rdata.len + 2) + return DNS_ENOBUFS; + + P->data[P->end++] = 0xff & (any->rdata.len >> 8); + P->data[P->end++] = 0xff & (any->rdata.len >> 0); + + memcpy(&P->data[P->end], any->rdata.data, any->rdata.len); + P->end += any->rdata.len; + + return 0; +} /* dns_any_push() */ + + +int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) { + const struct dns_rrtype *t; + int cmp; + + if ((cmp = x - y)) + return cmp; + + if ((t = dns_rrtype(x))) + return t->cmp(a, b); + + return -1; +} /* dns_any_cmp() */ + + +size_t dns_any_print(void *_dst, size_t lim, union dns_any *any, enum dns_type type) { + const struct dns_rrtype *t; + struct dns_buf src, dst; + + if ((t = dns_rrtype(type))) + return t->print(_dst, lim, any); + + dns_b_from(&src, any->rdata.data, any->rdata.len); + dns_b_into(&dst, _dst, lim); + + dns_b_putc(&dst, '"'); + + while (src.p < src.pe) { + dns_b_putc(&dst, '\\'); + dns_b_fmtju(&dst, *src.p++, 3); + } + + dns_b_putc(&dst, '"'); + + return dns_b_strllen(&dst); +} /* dns_any_print() */ + + +size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) { + const struct dns_rrtype *t; + + if ((t = dns_rrtype(type)) && t->cname) + return t->cname(dst, lim, any); + + return 0; +} /* dns_any_cname() */ + + +/* + * E V E N T T R A C I N G R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include /* DBL_MANT_DIG */ +#include /* PRIu64 */ + +/* for default trace ID generation try to fit in lua_Number, usually double */ +#define DNS_TRACE_ID_BITS DNS_PP_MIN(DBL_MANT_DIG, (sizeof (dns_trace_id_t) * CHAR_BIT)) /* assuming FLT_RADIX == 2 */ +#define DNS_TRACE_ID_MASK (((DNS_TRACE_ID_C(1) << (DNS_TRACE_ID_BITS - 1)) - 1) | (DNS_TRACE_ID_C(1) << (DNS_TRACE_ID_BITS - 1))) +#define DNS_TRACE_ID_PRI PRIu64 + +static inline dns_trace_id_t dns_trace_mkid(void) { + dns_trace_id_t id = 0; + unsigned r; /* return type of dns_random() */ + const size_t id_bit = sizeof id * CHAR_BIT; + const size_t r_bit = sizeof r * CHAR_BIT; + + for (size_t n = 0; n < id_bit; n += r_bit) { + r = dns_random(); + id <<= r_bit; + id |= r; + } + + return DNS_TRACE_ID_MASK & id; +} + +struct dns_trace { + dns_atomic_t refcount; + + FILE *fp; + dns_trace_id_t id; + + struct { + struct dns_trace_cname { + char host[DNS_D_MAXNAME + 1]; + struct sockaddr_storage addr; + } base[4]; + size_t p; + } cnames; +}; + +static void dns_te_initname(struct sockaddr_storage *ss, int fd, int (*f)(int, struct sockaddr *, socklen_t *)) { + socklen_t n = sizeof *ss; + + if (0 != f(fd, (struct sockaddr *)ss, &n)) + goto unspec; + + if (n > sizeof *ss) + goto unspec; + + return; +unspec: + memset(ss, '\0', sizeof *ss); + ss->ss_family = AF_UNSPEC; +} + +static void dns_te_initnames(struct sockaddr_storage *local, struct sockaddr_storage *remote, int fd) { + dns_te_initname(local, fd, &getsockname); + dns_te_initname(remote, fd, &getpeername); +} + +static struct dns_trace_event *dns_te_init(struct dns_trace_event *te, int type) { + /* NB: silence valgrind */ + memset(te, '\0', offsetof(struct dns_trace_event, data)); + te->type = type; + return te; +} + +int dns_trace_abi(void) { + return DNS_TRACE_ABI; +} + +struct dns_trace *dns_trace_open(FILE *fp, dns_error_t *error) { + static const struct dns_trace trace_initializer = { .refcount = 1 }; + struct dns_trace *trace; + + if (!(trace = malloc(sizeof *trace))) + goto syerr; + + *trace = trace_initializer; + + if (fp) { + trace->fp = fp; + } else if (!(fp = tmpfile())) { + goto syerr; + } + + trace->id = dns_trace_mkid(); + + return trace; +syerr: + *error = dns_syerr(); + + dns_trace_close(trace); + + return NULL; +} /* dns_trace_open() */ + +void dns_trace_close(struct dns_trace *trace) { + if (!trace || 1 != dns_trace_release(trace)) + return; + + if (trace->fp) + fclose(trace->fp); + free(trace); +} /* dns_trace_close() */ + +dns_refcount_t dns_trace_acquire(struct dns_trace *trace) { + return dns_atomic_fetch_add(&trace->refcount); +} /* dns_trace_acquire() */ + +static struct dns_trace *dns_trace_acquire_p(struct dns_trace *trace) { + return (trace)? dns_trace_acquire(trace), trace : NULL; +} /* dns_trace_acquire_p() */ + +dns_refcount_t dns_trace_release(struct dns_trace *trace) { + return dns_atomic_fetch_sub(&trace->refcount); +} /* dns_trace_release() */ + +dns_trace_id_t dns_trace_id(struct dns_trace *trace) { + return trace->id; +} /* dns_trace_id() */ + +dns_trace_id_t dns_trace_setid(struct dns_trace *trace, dns_trace_id_t id) { + trace->id = (id)? id : dns_trace_mkid(); + return trace->id; +} /* dns_trace_setid() */ + +struct dns_trace_event *dns_trace_get(struct dns_trace *trace, struct dns_trace_event **tp, dns_error_t *error) { + return dns_trace_fget(tp, trace->fp, error); +} /* dns_trace_get() */ + +dns_error_t dns_trace_put(struct dns_trace *trace, const struct dns_trace_event *te, const void *data, size_t datasize) { + return dns_trace_fput(te, data, datasize, trace->fp); +} /* dns_trace_put() */ + +struct dns_trace_event *dns_trace_tag(struct dns_trace *trace, struct dns_trace_event *te) { + struct timeval tv; + + te->id = trace->id; + gettimeofday(&tv, NULL); + dns_tv2ts(&te->ts, &tv); + te->abi = DNS_TRACE_ABI; + + return te; +} /* dns_trace_tag() */ + +static dns_error_t dns_trace_tag_and_put(struct dns_trace *trace, struct dns_trace_event *te, const void *data, size_t datasize) { + return dns_trace_put(trace, dns_trace_tag(trace, te), data, datasize); +} /* dns_trace_tag_and_put() */ + +struct dns_trace_event *dns_trace_fget(struct dns_trace_event **tp, FILE *fp, dns_error_t *error) { + const size_t headsize = offsetof(struct dns_trace_event, data); + struct dns_trace_event tmp, *te; + size_t n; + + errno = 0; + if (!(n = fread(&tmp, 1, headsize, fp))) + goto none; + if (n < offsetof(struct dns_trace_event, data)) + goto some; + + if (!(te = realloc(*tp, DNS_PP_MAX(headsize, tmp.size)))) { + *error = errno; + return NULL; + } + + *tp = te; + memcpy(te, &tmp, offsetof(struct dns_trace_event, data)); + + if (dns_te_datasize(te)) { + errno = 0; + if (!(n = fread(te->data, 1, dns_te_datasize(te), fp))) + goto none; + if (n < dns_te_datasize(te)) + goto some; + } + + return te; +none: + *error = (ferror(fp))? errno : 0; + return NULL; +some: + *error = 0; + return NULL; +} + +dns_error_t dns_trace_fput(const struct dns_trace_event *te, const void *data, size_t datasize, FILE *fp) { + size_t headsize = offsetof(struct dns_trace_event, data); + struct dns_trace_event tmp; + + memcpy(&tmp, te, headsize); + tmp.size = headsize + datasize; + + /* NB: ignore seek error as fp might not point to a regular file */ + (void)fseek(fp, 0, SEEK_END); + + if (fwrite(&tmp, 1, headsize, fp) < headsize) + return errno; + if (fwrite(data, 1, datasize, fp) < datasize) + return errno; + if (fflush(fp)) + return errno; + + return 0; +} + +static void dns_trace_setcname(struct dns_trace *trace, const char *host, const struct sockaddr *addr) { + struct dns_trace_cname *cname; + if (!trace || !trace->fp) + return; + + cname = &trace->cnames.base[trace->cnames.p]; + dns_strlcpy(cname->host, host, sizeof cname->host); + memcpy(&cname->addr, addr, DNS_PP_MIN(dns_sa_len(addr), sizeof cname->addr)); + + trace->cnames.p = (trace->cnames.p + 1) % lengthof(trace->cnames.base); +} + +static const char *dns_trace_cname(struct dns_trace *trace, const struct sockaddr *addr) { + if (!trace || !trace->fp) + return NULL; + + /* NB: start search from the write cursor to */ + for (const struct dns_trace_cname *cname = trace->cnames.base; cname < endof(trace->cnames.base); cname++) { + if (0 == dns_sa_cmp((struct sockaddr *)addr, (struct sockaddr *)&cname->addr)) + return cname->host; + } + + return NULL; +} + +static void dns_trace_res_submit(struct dns_trace *trace, const char *qname, enum dns_type qtype, enum dns_class qclass, int error) { + struct dns_trace_event te; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_RES_SUBMIT); + dns_strlcpy(te.res_submit.qname, qname, sizeof te.res_submit.qname); + te.res_submit.qtype = qtype; + te.res_submit.qclass = qclass; + te.res_submit.error = error; + dns_trace_tag_and_put(trace, &te, NULL, 0); +} + +static void dns_trace_res_fetch(struct dns_trace *trace, const struct dns_packet *packet, int error) { + struct dns_trace_event te; + const void *data; + size_t datasize; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_RES_FETCH); + data = (packet)? packet->data : NULL; + datasize = (packet)? packet->end : 0; + te.res_fetch.error = error; + dns_trace_tag_and_put(trace, &te, data, datasize); +} + +static void dns_trace_so_submit(struct dns_trace *trace, const struct dns_packet *packet, const struct sockaddr *haddr, int error) { + struct dns_trace_event te; + const char *cname; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_SO_SUBMIT); + memcpy(&te.so_submit.haddr, haddr, DNS_PP_MIN(dns_sa_len(haddr), sizeof te.so_submit.haddr)); + if ((cname = dns_trace_cname(trace, haddr))) + dns_strlcpy(te.so_submit.hname, cname, sizeof te.so_submit.hname); + te.so_submit.error = error; + dns_trace_tag_and_put(trace, &te, packet->data, packet->end); +} + +static void dns_trace_so_verify(struct dns_trace *trace, const struct dns_packet *packet, int error) { + struct dns_trace_event te; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_SO_VERIFY); + te.so_verify.error = error; + dns_trace_tag_and_put(trace, &te, packet->data, packet->end); +} + +static void dns_trace_so_fetch(struct dns_trace *trace, const struct dns_packet *packet, int error) { + struct dns_trace_event te; + const void *data; + size_t datasize; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_SO_FETCH); + data = (packet)? packet->data : NULL; + datasize = (packet)? packet->end : 0; + te.so_fetch.error = error; + dns_trace_tag_and_put(trace, &te, data, datasize); +} + +static void dns_trace_sys_connect(struct dns_trace *trace, int fd, int socktype, const struct sockaddr *dst, int error) { + struct dns_trace_event te; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_SYS_CONNECT); + dns_te_initname(&te.sys_connect.src, fd, &getsockname); + memcpy(&te.sys_connect.dst, dst, DNS_PP_MIN(dns_sa_len(dst), sizeof te.sys_connect.dst)); + te.sys_connect.socktype = socktype; + te.sys_connect.error = error; + dns_trace_tag_and_put(trace, &te, NULL, 0); +} + +static void dns_trace_sys_send(struct dns_trace *trace, int fd, int socktype, const void *data, size_t datasize, int error) { + struct dns_trace_event te; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_SYS_SEND); + dns_te_initnames(&te.sys_send.src, &te.sys_send.dst, fd); + te.sys_send.socktype = socktype; + te.sys_send.error = error; + dns_trace_tag_and_put(trace, &te, data, datasize); +} + +static void dns_trace_sys_recv(struct dns_trace *trace, int fd, int socktype, const void *data, size_t datasize, int error) { + struct dns_trace_event te; + if (!trace || !trace->fp) + return; + + dns_te_init(&te, DNS_TE_SYS_RECV); + dns_te_initnames(&te.sys_recv.dst, &te.sys_recv.src, fd); + te.sys_recv.socktype = socktype; + te.sys_recv.error = error; + dns_trace_tag_and_put(trace, &te, data, datasize); +} + +static dns_error_t dns_trace_dump_packet(struct dns_trace *trace, const char *prefix, const unsigned char *data, size_t datasize, FILE *fp) { + struct dns_packet *packet = NULL; + char *line = NULL, *p; + size_t size = 1, skip = 0; + struct dns_rr_i records; + struct dns_p_lines_i lines; + size_t len, count; + int error; + + if (!(packet = dns_p_make(datasize, &error))) + goto error; + + memcpy(packet->data, data, datasize); + packet->end = datasize; + (void)dns_p_study(packet); +resize: + if (!(p = dns_reallocarray(line, size, 2, &error))) + goto error; + line = p; + size *= 2; + + memset(&records, 0, sizeof records); + memset(&lines, 0, sizeof lines); + count = 0; + + while ((len = dns_p_lines(line, size, &error, packet, &records, &lines))) { + if (!(len < size)) { + skip = count; + goto resize; + } else if (skip <= count) { + fputs(prefix, fp); + fwrite(line, 1, len, fp); + } + count++; + } + + if (error) + goto error; + + error = 0; +error: + free(line); + dns_p_free(packet); + + return error; +} + +static dns_error_t dns_trace_dump_data(struct dns_trace *trace, const char *prefix, const unsigned char *data, size_t datasize, FILE *fp) { + struct dns_hxd_lines_i lines = { 0 }; + char line[128]; + size_t len; + + while ((len = dns_hxd_lines(line, sizeof line, data, datasize, &lines))) { + if (len >= sizeof line) + return EOVERFLOW; /* shouldn't be possible */ + fputs(prefix, fp); + fwrite(line, 1, len, fp); + } + + return 0; +} + +static dns_error_t dns_trace_dump_addr(struct dns_trace *trace, const char *prefix, const struct sockaddr_storage *ss, FILE *fp) { + const void *addr; + const char *path; + socklen_t len; + int error; + + if ((addr = dns_sa_addr(ss->ss_family, (struct sockaddr *)ss, NULL))) { + char ip[INET6_ADDRSTRLEN + 1]; + + if ((error = dns_ntop(ss->ss_family, addr, ip, sizeof ip))) + return error; + fprintf(fp, "%s%s\n", prefix, ip); + } else if ((path = dns_sa_path((struct sockaddr *)ss, &len))) { + fprintf(fp, "%sunix:%.*s", prefix, (int)len, path); + } else { + return EINVAL; + } + + return 0; +} + +static dns_error_t dns_trace_dump_meta(struct dns_trace *trace, const char *prefix, const struct dns_trace_event *te, dns_microseconds_t elapsed, FILE *fp) { + char time_s[48], elapsed_s[48]; + + dns_utime_print(time_s, sizeof time_s, dns_ts2us(&te->ts, 0)); + dns_utime_print(elapsed_s, sizeof elapsed_s, elapsed); + + fprintf(fp, "%sid: %"DNS_TRACE_ID_PRI"\n", prefix, te->id); + fprintf(fp, "%sts: %s (%s)\n", prefix, time_s, elapsed_s); + fprintf(fp, "%sabi: 0x%x (0x%x)\n", prefix, te->abi, DNS_TRACE_ABI); + return 0; +} + +static dns_error_t dns_trace_dump_error(struct dns_trace *trace, const char *prefix, int error, FILE *fp) { + fprintf(fp, "%s%d (%s)\n", prefix, error, (error)? dns_strerror(error) : "none"); + return 0; +} + +dns_error_t dns_trace_dump(struct dns_trace *trace, FILE *fp) { + struct dns_trace_event *te = NULL; + struct { + dns_trace_id_t id; + dns_microseconds_t begin, elapsed; + } state = { 0 }; + int error; + + if (!trace || !trace->fp) + return EINVAL; + + if (0 != fseek(trace->fp, 0, SEEK_SET)) + goto syerr; + + while (dns_trace_fget(&te, trace->fp, &error)) { + size_t datasize = dns_te_datasize(te); + const unsigned char *data = (datasize)? te->data : NULL; + + if (state.id != te->id) { + state.id = te->id; + state.begin = dns_ts2us(&te->ts, 0); + } + dns_time_diff(&state.elapsed, dns_ts2us(&te->ts, 0), state.begin); + + switch(te->type) { + case DNS_TE_RES_SUBMIT: + fprintf(fp, "dns_res_submit:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + fprintf(fp, " qname: %s\n", te->res_submit.qname); + fprintf(fp, " qtype: %s\n", dns_strtype(te->res_submit.qtype)); + fprintf(fp, " qclass: %s\n", dns_strclass(te->res_submit.qclass)); + dns_trace_dump_error(trace, " error: ", te->res_submit.error, fp); + break; + case DNS_TE_RES_FETCH: + fprintf(fp, "dns_res_fetch:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + dns_trace_dump_error(trace, " error: ", te->res_fetch.error, fp); + + if (data) { + fprintf(fp, " packet: |\n"); + if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp))) + goto error; + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + case DNS_TE_SO_SUBMIT: + fprintf(fp, "dns_so_submit:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + fprintf(fp, " hname: %s\n", te->so_submit.hname); + dns_trace_dump_addr(trace, " haddr: ", &te->so_submit.haddr, fp); + dns_trace_dump_error(trace, " error: ", te->so_submit.error, fp); + + if (data) { + fprintf(fp, " packet: |\n"); + if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp))) + goto error; + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + case DNS_TE_SO_VERIFY: + fprintf(fp, "dns_so_verify:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + dns_trace_dump_error(trace, " error: ", te->so_verify.error, fp); + + if (data) { + fprintf(fp, " packet: |\n"); + if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp))) + goto error; + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + case DNS_TE_SO_FETCH: + fprintf(fp, "dns_so_fetch:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + dns_trace_dump_error(trace, " error: ", te->so_fetch.error, fp); + + if (data) { + fprintf(fp, " packet: |\n"); + if ((error = dns_trace_dump_packet(trace, " ", data, datasize, fp))) + goto error; + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + case DNS_TE_SYS_CONNECT: { + int socktype = te->sys_connect.socktype; + fprintf(fp, "dns_sys_connect:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + dns_trace_dump_addr(trace, " src: ", &te->sys_connect.src, fp); + dns_trace_dump_addr(trace, " dst: ", &te->sys_connect.dst, fp); + fprintf(fp, " socktype: %d (%s)\n", socktype, ((socktype == SOCK_STREAM)? "SOCK_STREAM" : (socktype == SOCK_DGRAM)? "SOCK_DGRAM" : "?")); + dns_trace_dump_error(trace, " error: ", te->sys_connect.error, fp); + + break; + } + case DNS_TE_SYS_SEND: { + int socktype = te->sys_send.socktype; + fprintf(fp, "dns_sys_send:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + dns_trace_dump_addr(trace, " src: ", &te->sys_send.src, fp); + dns_trace_dump_addr(trace, " dst: ", &te->sys_send.dst, fp); + fprintf(fp, " socktype: %d (%s)\n", socktype, ((socktype == SOCK_STREAM)? "SOCK_STREAM" : (socktype == SOCK_DGRAM)? "SOCK_DGRAM" : "?")); + dns_trace_dump_error(trace, " error: ", te->sys_send.error, fp); + + if (data) { + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + } + case DNS_TE_SYS_RECV: { + int socktype = te->sys_recv.socktype; + fprintf(fp, "dns_sys_recv:\n"); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + dns_trace_dump_addr(trace, " src: ", &te->sys_recv.src, fp); + dns_trace_dump_addr(trace, " dst: ", &te->sys_recv.dst, fp); + fprintf(fp, " socktype: %d (%s)\n", socktype, ((socktype == SOCK_STREAM)? "SOCK_STREAM" : (socktype == SOCK_DGRAM)? "SOCK_DGRAM" : "?")); + dns_trace_dump_error(trace, " error: ", te->sys_recv.error, fp); + + if (data) { + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + } + default: + fprintf(fp, "unknown(0x%.2x):\n", te->type); + dns_trace_dump_meta(trace, " ", te, state.elapsed, fp); + + if (data) { + fprintf(fp, " data: |\n"); + if ((error = dns_trace_dump_data(trace, " ", data, datasize, fp))) + goto error; + } + + break; + } + } + + goto epilog; +syerr: + error = errno; +error: + (void)0; +epilog: + free(te); + + return error; +} + +/* + * H O S T S R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hosts { + struct dns_hosts_entry { + char host[DNS_D_MAXNAME + 1]; + char arpa[73 + 1]; + + int af; + + union { + struct in_addr a4; + struct in6_addr a6; + } addr; + + _Bool alias; + + struct dns_hosts_entry *next; + } *head, **tail; + + dns_atomic_t refcount; +}; /* struct dns_hosts */ + + +struct dns_hosts *dns_hosts_open(int *error) { + static const struct dns_hosts hosts_initializer = { .refcount = 1 }; + struct dns_hosts *hosts; + + if (!(hosts = malloc(sizeof *hosts))) + goto syerr; + + *hosts = hosts_initializer; + + hosts->tail = &hosts->head; + + return hosts; +syerr: + *error = dns_syerr(); + + free(hosts); + + return 0; +} /* dns_hosts_open() */ + + +void dns_hosts_close(struct dns_hosts *hosts) { + struct dns_hosts_entry *ent, *xnt; + + if (!hosts || 1 != dns_hosts_release(hosts)) + return; + + for (ent = hosts->head; ent; ent = xnt) { + xnt = ent->next; + + free(ent); + } + + free(hosts); + + return; +} /* dns_hosts_close() */ + + +dns_refcount_t dns_hosts_acquire(struct dns_hosts *hosts) { + return dns_atomic_fetch_add(&hosts->refcount); +} /* dns_hosts_acquire() */ + + +dns_refcount_t dns_hosts_release(struct dns_hosts *hosts) { + return dns_atomic_fetch_sub(&hosts->refcount); +} /* dns_hosts_release() */ + + +struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) { + if (hosts) + dns_hosts_release(hosts); + + return hosts; +} /* dns_hosts_mortal() */ + + +struct dns_hosts *dns_hosts_local(int *error_) { + struct dns_hosts *hosts; + int error; + + if (!(hosts = dns_hosts_open(&error))) + goto error; + + if ((error = dns_hosts_loadpath(hosts, "/etc/hosts"))) + goto error; + + return hosts; +error: + *error_ = error; + + dns_hosts_close(hosts); + + return 0; +} /* dns_hosts_local() */ + + +#define dns_hosts_issep(ch) (dns_isspace(ch)) +#define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';') + +int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) { + struct dns_hosts_entry ent; + char word[DNS_PP_MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1]; + unsigned wp, wc, skip; + int ch, error; + + rewind(fp); + + do { + memset(&ent, '\0', sizeof ent); + wc = 0; + skip = 0; + + do { + memset(word, '\0', sizeof word); + wp = 0; + + while (EOF != (ch = fgetc(fp)) && ch != '\n') { + skip |= !!dns_hosts_iscom(ch); + + if (skip) + continue; + + if (dns_hosts_issep(ch)) + break; + + if (wp < sizeof word - 1) + word[wp] = ch; + wp++; + } + + if (!wp) + continue; + + wc++; + + switch (wc) { + case 0: + break; + case 1: + ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET; + skip = (1 != dns_inet_pton(ent.af, word, &ent.addr)); + + break; + default: + if (!wp) + break; + + dns_d_anchor(ent.host, sizeof ent.host, word, wp); + + if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2)))) + return error; + + break; + } /* switch() */ + } while (ch != EOF && ch != '\n'); + } while (ch != EOF); + + return 0; +} /* dns_hosts_loadfile() */ + + +int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) { + FILE *fp; + int error; + + if (!(fp = dns_fopen(path, "rt", &error))) + return error; + + error = dns_hosts_loadfile(hosts, fp); + + fclose(fp); + + return error; +} /* dns_hosts_loadpath() */ + + +int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) { + struct dns_hosts_entry *ent, *xnt; + char addr[INET6_ADDRSTRLEN + 1]; + unsigned i; + + for (ent = hosts->head; ent; ent = xnt) { + xnt = ent->next; + + dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr); + + fputs(addr, fp); + + for (i = strlen(addr); i < INET_ADDRSTRLEN; i++) + fputc(' ', fp); + + fputc(' ', fp); + + fputs(ent->host, fp); + fputc('\n', fp); + } + + return 0; +} /* dns_hosts_dump() */ + + +int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) { + struct dns_hosts_entry *ent; + int error; + + if (!(ent = malloc(sizeof *ent))) + goto syerr; + + dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host)); + + switch ((ent->af = af)) { + case AF_INET6: + memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6); + + dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr); + + break; + case AF_INET: + memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4); + + dns_a_arpa(ent->arpa, sizeof ent->arpa, addr); + + break; + default: + error = EINVAL; + + goto error; + } /* switch() */ + + ent->alias = alias; + + ent->next = 0; + *hosts->tail = ent; + hosts->tail = &ent->next; + + return 0; +syerr: + error = dns_syerr(); +error: + free(ent); + + return error; +} /* dns_hosts_insert() */ + + +struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { + struct dns_packet *P = dns_p_new(512); + struct dns_packet *A = 0; + struct dns_rr rr; + struct dns_hosts_entry *ent; + int error, af; + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + + if ((error = dns_rr_parse(&rr, 12, Q))) + goto error; + + if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error))) + goto error; + else if (qlen >= sizeof qname) + goto toolong; + + if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0))) + goto error; + + switch (rr.type) { + case DNS_T_PTR: + for (ent = hosts->head; ent; ent = ent->next) { + if (ent->alias || 0 != strcasecmp(qname, ent->arpa)) + continue; + + if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host))) + goto error; + } + + break; + case DNS_T_AAAA: + af = AF_INET6; + + goto loop; + case DNS_T_A: + af = AF_INET; + +loop: for (ent = hosts->head; ent; ent = ent->next) { + if (ent->af != af || 0 != strcasecmp(qname, ent->host)) + continue; + + if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr))) + goto error; + } + + break; + default: + break; + } /* switch() */ + + + if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) + goto error; + + return A; +toolong: + error = DNS_EILLEGAL; +error: + *error_ = error; + + dns_p_free(A); + + return 0; +} /* dns_hosts_query() */ + + +/* + * R E S O L V . C O N F R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_resolv_conf *dns_resconf_open(int *error) { + static const struct dns_resolv_conf resconf_initializer = { + .lookup = "bf", + .family = { AF_INET, AF_INET6 }, + .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, }, + .iface = { .ss_family = AF_INET }, + }; + struct dns_resolv_conf *resconf; + struct sockaddr_in *sin; + + if (!(resconf = malloc(sizeof *resconf))) + goto syerr; + + *resconf = resconf_initializer; + + sin = (struct sockaddr_in *)&resconf->nameserver[0]; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_port = htons(53); +#if defined(SA_LEN) + sin->sin_len = sizeof *sin; +#endif + + if (0 != gethostname(resconf->search[0], sizeof resconf->search[0])) + goto syerr; + + dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); + dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); + + /* + * XXX: If gethostname() returned a string without any label + * separator, then search[0][0] should be NUL. + */ + + dns_resconf_acquire(resconf); + + return resconf; +syerr: + *error = dns_syerr(); + + free(resconf); + + return 0; +} /* dns_resconf_open() */ + + +void dns_resconf_close(struct dns_resolv_conf *resconf) { + if (!resconf || 1 != dns_resconf_release(resconf)) + return /* void */; + + free(resconf); +} /* dns_resconf_close() */ + + +dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *resconf) { + return dns_atomic_fetch_add(&resconf->_.refcount); +} /* dns_resconf_acquire() */ + + +dns_refcount_t dns_resconf_release(struct dns_resolv_conf *resconf) { + return dns_atomic_fetch_sub(&resconf->_.refcount); +} /* dns_resconf_release() */ + + +struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) { + if (resconf) + dns_resconf_release(resconf); + + return resconf; +} /* dns_resconf_mortal() */ + + +struct dns_resolv_conf *dns_resconf_local(int *error_) { + struct dns_resolv_conf *resconf; + int error; + + if (!(resconf = dns_resconf_open(&error))) + goto error; + + if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf"))) { + /* + * NOTE: Both the glibc and BIND9 resolvers ignore a missing + * /etc/resolv.conf, defaulting to a nameserver of + * 127.0.0.1. See also dns_hints_insert_resconf, and the + * default initialization of nameserver[0] in + * dns_resconf_open. + */ + if (error != ENOENT) + goto error; + } + + if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) { + if (error != ENOENT) + goto error; + } + + return resconf; +error: + *error_ = error; + + dns_resconf_close(resconf); + + return 0; +} /* dns_resconf_local() */ + + +struct dns_resolv_conf *dns_resconf_root(int *error) { + struct dns_resolv_conf *resconf; + + if ((resconf = dns_resconf_local(error))) + resconf->options.recurse = 1; + + return resconf; +} /* dns_resconf_root() */ + + +static time_t dns_resconf_timeout(const struct dns_resolv_conf *resconf) { + return (time_t)DNS_PP_MIN(INT_MAX, resconf->options.timeout); +} /* dns_resconf_timeout() */ + + +enum dns_resconf_keyword { + DNS_RESCONF_NAMESERVER, + DNS_RESCONF_DOMAIN, + DNS_RESCONF_SEARCH, + DNS_RESCONF_LOOKUP, + DNS_RESCONF_FILE, + DNS_RESCONF_BIND, + DNS_RESCONF_CACHE, + DNS_RESCONF_FAMILY, + DNS_RESCONF_INET4, + DNS_RESCONF_INET6, + DNS_RESCONF_OPTIONS, + DNS_RESCONF_EDNS0, + DNS_RESCONF_NDOTS, + DNS_RESCONF_TIMEOUT, + DNS_RESCONF_ATTEMPTS, + DNS_RESCONF_ROTATE, + DNS_RESCONF_RECURSE, + DNS_RESCONF_SMART, + DNS_RESCONF_TCP, + DNS_RESCONF_TCPx, + DNS_RESCONF_INTERFACE, + DNS_RESCONF_ZERO, + DNS_RESCONF_ONE, + DNS_RESCONF_ENABLE, + DNS_RESCONF_ONLY, + DNS_RESCONF_DISABLE, +}; /* enum dns_resconf_keyword */ + +static enum dns_resconf_keyword dns_resconf_keyword(const char *word) { + static const char *words[] = { + [DNS_RESCONF_NAMESERVER] = "nameserver", + [DNS_RESCONF_DOMAIN] = "domain", + [DNS_RESCONF_SEARCH] = "search", + [DNS_RESCONF_LOOKUP] = "lookup", + [DNS_RESCONF_FILE] = "file", + [DNS_RESCONF_BIND] = "bind", + [DNS_RESCONF_CACHE] = "cache", + [DNS_RESCONF_FAMILY] = "family", + [DNS_RESCONF_INET4] = "inet4", + [DNS_RESCONF_INET6] = "inet6", + [DNS_RESCONF_OPTIONS] = "options", + [DNS_RESCONF_EDNS0] = "edns0", + [DNS_RESCONF_ROTATE] = "rotate", + [DNS_RESCONF_RECURSE] = "recurse", + [DNS_RESCONF_SMART] = "smart", + [DNS_RESCONF_TCP] = "tcp", + [DNS_RESCONF_INTERFACE] = "interface", + [DNS_RESCONF_ZERO] = "0", + [DNS_RESCONF_ONE] = "1", + [DNS_RESCONF_ENABLE] = "enable", + [DNS_RESCONF_ONLY] = "only", + [DNS_RESCONF_DISABLE] = "disable", + }; + unsigned i; + + for (i = 0; i < lengthof(words); i++) { + if (words[i] && 0 == strcasecmp(words[i], word)) + return i; + } + + if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1)) + return DNS_RESCONF_NDOTS; + + if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1)) + return DNS_RESCONF_TIMEOUT; + + if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1)) + return DNS_RESCONF_ATTEMPTS; + + if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1)) + return DNS_RESCONF_TCPx; + + return -1; +} /* dns_resconf_keyword() */ + + +/** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */ +int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) { + struct { char buf[128], *p; } addr = { "", addr.buf }; + unsigned short port = 0; + int ch, af = AF_INET, error; + + while ((ch = *src++)) { + switch (ch) { + case ' ': + /* FALL THROUGH */ + case '\t': + break; + case '[': + break; + case ']': + while ((ch = *src++)) { + if (dns_isdigit(ch)) { + port *= 10; + port += ch - '0'; + } + } + + goto inet; + case ':': + af = AF_INET6; + + /* FALL THROUGH */ + default: + if (addr.p < endof(addr.buf) - 1) + *addr.p++ = ch; + + break; + } /* switch() */ + } /* while() */ +inet: + + if ((error = dns_pton(af, addr.buf, dns_sa_addr(af, ss, NULL)))) + return error; + + port = (!port)? 53 : port; + *dns_sa_port(af, ss) = htons(port); + dns_sa_family(ss) = af; + + return 0; +} /* dns_resconf_pton() */ + +#define dns_resconf_issep(ch) (dns_isspace(ch) || (ch) == ',') +#define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';') + +int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { + unsigned sa_count = 0; + char words[6][DNS_D_MAXNAME + 1]; + unsigned wp, wc, i, j, n; + int ch, error; + + rewind(fp); + + do { + memset(words, '\0', sizeof words); + wp = 0; + wc = 0; + + while (EOF != (ch = getc(fp)) && ch != '\n') { + if (dns_resconf_issep(ch)) { + if (wp > 0) { + wp = 0; + + if (++wc >= lengthof(words)) + goto skip; + } + } else if (dns_resconf_iscom(ch)) { +skip: + do { + ch = getc(fp); + } while (ch != EOF && ch != '\n'); + + break; + } else if (wp < sizeof words[wc] - 1) { + words[wc][wp++] = ch; + } else { + wp = 0; /* drop word */ + goto skip; + } + } + + if (wp > 0) + wc++; + + if (wc < 2) + continue; + + switch (dns_resconf_keyword(words[0])) { + case DNS_RESCONF_NAMESERVER: + if (sa_count >= lengthof(resconf->nameserver)) + continue; + + if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1]))) + continue; + + sa_count++; + + break; + case DNS_RESCONF_DOMAIN: + case DNS_RESCONF_SEARCH: + memset(resconf->search, '\0', sizeof resconf->search); + + for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++) + dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i])); + + break; + case DNS_RESCONF_LOOKUP: + for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) { + switch (dns_resconf_keyword(words[i])) { + case DNS_RESCONF_FILE: + resconf->lookup[j++] = 'f'; + + break; + case DNS_RESCONF_BIND: + resconf->lookup[j++] = 'b'; + + break; + case DNS_RESCONF_CACHE: + resconf->lookup[j++] = 'c'; + + break; + default: + break; + } /* switch() */ + } /* for() */ + + break; + case DNS_RESCONF_FAMILY: + for (i = 1, j = 0; i < wc && j < lengthof(resconf->family); i++) { + switch (dns_resconf_keyword(words[i])) { + case DNS_RESCONF_INET4: + resconf->family[j++] = AF_INET; + + break; + case DNS_RESCONF_INET6: + resconf->family[j++] = AF_INET6; + + break; + default: + break; + } + } + + break; + case DNS_RESCONF_OPTIONS: + for (i = 1; i < wc; i++) { + switch (dns_resconf_keyword(words[i])) { + case DNS_RESCONF_EDNS0: + resconf->options.edns0 = 1; + + break; + case DNS_RESCONF_NDOTS: + for (j = sizeof "ndots:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { + n *= 10; + n += words[i][j] - '0'; + } /* for() */ + + resconf->options.ndots = n; + + break; + case DNS_RESCONF_TIMEOUT: + for (j = sizeof "timeout:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { + n *= 10; + n += words[i][j] - '0'; + } /* for() */ + + resconf->options.timeout = n; + + break; + case DNS_RESCONF_ATTEMPTS: + for (j = sizeof "attempts:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { + n *= 10; + n += words[i][j] - '0'; + } /* for() */ + + resconf->options.attempts = n; + + break; + case DNS_RESCONF_ROTATE: + resconf->options.rotate = 1; + + break; + case DNS_RESCONF_RECURSE: + resconf->options.recurse = 1; + + break; + case DNS_RESCONF_SMART: + resconf->options.smart = 1; + + break; + case DNS_RESCONF_TCP: + resconf->options.tcp = DNS_RESCONF_TCP_ONLY; + + break; + case DNS_RESCONF_TCPx: + switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) { + case DNS_RESCONF_ENABLE: + resconf->options.tcp = DNS_RESCONF_TCP_ENABLE; + + break; + case DNS_RESCONF_ONE: + case DNS_RESCONF_ONLY: + resconf->options.tcp = DNS_RESCONF_TCP_ONLY; + + break; + case DNS_RESCONF_ZERO: + case DNS_RESCONF_DISABLE: + resconf->options.tcp = DNS_RESCONF_TCP_DISABLE; + + break; + default: + break; + } /* switch() */ + + break; + default: + break; + } /* switch() */ + } /* for() */ + + break; + case DNS_RESCONF_INTERFACE: + for (i = 0, n = 0; dns_isdigit(words[2][i]); i++) { + n *= 10; + n += words[2][i] - '0'; + } + + dns_resconf_setiface(resconf, words[1], n); + + break; + default: + break; + } /* switch() */ + } while (ch != EOF); + + return 0; +} /* dns_resconf_loadfile() */ + + +int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { + FILE *fp; + int error; + + if (!(fp = dns_fopen(path, "rt", &error))) + return error; + + error = dns_resconf_loadfile(resconf, fp); + + fclose(fp); + + return error; +} /* dns_resconf_loadpath() */ + + +struct dns_anyconf { + char *token[16]; + unsigned count; + char buffer[1024], *tp, *cp; +}; /* struct dns_anyconf */ + + +static void dns_anyconf_reset(struct dns_anyconf *cf) { + cf->count = 0; + cf->tp = cf->cp = cf->buffer; +} /* dns_anyconf_reset() */ + + +static int dns_anyconf_push(struct dns_anyconf *cf) { + if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token))) + return ENOMEM; + + *cf->cp++ = '\0'; + cf->token[cf->count++] = cf->tp; + cf->tp = cf->cp; + + return 0; +} /* dns_anyconf_push() */ + + +static void dns_anyconf_pop(struct dns_anyconf *cf) { + if (cf->count > 0) { + --cf->count; + cf->tp = cf->cp = cf->token[cf->count]; + cf->token[cf->count] = 0; + } +} /* dns_anyconf_pop() */ + + +static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) { + if (!(cf->cp < endof(cf->buffer))) + return ENOMEM; + + *cf->cp++ = ch; + + return 0; +} /* dns_anyconf_addc() */ + + +static _Bool dns_anyconf_match(const char *pat, int mc) { + _Bool match; + int pc; + + if (*pat == '^') { + match = 0; + ++pat; + } else { + match = 1; + } + + while ((pc = *(const unsigned char *)pat++)) { + switch (pc) { + case '%': + if (!(pc = *(const unsigned char *)pat++)) + return !match; + + switch (pc) { + case 'a': + if (dns_isalpha(mc)) + return match; + break; + case 'd': + if (dns_isdigit(mc)) + return match; + break; + case 'w': + if (dns_isalnum(mc)) + return match; + break; + case 's': + if (dns_isspace(mc)) + return match; + break; + default: + if (mc == pc) + return match; + break; + } /* switch() */ + + break; + default: + if (mc == pc) + return match; + break; + } /* switch() */ + } /* while() */ + + return !match; +} /* dns_anyconf_match() */ + + +static int dns_anyconf_peek(FILE *fp) { + int ch; + ch = getc(fp); + ungetc(ch, fp); + return ch; +} /* dns_anyconf_peek() */ + + +static size_t dns_anyconf_skip(const char *pat, FILE *fp) { + size_t count = 0; + int ch; + + while (EOF != (ch = getc(fp))) { + if (dns_anyconf_match(pat, ch)) { + count++; + continue; + } + + ungetc(ch, fp); + + break; + } + + return count; +} /* dns_anyconf_skip() */ + + +static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) { + size_t len; + int ch; + + while (EOF != (ch = getc(fp))) { + if (dns_anyconf_match(pat, ch)) { + if ((*error = dns_anyconf_addc(cf, ch))) + return 0; + + continue; + } else { + ungetc(ch, fp); + + break; + } + } + + if ((len = cf->cp - cf->tp)) { + if ((*error = dns_anyconf_push(cf))) + return 0; + + return len; + } else { + *error = 0; + + return 0; + } +} /* dns_anyconf_scan() */ + + +DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) { + unsigned i; + + fprintf(fp, "tokens:"); + + for (i = 0; i < cf->count; i++) { + fprintf(fp, " %s", cf->token[i]); + } + + fputc('\n', fp); +} /* dns_anyconf_dump() */ + + +enum dns_nssconf_keyword { + DNS_NSSCONF_INVALID = 0, + DNS_NSSCONF_HOSTS = 1, + DNS_NSSCONF_SUCCESS, + DNS_NSSCONF_NOTFOUND, + DNS_NSSCONF_UNAVAIL, + DNS_NSSCONF_TRYAGAIN, + DNS_NSSCONF_CONTINUE, + DNS_NSSCONF_RETURN, + DNS_NSSCONF_FILES, + DNS_NSSCONF_DNS, + DNS_NSSCONF_MDNS, + + DNS_NSSCONF_LAST, +}; /* enum dns_nssconf_keyword */ + +static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) { + static const char *list[] = { + [DNS_NSSCONF_HOSTS] = "hosts", + [DNS_NSSCONF_SUCCESS] = "success", + [DNS_NSSCONF_NOTFOUND] = "notfound", + [DNS_NSSCONF_UNAVAIL] = "unavail", + [DNS_NSSCONF_TRYAGAIN] = "tryagain", + [DNS_NSSCONF_CONTINUE] = "continue", + [DNS_NSSCONF_RETURN] = "return", + [DNS_NSSCONF_FILES] = "files", + [DNS_NSSCONF_DNS] = "dns", + [DNS_NSSCONF_MDNS] = "mdns", + }; + unsigned i; + + for (i = 1; i < lengthof(list); i++) { + if (list[i] && 0 == strcasecmp(list[i], word)) + return i; + } + + return DNS_NSSCONF_INVALID; +} /* dns_nssconf_keyword() */ + + +static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) { + static const char map[] = { + ['S'] = DNS_NSSCONF_SUCCESS, + ['N'] = DNS_NSSCONF_NOTFOUND, + ['U'] = DNS_NSSCONF_UNAVAIL, + ['T'] = DNS_NSSCONF_TRYAGAIN, + ['C'] = DNS_NSSCONF_CONTINUE, + ['R'] = DNS_NSSCONF_RETURN, + ['f'] = DNS_NSSCONF_FILES, + ['F'] = DNS_NSSCONF_FILES, + ['d'] = DNS_NSSCONF_DNS, + ['D'] = DNS_NSSCONF_DNS, + ['b'] = DNS_NSSCONF_DNS, + ['B'] = DNS_NSSCONF_DNS, + ['m'] = DNS_NSSCONF_MDNS, + ['M'] = DNS_NSSCONF_MDNS, + }; + + return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID; +} /* dns_nssconf_c2k() */ + + +DNS_PRAGMA_PUSH +DNS_PRAGMA_QUIET + +static int dns_nssconf_k2c(int k) { + static const char map[DNS_NSSCONF_LAST] = { + [DNS_NSSCONF_SUCCESS] = 'S', + [DNS_NSSCONF_NOTFOUND] = 'N', + [DNS_NSSCONF_UNAVAIL] = 'U', + [DNS_NSSCONF_TRYAGAIN] = 'T', + [DNS_NSSCONF_CONTINUE] = 'C', + [DNS_NSSCONF_RETURN] = 'R', + [DNS_NSSCONF_FILES] = 'f', + [DNS_NSSCONF_DNS] = 'b', + [DNS_NSSCONF_MDNS] = 'm', + }; + + return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?'; +} /* dns_nssconf_k2c() */ + +static const char *dns_nssconf_k2s(int k) { + static const char *const map[DNS_NSSCONF_LAST] = { + [DNS_NSSCONF_SUCCESS] = "SUCCESS", + [DNS_NSSCONF_NOTFOUND] = "NOTFOUND", + [DNS_NSSCONF_UNAVAIL] = "UNAVAIL", + [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN", + [DNS_NSSCONF_CONTINUE] = "continue", + [DNS_NSSCONF_RETURN] = "return", + [DNS_NSSCONF_FILES] = "files", + [DNS_NSSCONF_DNS] = "dns", + [DNS_NSSCONF_MDNS] = "mdns", + }; + + return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : ""; +} /* dns_nssconf_k2s() */ + +DNS_PRAGMA_POP + + +int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { + enum dns_nssconf_keyword source, status, action; + char lookup[sizeof resconf->lookup] = "", *lp; + struct dns_anyconf cf; + size_t i; + int error; + + while (!feof(fp) && !ferror(fp)) { + dns_anyconf_reset(&cf); + + dns_anyconf_skip("%s", fp); + + if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) + goto nextent; + + if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0])) + goto nextent; + + dns_anyconf_pop(&cf); + + if (!dns_anyconf_skip(": \t", fp)) + goto nextent; + + *(lp = lookup) = '\0'; + + while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { + dns_anyconf_skip(" \t", fp); + + if ('[' == dns_anyconf_peek(fp)) { + dns_anyconf_skip("[ \t", fp); + + while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { + dns_anyconf_skip("= \t", fp); + if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) { + dns_anyconf_pop(&cf); /* discard status */ + dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */ + break; + } + dns_anyconf_skip(" \t", fp); + } + + dns_anyconf_skip("] \t", fp); + } + + if ((size_t)(endof(lookup) - lp) < cf.count + 1) /* +1 for '\0' */ + goto nextsrc; + + source = dns_nssconf_keyword(cf.token[0]); + + switch (source) { + case DNS_NSSCONF_DNS: + case DNS_NSSCONF_MDNS: + case DNS_NSSCONF_FILES: + *lp++ = dns_nssconf_k2c(source); + break; + default: + goto nextsrc; + } + + for (i = 1; i + 1 < cf.count; i += 2) { + status = dns_nssconf_keyword(cf.token[i]); + action = dns_nssconf_keyword(cf.token[i + 1]); + + switch (status) { + case DNS_NSSCONF_SUCCESS: + case DNS_NSSCONF_NOTFOUND: + case DNS_NSSCONF_UNAVAIL: + case DNS_NSSCONF_TRYAGAIN: + *lp++ = dns_nssconf_k2c(status); + break; + default: + continue; + } + + switch (action) { + case DNS_NSSCONF_CONTINUE: + case DNS_NSSCONF_RETURN: + break; + default: + action = (status == DNS_NSSCONF_SUCCESS) + ? DNS_NSSCONF_RETURN + : DNS_NSSCONF_CONTINUE; + break; + } + + *lp++ = dns_nssconf_k2c(action); + } +nextsrc: + *lp = '\0'; + dns_anyconf_reset(&cf); + } +nextent: + dns_anyconf_skip("^\n", fp); + } + + if (*lookup) + strncpy(resconf->lookup, lookup, sizeof resconf->lookup); + + return 0; +} /* dns_nssconf_loadfile() */ + + +int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { + FILE *fp; + int error; + + if (!(fp = dns_fopen(path, "rt", &error))) + return error; + + error = dns_nssconf_loadfile(resconf, fp); + + fclose(fp); + + return error; +} /* dns_nssconf_loadpath() */ + + +struct dns_nssconf_source { + enum dns_nssconf_keyword source, success, notfound, unavail, tryagain; +}; /* struct dns_nssconf_source */ + +typedef unsigned dns_nssconf_i; + +static inline int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) { + return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0; +} /* dns_nssconf_peek() */ + +static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) { + int source, status, action; + + src->source = DNS_NSSCONF_INVALID; + src->success = DNS_NSSCONF_RETURN; + src->notfound = DNS_NSSCONF_CONTINUE; + src->unavail = DNS_NSSCONF_CONTINUE; + src->tryagain = DNS_NSSCONF_CONTINUE; + + while ((source = dns_nssconf_peek(resconf, *state))) { + source = dns_nssconf_c2k(source); + ++*state; + + switch (source) { + case DNS_NSSCONF_FILES: + case DNS_NSSCONF_DNS: + case DNS_NSSCONF_MDNS: + src->source = source; + break; + default: + continue; + } + + while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) { + status = dns_nssconf_c2k(status); + action = dns_nssconf_c2k(action); + + switch (action) { + case DNS_NSSCONF_RETURN: + case DNS_NSSCONF_CONTINUE: + break; + default: + goto done; + } + + switch (status) { + case DNS_NSSCONF_SUCCESS: + src->success = action; + break; + case DNS_NSSCONF_NOTFOUND: + src->notfound = action; + break; + case DNS_NSSCONF_UNAVAIL: + src->unavail = action; + break; + case DNS_NSSCONF_TRYAGAIN: + src->tryagain = action; + break; + default: + goto done; + } + + *state += 2; + } + + break; + } +done: + return src->source != DNS_NSSCONF_INVALID; +} /* dns_nssconf_next() */ + + +static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) { + switch (status) { + case DNS_NSSCONF_SUCCESS: + if (action == DNS_NSSCONF_RETURN) + return 0; + break; + default: + if (action == DNS_NSSCONF_CONTINUE) + return 0; + break; + } + + fputc(' ', fp); + + if (!*count) + fputc('[', fp); + + fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action)); + + ++*count; + + return 0; +} /* dns_nssconf_dump_status() */ + + +int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { + struct dns_nssconf_source src; + dns_nssconf_i i = 0; + + fputs("hosts:", fp); + + while (dns_nssconf_next(&src, resconf, &i)) { + unsigned n = 0; + + fprintf(fp, " %s", dns_nssconf_k2s(src.source)); + + dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp); + dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp); + dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp); + dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp); + + if (n) + fputc(']', fp); + } + + fputc('\n', fp); + + return 0; +} /* dns_nssconf_dump() */ + + +int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) { + int af = (strchr(addr, ':'))? AF_INET6 : AF_INET; + int error; + + if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL)))) + return error; + + *dns_sa_port(af, &resconf->iface) = htons(port); + resconf->iface.ss_family = af; + + return 0; +} /* dns_resconf_setiface() */ + + +#define DNS_SM_RESTORE \ + do { \ + pc = 0xff & (*state >> 0); \ + srchi = 0xff & (*state >> 8); \ + ndots = 0xff & (*state >> 16); \ + } while (0) + +#define DNS_SM_SAVE \ + do { \ + *state = ((0xff & pc) << 0) \ + | ((0xff & srchi) << 8) \ + | ((0xff & ndots) << 16); \ + } while (0) + +size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) { + unsigned pc, srchi, ndots, len; + + DNS_SM_ENTER; + + /* if FQDN then return as-is and finish */ + if (dns_d_isanchored(qname, qlen)) { + len = dns_d_anchor(dst, lim, qname, qlen); + DNS_SM_YIELD(len); + DNS_SM_EXIT; + } + + ndots = dns_d_ndots(qname, qlen); + + if (ndots >= resconf->options.ndots) { + len = dns_d_anchor(dst, lim, qname, qlen); + DNS_SM_YIELD(len); + } + + while (srchi < lengthof(resconf->search) && resconf->search[srchi][0]) { + struct dns_buf buf = DNS_B_INTO(dst, lim); + const char *dn = resconf->search[srchi++]; + + dns_b_put(&buf, qname, qlen); + dns_b_putc(&buf, '.'); + dns_b_puts(&buf, dn); + if (!dns_d_isanchored(dn, strlen(dn))) + dns_b_putc(&buf, '.'); + len = dns_b_strllen(&buf); + DNS_SM_YIELD(len); + } + + if (ndots < resconf->options.ndots) { + len = dns_d_anchor(dst, lim, qname, qlen); + DNS_SM_YIELD(len); + } + + DNS_SM_LEAVE; + + return dns_strlcpy(dst, "", lim); +} /* dns_resconf_search() */ + +#undef DNS_SM_SAVE +#undef DNS_SM_RESTORE + + +int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { + unsigned i; + int af; + + for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) { + char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; + unsigned short port; + + dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i], NULL), addr, sizeof addr); + port = ntohs(*dns_sa_port(af, &resconf->nameserver[i])); + + if (port == 53) + fprintf(fp, "nameserver %s\n", addr); + else + fprintf(fp, "nameserver [%s]:%hu\n", addr, port); + } + + + fprintf(fp, "search"); + + for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++) + fprintf(fp, " %s", resconf->search[i]); + + fputc('\n', fp); + + + fputs("; ", fp); + dns_nssconf_dump(resconf, fp); + + fprintf(fp, "lookup"); + + for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) { + switch (resconf->lookup[i]) { + case 'b': + fprintf(fp, " bind"); break; + case 'f': + fprintf(fp, " file"); break; + case 'c': + fprintf(fp, " cache"); break; + } + } + + fputc('\n', fp); + + + fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts); + + if (resconf->options.edns0) + fprintf(fp, " edns0"); + if (resconf->options.rotate) + fprintf(fp, " rotate"); + if (resconf->options.recurse) + fprintf(fp, " recurse"); + if (resconf->options.smart) + fprintf(fp, " smart"); + + switch (resconf->options.tcp) { + case DNS_RESCONF_TCP_ENABLE: + break; + case DNS_RESCONF_TCP_ONLY: + fprintf(fp, " tcp"); + break; + case DNS_RESCONF_TCP_SOCKS: + fprintf(fp, " tcp:socks"); + break; + case DNS_RESCONF_TCP_DISABLE: + fprintf(fp, " tcp:disable"); + break; + } + + fputc('\n', fp); + + + if ((af = resconf->iface.ss_family) != AF_UNSPEC) { + char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; + + dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface, NULL), addr, sizeof addr); + + fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface))); + } + + return 0; +} /* dns_resconf_dump() */ + + +/* + * H I N T S E R V E R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hints_soa { + unsigned char zone[DNS_D_MAXNAME + 1]; + + struct { + struct sockaddr_storage ss; + unsigned priority; + } addrs[16]; + + unsigned count; + + struct dns_hints_soa *next; +}; /* struct dns_hints_soa */ + + +struct dns_hints { + dns_atomic_t refcount; + + struct dns_hints_soa *head; +}; /* struct dns_hints */ + + +struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) { + static const struct dns_hints H_initializer; + struct dns_hints *H; + + (void)resconf; + + if (!(H = malloc(sizeof *H))) + goto syerr; + + *H = H_initializer; + + dns_hints_acquire(H); + + return H; +syerr: + *error = dns_syerr(); + + free(H); + + return 0; +} /* dns_hints_open() */ + + +void dns_hints_close(struct dns_hints *H) { + struct dns_hints_soa *soa, *nxt; + + if (!H || 1 != dns_hints_release(H)) + return /* void */; + + for (soa = H->head; soa; soa = nxt) { + nxt = soa->next; + + free(soa); + } + + free(H); + + return /* void */; +} /* dns_hints_close() */ + + +dns_refcount_t dns_hints_acquire(struct dns_hints *H) { + return dns_atomic_fetch_add(&H->refcount); +} /* dns_hints_acquire() */ + + +dns_refcount_t dns_hints_release(struct dns_hints *H) { + return dns_atomic_fetch_sub(&H->refcount); +} /* dns_hints_release() */ + + +struct dns_hints *dns_hints_mortal(struct dns_hints *hints) { + if (hints) + dns_hints_release(hints); + + return hints; +} /* dns_hints_mortal() */ + + +struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) { + struct dns_hints *hints = 0; + int error; + + if (resconf) + dns_resconf_acquire(resconf); + else if (!(resconf = dns_resconf_local(&error))) + goto error; + + if (!(hints = dns_hints_open(resconf, &error))) + goto error; + + error = 0; + + if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error) + goto error; + + dns_resconf_close(resconf); + + return hints; +error: + *error_ = error; + + dns_resconf_close(resconf); + dns_hints_close(hints); + + return 0; +} /* dns_hints_local() */ + + +struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) { + static const struct { + int af; + char addr[INET6_ADDRSTRLEN]; + } root_hints[] = { + { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */ + { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:84::b" }, /* B.ROOT-SERVERS.NET. */ + { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:2::c" }, /* C.ROOT-SERVERS.NET. */ + { AF_INET, "199.7.91.13" }, /* D.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:2d::d" }, /* D.ROOT-SERVERS.NET. */ + { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */ + { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */ + { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */ + { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */ + { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:7FE::53" }, /* I.ROOT-SERVERS.NET. */ + { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */ + { AF_INET, "193.0.14.129" }, /* K.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:7FD::1" }, /* K.ROOT-SERVERS.NET. */ + { AF_INET, "199.7.83.42" }, /* L.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:500:3::42" }, /* L.ROOT-SERVERS.NET. */ + { AF_INET, "202.12.27.33" }, /* M.ROOT-SERVERS.NET. */ + { AF_INET6, "2001:DC3::35" }, /* M.ROOT-SERVERS.NET. */ + }; + struct dns_hints *hints = 0; + struct sockaddr_storage ss; + unsigned i; + int error, af; + + if (!(hints = dns_hints_open(resconf, &error))) + goto error; + + for (i = 0; i < lengthof(root_hints); i++) { + af = root_hints[i].af; + + if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL)))) + goto error; + + *dns_sa_port(af, &ss) = htons(53); + ss.ss_family = af; + + if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1))) + goto error; + } + + return hints; +error: + *error_ = error; + + dns_hints_close(hints); + + return 0; +} /* dns_hints_root() */ + + +static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) { + struct dns_hints_soa *soa; + + for (soa = H->head; soa; soa = soa->next) { + if (0 == strcasecmp(zone, (char *)soa->zone)) + return soa; + } + + return 0; +} /* dns_hints_fetch() */ + + +int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) { + static const struct dns_hints_soa soa_initializer; + struct dns_hints_soa *soa; + unsigned i; + + if (!(soa = dns_hints_fetch(H, zone))) { + if (!(soa = malloc(sizeof *soa))) + return dns_syerr(); + *soa = soa_initializer; + dns_strlcpy((char *)soa->zone, zone, sizeof soa->zone); + + soa->next = H->head; + H->head = soa; + } + + i = soa->count % lengthof(soa->addrs); + + memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa)); + + soa->addrs[i].priority = DNS_PP_MAX(1, priority); + + if (soa->count < lengthof(soa->addrs)) + soa->count++; + + return 0; +} /* dns_hints_insert() */ + + +static _Bool dns_hints_isinaddr_any(const void *sa) { + struct in_addr *addr; + + if (dns_sa_family(sa) != AF_INET) + return 0; + + addr = dns_sa_addr(AF_INET, sa, NULL); + return addr->s_addr == htonl(INADDR_ANY); +} + +unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, const struct dns_resolv_conf *resconf, int *error_) { + unsigned i, n, p; + int error; + + for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) { + union { struct sockaddr_in sin; } tmp; + struct sockaddr *ns; + + /* + * dns_resconf_open initializes nameserver[0] to INADDR_ANY. + * + * Traditionally the semantics of 0.0.0.0 meant the default + * interface, which evolved to mean the loopback interface. + * See comment block preceding resolv/res_init.c:res_init in + * glibc 2.23. As of 2.23, glibc no longer translates + * 0.0.0.0 despite the code comment, but it does default to + * 127.0.0.1 when no nameservers are present. + * + * BIND9 as of 9.10.3 still translates 0.0.0.0 to 127.0.0.1. + * See lib/lwres/lwconfig.c:lwres_create_addr and the + * convert_zero flag. 127.0.0.1 is also the default when no + * nameservers are present. + */ + if (dns_hints_isinaddr_any(&resconf->nameserver[i])) { + memcpy(&tmp.sin, &resconf->nameserver[i], sizeof tmp.sin); + tmp.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ns = (struct sockaddr *)&tmp.sin; + } else { + ns = (struct sockaddr *)&resconf->nameserver[i]; + } + + if ((error = dns_hints_insert(H, zone, ns, p))) + goto error; + + p += !resconf->options.rotate; + } + + return n; +error: + *error_ = error; + + return n; +} /* dns_hints_insert_resconf() */ + + +static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) { + int cmp; + + if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority)) + return cmp; + + return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed); +} /* dns_hints_i_cmp() */ + + +static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) { + unsigned p0, p; + + p0 = 0; + + for (p = 1; p < soa->count; p++) { + if (dns_hints_i_cmp(p, p0, i, soa) < 0) + p0 = p; + } + + return p0; +} /* dns_hints_i_start() */ + + +static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) { + unsigned pZ, p; + + for (pZ = 0; pZ < soa->count; pZ++) { + if (dns_hints_i_cmp(pZ, p0, i, soa) > 0) + goto cont; + } + + return soa->count; +cont: + for (p = pZ + 1; p < soa->count; p++) { + if (dns_hints_i_cmp(p, p0, i, soa) <= 0) + continue; + + if (dns_hints_i_cmp(p, pZ, i, soa) >= 0) + continue; + + pZ = p; + } + + + return pZ; +} /* dns_hints_i_skip() */ + + +static struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) { + static const struct dns_hints_i i_initializer; + struct dns_hints_soa *soa; + + i->state = i_initializer.state; + + do { + i->state.seed = dns_random(); + } while (0 == i->state.seed); + + if ((soa = dns_hints_fetch(hints, i->zone))) { + i->state.next = dns_hints_i_start(i, soa); + } + + return i; +} /* dns_hints_i_init() */ + + +unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) { + struct dns_hints_soa *soa; + unsigned n; + + if (!(soa = dns_hints_fetch(H, i->zone))) + return 0; + + n = 0; + + while (i->state.next < soa->count && n < lim) { + *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss; + *sa_len = dns_sa_len(*sa); + + sa++; + sa_len++; + n++; + + i->state.next = dns_hints_i_skip(i->state.next, i, soa); + } + + return n; +} /* dns_hints_grep() */ + + +struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { + struct dns_packet *A, *P; + struct dns_rr rr; + char zone[DNS_D_MAXNAME + 1]; + size_t zlen; + struct dns_hints_i i; + struct sockaddr *sa; + socklen_t slen; + int error; + + if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error)) + goto error; + + if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) + goto error; + else if (zlen >= sizeof zone) + goto toolong; + + P = dns_p_new(512); + dns_header(P)->qr = 1; + + if ((error = dns_rr_copy(P, &rr, Q))) + goto error; + + if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local."))) + goto error; + + do { + i.zone = zone; + + dns_hints_i_init(&i, hints); + + while (dns_hints_grep(&sa, &slen, 1, &i, hints)) { + int af = sa->sa_family; + int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A; + + if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa, NULL)))) + goto error; + } + } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen))); + + if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) + goto error; + + return A; +toolong: + error = DNS_EILLEGAL; +error: + *error_ = error; + + return 0; +} /* dns_hints_query() */ + + +/** ugly hack to support specifying ports other than 53 in resolv.conf. */ +static unsigned short dns_hints_port(struct dns_hints *hints, int af, void *addr) { + struct dns_hints_soa *soa; + void *addrsoa; + socklen_t addrlen; + unsigned short port; + unsigned i; + + for (soa = hints->head; soa; soa = soa->next) { + for (i = 0; i < soa->count; i++) { + if (af != soa->addrs[i].ss.ss_family) + continue; + + if (!(addrsoa = dns_sa_addr(af, &soa->addrs[i].ss, &addrlen))) + continue; + + if (memcmp(addr, addrsoa, addrlen)) + continue; + + port = *dns_sa_port(af, &soa->addrs[i].ss); + + return (port)? port : htons(53); + } + } + + return htons(53); +} /* dns_hints_port() */ + + +int dns_hints_dump(struct dns_hints *hints, FILE *fp) { + struct dns_hints_soa *soa; + char addr[INET6_ADDRSTRLEN]; + unsigned i; + int af, error; + + for (soa = hints->head; soa; soa = soa->next) { + fprintf(fp, "ZONE \"%s\"\n", soa->zone); + + for (i = 0; i < soa->count; i++) { + af = soa->addrs[i].ss.ss_family; + + if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss, NULL), addr, sizeof addr))) + return error; + + fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss))); + } + } + + return 0; +} /* dns_hints_dump() */ + + +/* + * C A C H E R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static dns_refcount_t dns_cache_acquire(struct dns_cache *cache) { + return dns_atomic_fetch_add(&cache->_.refcount); +} /* dns_cache_acquire() */ + + +static dns_refcount_t dns_cache_release(struct dns_cache *cache) { + return dns_atomic_fetch_sub(&cache->_.refcount); +} /* dns_cache_release() */ + + +static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) { + (void)query; + (void)cache; + (void)error; + + return NULL; +} /* dns_cache_query() */ + + +static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) { + (void)query; + (void)cache; + + return 0; +} /* dns_cache_submit() */ + + +static int dns_cache_check(struct dns_cache *cache) { + (void)cache; + + return 0; +} /* dns_cache_check() */ + + +static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) { + (void)cache; + (void)error; + + return NULL; +} /* dns_cache_fetch() */ + + +static int dns_cache_pollfd(struct dns_cache *cache) { + (void)cache; + + return -1; +} /* dns_cache_pollfd() */ + + +static short dns_cache_events(struct dns_cache *cache) { + (void)cache; + + return 0; +} /* dns_cache_events() */ + + +static void dns_cache_clear(struct dns_cache *cache) { + (void)cache; + + return; +} /* dns_cache_clear() */ + + +struct dns_cache *dns_cache_init(struct dns_cache *cache) { + static const struct dns_cache c_init = { + .acquire = &dns_cache_acquire, + .release = &dns_cache_release, + .query = &dns_cache_query, + .submit = &dns_cache_submit, + .check = &dns_cache_check, + .fetch = &dns_cache_fetch, + .pollfd = &dns_cache_pollfd, + .events = &dns_cache_events, + .clear = &dns_cache_clear, + ._ = { .refcount = 1, }, + }; + + *cache = c_init; + + return cache; +} /* dns_cache_init() */ + + +void dns_cache_close(struct dns_cache *cache) { + if (cache) + cache->release(cache); +} /* dns_cache_close() */ + + +/* + * S O C K E T R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void dns_socketclose(int *fd, const struct dns_options *opts) { + if (opts && opts->closefd.cb) + opts->closefd.cb(fd, opts->closefd.arg); + + if (*fd != -1) { +#if _WIN32 + closesocket(*fd); +#else + close(*fd); +#endif + *fd = -1; + } +} /* dns_socketclose() */ + + +#ifndef HAVE_IOCTLSOCKET +#define HAVE_IOCTLSOCKET (_WIN32 || _WIN64) +#endif + +#ifndef HAVE_SOCK_CLOEXEC +#define HAVE_SOCK_CLOEXEC (defined SOCK_CLOEXEC) +#endif + +#ifndef HAVE_SOCK_NONBLOCK +#define HAVE_SOCK_NONBLOCK (defined SOCK_NONBLOCK) +#endif + +#define DNS_SO_MAXTRY 7 + +static int dns_socket(struct sockaddr *local, int type, int *error_) { + int fd = -1, flags, error; +#if defined FIONBIO + unsigned long opt; +#endif + + flags = 0; +#if HAVE_SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif +#if HAVE_SOCK_NONBLOCK + flags |= SOCK_NONBLOCK; +#endif + if (-1 == (fd = socket(local->sa_family, type|flags, 0))) + goto soerr; + +#if defined F_SETFD && !HAVE_SOCK_CLOEXEC + if (-1 == fcntl(fd, F_SETFD, 1)) + goto syerr; +#endif + +#if defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK + if (-1 == (flags = fcntl(fd, F_GETFL))) + goto syerr; + if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) + goto syerr; +#elif defined FIONBIO && HAVE_IOCTLSOCKET + opt = 1; + if (0 != ioctlsocket(fd, FIONBIO, &opt)) + goto soerr; +#endif + +#if defined SO_NOSIGPIPE + if (type != SOCK_DGRAM) { + if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int))) + goto soerr; + } +#endif + + if (local->sa_family != AF_INET && local->sa_family != AF_INET6) + return fd; + + if (type != SOCK_DGRAM) + return fd; + + /* + * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by + * default. Though the ephemeral range is quite small on OS X + * (49152-65535 on 10.10) and Linux (32768-60999 on 4.4.0, Ubuntu + * Xenial). See also RFC 6056. + * + * TODO: Optionally rely on the kernel to select a random port. + */ + if (*dns_sa_port(local->sa_family, local) == 0) { + struct sockaddr_storage tmp; + unsigned i, port; + + memcpy(&tmp, local, dns_sa_len(local)); + + for (i = 0; i < DNS_SO_MAXTRY; i++) { + port = 1025 + (dns_random() % 64510); + + *dns_sa_port(tmp.ss_family, &tmp) = htons(port); + + if (0 == bind(fd, (struct sockaddr *)&tmp, dns_sa_len(&tmp))) + return fd; + } + + /* NB: continue to next bind statement */ + } + + if (0 == bind(fd, local, dns_sa_len(local))) + return fd; + + /* FALL THROUGH */ +soerr: + error = dns_soerr(); + + goto error; +#if (defined F_SETFD && !HAVE_SOCK_CLOEXEC) || (defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK) +syerr: + error = dns_syerr(); + + goto error; +#endif +error: + *error_ = error; + + dns_socketclose(&fd, NULL); + + return -1; +} /* dns_socket() */ + + +enum { + DNS_SO_UDP_INIT = 1, + DNS_SO_UDP_CONN, + DNS_SO_UDP_SEND, + DNS_SO_UDP_RECV, + DNS_SO_UDP_DONE, + + DNS_SO_TCP_INIT, + DNS_SO_TCP_CONN, + DNS_SO_TCP_SEND, + DNS_SO_TCP_RECV, + DNS_SO_TCP_DONE, + + DNS_SO_SOCKS_INIT, + DNS_SO_SOCKS_CONN, + DNS_SO_SOCKS_HELLO_SEND, + DNS_SO_SOCKS_HELLO_RECV, + DNS_SO_SOCKS_AUTH_SEND, + DNS_SO_SOCKS_AUTH_RECV, + DNS_SO_SOCKS_REQUEST_PREPARE, + DNS_SO_SOCKS_REQUEST_SEND, + DNS_SO_SOCKS_REQUEST_RECV, + DNS_SO_SOCKS_REQUEST_RECV_V6, + DNS_SO_SOCKS_HANDSHAKE_DONE, +}; + +struct dns_socket { + struct dns_options opts; + + int udp; + int tcp; + + int *old; + unsigned onum, olim; + + int type; + + struct sockaddr_storage local, remote; + + struct dns_k_permutor qids; + + struct dns_stat stat; + + struct dns_trace *trace; + + /* + * NOTE: dns_so_reset() zeroes everything from here down. + */ + int state; + + unsigned short qid; + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + enum dns_type qtype; + enum dns_class qclass; + + struct dns_packet *query; + size_t qout; + + /* During a SOCKS handshake the query is temporarily stored + * here. */ + struct dns_packet *query_backup; + + struct dns_clock elapsed; + + struct dns_packet *answer; + size_t alen, apos; +}; /* struct dns_socket */ + + +/* + * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have + * a chance to recognize a state change after installing a persistent event + * and where sequential descriptors with the same integer value returned + * from _pollfd() would be ambiguous. See dns_so_closefds(). + */ +static int dns_so_closefd(struct dns_socket *so, int *fd) { + int error; + + if (*fd == -1) + return 0; + + if (so->opts.closefd.cb) { + if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) { + return error; + } else if (*fd == -1) + return 0; + } + + if (!(so->onum < so->olim)) { + unsigned olim = DNS_PP_MAX(4, so->olim * 2); + void *old; + + if (!(old = realloc(so->old, sizeof so->old[0] * olim))) + return dns_syerr(); + + so->old = old; + so->olim = olim; + } + + so->old[so->onum++] = *fd; + *fd = -1; + + return 0; +} /* dns_so_closefd() */ + + +#define DNS_SO_CLOSE_UDP 0x01 +#define DNS_SO_CLOSE_TCP 0x02 +#define DNS_SO_CLOSE_OLD 0x04 +#define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD) + +static void dns_so_closefds(struct dns_socket *so, int which) { + if (DNS_SO_CLOSE_UDP & which) + dns_socketclose(&so->udp, &so->opts); + if (DNS_SO_CLOSE_TCP & which) + dns_socketclose(&so->tcp, &so->opts); + if (DNS_SO_CLOSE_OLD & which) { + unsigned i; + for (i = 0; i < so->onum; i++) + dns_socketclose(&so->old[i], &so->opts); + so->onum = 0; + free(so->old); + so->old = 0; + so->olim = 0; + } +} /* dns_so_closefds() */ + + +static void dns_so_destroy(struct dns_socket *); + +static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { + static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1, }; + + *so = so_initializer; + so->type = type; + + if (opts) + so->opts = *opts; + + if (local) + memcpy(&so->local, local, dns_sa_len(local)); + + if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error))) + goto error; + + dns_k_permutor_init(&so->qids, 1, 65535); + + return so; +error: + dns_so_destroy(so); + + return 0; +} /* dns_so_init() */ + + +struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { + struct dns_socket *so; + + if (!(so = malloc(sizeof *so))) + goto syerr; + + if (!dns_so_init(so, local, type, opts, error)) + goto error; + + return so; +syerr: + *error = dns_syerr(); +error: + dns_so_close(so); + + return 0; +} /* dns_so_open() */ + + +static void dns_so_destroy(struct dns_socket *so) { + dns_so_reset(so); + dns_so_closefds(so, DNS_SO_CLOSE_ALL); + dns_trace_close(so->trace); +} /* dns_so_destroy() */ + + +void dns_so_close(struct dns_socket *so) { + if (!so) + return; + + dns_so_destroy(so); + + free(so); +} /* dns_so_close() */ + + +void dns_so_reset(struct dns_socket *so) { + dns_p_setptr(&so->answer, NULL); + + memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state)); +} /* dns_so_reset() */ + + +unsigned short dns_so_mkqid(struct dns_socket *so) { + return dns_k_permutor_step(&so->qids); +} /* dns_so_mkqid() */ + + +#define DNS_SO_MINBUF 768 + +static int dns_so_newanswer(struct dns_socket *so, size_t len) { + size_t size = offsetof(struct dns_packet, data) + DNS_PP_MAX(len, DNS_SO_MINBUF); + void *p; + + if (!(p = realloc(so->answer, size))) + return dns_syerr(); + + so->answer = dns_p_init(p, size); + + return 0; +} /* dns_so_newanswer() */ + + +int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) { + struct dns_rr rr; + int error = DNS_EUNKNOWN; + + dns_so_reset(so); + + if ((error = dns_rr_parse(&rr, 12, Q))) + goto error; + + if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error))) + goto error; + /* + * NOTE: Don't bail if expansion is too long; caller may be + * intentionally sending long names. However, we won't be able to + * verify it on return. + */ + + so->qtype = rr.type; + so->qclass = rr.class; + + if ((error = dns_so_newanswer(so, (Q->memo.opt.maxudp)? Q->memo.opt.maxudp : DNS_SO_MINBUF))) + goto syerr; + + memcpy(&so->remote, host, dns_sa_len(host)); + + so->query = Q; + so->qout = 0; + + dns_begin(&so->elapsed); + + if (dns_header(so->query)->qid == 0) + dns_header(so->query)->qid = dns_so_mkqid(so); + + so->qid = dns_header(so->query)->qid; + so->state = (so->opts.socks_host && so->opts.socks_host->ss_family) ? DNS_SO_SOCKS_INIT : + (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT; + + so->stat.queries++; + dns_trace_so_submit(so->trace, Q, host, 0); + + return 0; +syerr: + error = dns_syerr(); +error: + dns_so_reset(so); + dns_trace_so_submit(so->trace, Q, host, error); + return error; +} /* dns_so_submit() */ + + +static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) { + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + struct dns_rr rr; + int error = -1; + + if (P->end < 12) + goto reject; + + if (so->qid != dns_header(P)->qid) + goto reject; + + if (!dns_p_count(P, DNS_S_QD)) + goto reject; + + if (0 != dns_rr_parse(&rr, 12, P)) + goto reject; + + if (rr.type != so->qtype || rr.class != so->qclass) + goto reject; + + if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error))) + goto error; + else if (qlen >= sizeof qname || qlen != so->qlen) + goto reject; + + if (0 != strcasecmp(so->qname, qname)) + goto reject; + + dns_trace_so_verify(so->trace, P, 0); + + return 0; +reject: + error = DNS_EVERIFY; +error: + DNS_SHOW(P, "rejecting packet (%s)", dns_strerror(error)); + dns_trace_so_verify(so->trace, P, error); + + return error; +} /* dns_so_verify() */ + + +static _Bool dns_so_tcp_keep(struct dns_socket *so) { + struct sockaddr_storage remote; + + if (so->tcp == -1) + return 0; + + if (0 != getpeername(so->tcp, (struct sockaddr *)&remote, &(socklen_t){ sizeof remote })) + return 0; + + return 0 == dns_sa_cmp(&remote, &so->remote); +} /* dns_so_tcp_keep() */ + + +/* Convenience functions for sending non-DNS data. */ + +/* Set up everything for sending LENGTH octets. Returns the buffer + for the data. */ +static unsigned char *dns_so_tcp_send_buffer(struct dns_socket *so, size_t length) { + /* Skip the length octets, we are not doing DNS. */ + so->qout = 2; + so->query->end = length; + return so->query->data; +} + +/* Set up everything for receiving LENGTH octets. */ +static void dns_so_tcp_recv_expect(struct dns_socket *so, size_t length) { + /* Skip the length octets, we are not doing DNS. */ + so->apos = 2; + so->alen = length; +} + +/* Returns the buffer containing the received data. */ +static unsigned char *dns_so_tcp_recv_buffer(struct dns_socket *so) { + return so->answer->data; +} + + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warray-bounds" +#endif + +static int dns_so_tcp_send(struct dns_socket *so) { + unsigned char *qsrc; + size_t qend; + int error; + size_t n; + + so->query->data[-2] = 0xff & (so->query->end >> 8); + so->query->data[-1] = 0xff & (so->query->end >> 0); + + qend = so->query->end + 2; + + while (so->qout < qend) { + qsrc = &so->query->data[-2] + so->qout; + n = dns_send_nopipe(so->tcp, (void *)qsrc, qend - so->qout, 0, &error); + dns_trace_sys_send(so->trace, so->tcp, SOCK_STREAM, qsrc, n, error); + if (error) + return error; + so->qout += n; + so->stat.tcp.sent.bytes += n; + } + + so->stat.tcp.sent.count++; + + return 0; +} /* dns_so_tcp_send() */ + + +static int dns_so_tcp_recv(struct dns_socket *so) { + unsigned char *asrc; + size_t aend, alen, n; + int error; + + aend = so->alen + 2; + + while (so->apos < aend) { + asrc = &so->answer->data[-2]; + + n = dns_recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0, &error); + dns_trace_sys_recv(so->trace, so->tcp, SOCK_STREAM, &asrc[so->apos], n, error); + if (error) + return error; + + so->apos += n; + so->stat.tcp.rcvd.bytes += n; + + if (so->alen == 0 && so->apos >= 2) { + alen = ((0xff & so->answer->data[-2]) << 8) + | ((0xff & so->answer->data[-1]) << 0); + + if ((error = dns_so_newanswer(so, alen))) + return error; + + so->alen = alen; + aend = alen + 2; + } + } + + so->answer->end = so->alen; + so->stat.tcp.rcvd.count++; + + return 0; +} /* dns_so_tcp_recv() */ + +#if __clang__ +#pragma clang diagnostic pop +#endif + + +int dns_so_check(struct dns_socket *so) { + int error; + size_t n; + unsigned char *buffer; + +retry: + switch (so->state) { + case DNS_SO_UDP_INIT: + so->state++; + case DNS_SO_UDP_CONN: + error = dns_connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)); + dns_trace_sys_connect(so->trace, so->udp, SOCK_DGRAM, (struct sockaddr *)&so->remote, error); + if (error) + goto error; + + so->state++; + case DNS_SO_UDP_SEND: + n = dns_send(so->udp, (void *)so->query->data, so->query->end, 0, &error); + dns_trace_sys_send(so->trace, so->udp, SOCK_DGRAM, so->query->data, n, error); + if (error) + goto error; + + so->stat.udp.sent.bytes += n; + so->stat.udp.sent.count++; + + so->state++; + case DNS_SO_UDP_RECV: + n = dns_recv(so->udp, (void *)so->answer->data, so->answer->size, 0, &error); + dns_trace_sys_recv(so->trace, so->udp, SOCK_DGRAM, so->answer->data, n, error); + if (error) + goto error; + + so->answer->end = n; + so->stat.udp.rcvd.bytes += n; + so->stat.udp.rcvd.count++; + + if ((error = dns_so_verify(so, so->answer))) + goto trash; + + so->state++; + case DNS_SO_UDP_DONE: + if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM) + return 0; + + so->state++; + case DNS_SO_TCP_INIT: + if (dns_so_tcp_keep(so)) { + so->state = DNS_SO_TCP_SEND; + + goto retry; + } + + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + + if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) + goto error; + + so->state++; + case DNS_SO_TCP_CONN: + error = dns_connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)); + dns_trace_sys_connect(so->trace, so->tcp, SOCK_STREAM, (struct sockaddr *)&so->remote, error); + if (error && error != DNS_EISCONN) + goto error; + + so->state++; + case DNS_SO_TCP_SEND: + if ((error = dns_so_tcp_send(so))) + goto error; + + so->state++; + case DNS_SO_TCP_RECV: + if ((error = dns_so_tcp_recv(so))) + goto error; + + so->state++; + case DNS_SO_TCP_DONE: + /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */ + if (so->type != SOCK_STREAM) { + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + } + + if ((error = dns_so_verify(so, so->answer))) + goto error; + + return 0; + case DNS_SO_SOCKS_INIT: + if ((error = dns_so_closefd(so, &so->tcp))) + goto error; + + if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) + goto error; + + so->state++; + case DNS_SO_SOCKS_CONN: { + unsigned char method; + + error = dns_connect(so->tcp, (struct sockaddr *)so->opts.socks_host, dns_sa_len(so->opts.socks_host)); + dns_trace_sys_connect(so->trace, so->tcp, SOCK_STREAM, (struct sockaddr *)so->opts.socks_host, error); + if (error && error != DNS_EISCONN) + goto error; + + /* We need to do a handshake with the SOCKS server, + * but the query is already in the buffer. Move it + * out of the way. */ + dns_p_movptr(&so->query_backup, &so->query); + + /* Create a new buffer for the handshake. */ + dns_p_grow(&so->query); + + /* Negotiate method. */ + buffer = dns_so_tcp_send_buffer(so, 3); + buffer[0] = 5; /* RFC-1928 VER field. */ + buffer[1] = 1; /* NMETHODS */ + if (so->opts.socks_user) + method = 2; /* Method: username/password authentication. */ + else + method = 0; /* Method: No authentication required. */ + buffer[2] = method; + + so->state++; + } + case DNS_SO_SOCKS_HELLO_SEND: + if ((error = dns_so_tcp_send(so))) + goto error; + + dns_so_tcp_recv_expect(so, 2); + so->state++; + case DNS_SO_SOCKS_HELLO_RECV: { + unsigned char method; + + if ((error = dns_so_tcp_recv(so))) + goto error; + + buffer = dns_so_tcp_recv_buffer(so); + method = so->opts.socks_user ? 2 : 0; + if (buffer[0] != 5 || buffer[1] != method) { + /* Socks server returned wrong version or does + not support our requested method. */ + error = ENOTSUP; /* Fixme: Is there a better errno? */ + goto error; + } + + if (method == 0) { + /* No authentication, go ahead and send the + request. */ + so->state = DNS_SO_SOCKS_REQUEST_PREPARE; + goto retry; + } + + /* Prepare username/password sub-negotiation. */ + if (! so->opts.socks_password) { + error = EINVAL; /* No password given. */ + goto error; + } else { + size_t buflen, ulen, plen; + + ulen = strlen(so->opts.socks_user); + plen = strlen(so->opts.socks_password); + if (!ulen || ulen > 255 || !plen || plen > 255) { + error = EINVAL; /* Credentials too long or too short. */ + goto error; + } + + buffer = dns_so_tcp_send_buffer(so, 3 + ulen + plen); + buffer[0] = 1; /* VER of the sub-negotiation. */ + buffer[1] = (unsigned char) ulen; + buflen = 2; + memcpy (buffer+buflen, so->opts.socks_user, ulen); + buflen += ulen; + buffer[buflen++] = (unsigned char) plen; + memcpy (buffer+buflen, so->opts.socks_password, plen); + } + + so->state++; + } + case DNS_SO_SOCKS_AUTH_SEND: + if ((error = dns_so_tcp_send(so))) + goto error; + + /* Skip the two length octets, and receive two octets. */ + dns_so_tcp_recv_expect(so, 2); + + so->state++; + case DNS_SO_SOCKS_AUTH_RECV: + if ((error = dns_so_tcp_recv(so))) + goto error; + + buffer = dns_so_tcp_recv_buffer(so); + if (buffer[0] != 1) { + /* SOCKS server returned wrong version. */ + error = EPROTO; + goto error; + } + if (buffer[1]) { + /* SOCKS server denied access. */ + error = EACCES; + goto error; + } + + so->state++; + case DNS_SO_SOCKS_REQUEST_PREPARE: + /* Send request details (rfc-1928, 4). */ + buffer = dns_so_tcp_send_buffer(so, so->remote.ss_family == AF_INET6 ? 22 : 10); + buffer[0] = 5; /* VER */ + buffer[1] = 1; /* CMD = CONNECT */ + buffer[2] = 0; /* RSV */ + if (so->remote.ss_family == AF_INET6) { + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&so->remote; + + buffer[3] = 4; /* ATYP = IPv6 */ + memcpy (buffer+ 4, &addr_in6->sin6_addr.s6_addr, 16); /* DST.ADDR */ + memcpy (buffer+20, &addr_in6->sin6_port, 2); /* DST.PORT */ + } else { + struct sockaddr_in *addr_in = (struct sockaddr_in *)&so->remote; + + buffer[3] = 1; /* ATYP = IPv4 */ + memcpy (buffer+4, &addr_in->sin_addr.s_addr, 4); /* DST.ADDR */ + memcpy (buffer+8, &addr_in->sin_port, 2); /* DST.PORT */ + } + + so->state++; + case DNS_SO_SOCKS_REQUEST_SEND: + if ((error = dns_so_tcp_send(so))) + goto error; + + /* Expect ten octets. This is the length of the + * response assuming a IPv4 address is used. */ + dns_so_tcp_recv_expect(so, 10); + so->state++; + case DNS_SO_SOCKS_REQUEST_RECV: + if ((error = dns_so_tcp_recv(so))) + goto error; + + buffer = dns_so_tcp_recv_buffer(so); + if (buffer[0] != 5 || buffer[2] != 0) { + /* Socks server returned wrong version or the + reserved field is not zero. */ + error = EPROTO; + goto error; + } + if (buffer[1]) { + switch (buffer[1]) { + case 0x01: /* general SOCKS server failure. */ + error = ENETDOWN; + break; + case 0x02: /* connection not allowed by ruleset. */ + error = EACCES; + break; + case 0x03: /* Network unreachable */ + error = ENETUNREACH; + break; + case 0x04: /* Host unreachable */ + error = EHOSTUNREACH; + break; + case 0x05: /* Connection refused */ + error = ECONNREFUSED; + break; + case 0x06: /* TTL expired */ + error = ETIMEDOUT; + break; + case 0x08: /* Address type not supported */ + error = EPROTONOSUPPORT; + break; + case 0x07: /* Command not supported */ + default: + error = ENOTSUP; /* Fixme: Is there a better error? */ + break; + } + goto error; + } + + if (buffer[3] == 1) { + /* This was indeed an IPv4 address. */ + so->state = DNS_SO_SOCKS_HANDSHAKE_DONE; + goto retry; + } + + if (buffer[3] != 4) { + error = ENOTSUP; + goto error; + } + + /* Expect receive twelve octets. This accounts for + * the remaining bytes assuming an IPv6 address is + * used. */ + dns_so_tcp_recv_expect(so, 12); + so->state++; + case DNS_SO_SOCKS_REQUEST_RECV_V6: + if ((error = dns_so_tcp_recv(so))) + goto error; + + so->state++; + case DNS_SO_SOCKS_HANDSHAKE_DONE: + /* We have not way to store the actual address used by + * the server. Then again, we don't really care. */ + + /* Restore the query. */ + dns_p_movptr(&so->query, &so->query_backup); + + /* Reset cursors. */ + so->qout = 0; + so->apos = 0; + so->alen = 0; + + /* SOCKS handshake is done. Proceed with the + * lookup. */ + so->state = DNS_SO_TCP_SEND; + goto retry; + default: + error = DNS_EUNKNOWN; + + goto error; + } /* switch() */ + +trash: + DNS_CARP("discarding packet"); + goto retry; +error: + switch (error) { + case DNS_EINTR: + goto retry; + case DNS_EINPROGRESS: + /* FALL THROUGH */ + case DNS_EALREADY: + /* FALL THROUGH */ +#if DNS_EWOULDBLOCK != DNS_EAGAIN + case DNS_EWOULDBLOCK: + /* FALL THROUGH */ +#endif + error = DNS_EAGAIN; + + break; + } /* switch() */ + + return error; +} /* dns_so_check() */ + + +struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) { + struct dns_packet *answer; + + switch (so->state) { + case DNS_SO_UDP_DONE: + case DNS_SO_TCP_DONE: + answer = so->answer; + so->answer = 0; + dns_trace_so_fetch(so->trace, answer, 0); + + return answer; + default: + *error = DNS_EUNKNOWN; + dns_trace_so_fetch(so->trace, NULL, *error); + + return 0; + } +} /* dns_so_fetch() */ + + +struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) { + struct dns_packet *A; + int error; + + if (!so->state) { + if ((error = dns_so_submit(so, Q, host))) + goto error; + } + + if ((error = dns_so_check(so))) + goto error; + + if (!(A = dns_so_fetch(so, &error))) + goto error; + + dns_so_reset(so); + + return A; +error: + *error_ = error; + + return 0; +} /* dns_so_query() */ + + +time_t dns_so_elapsed(struct dns_socket *so) { + return dns_elapsed(&so->elapsed); +} /* dns_so_elapsed() */ + + +void dns_so_clear(struct dns_socket *so) { + dns_so_closefds(so, DNS_SO_CLOSE_OLD); +} /* dns_so_clear() */ + + +static int dns_so_events2(struct dns_socket *so, enum dns_events type) { + int events = 0; + + switch (so->state) { + case DNS_SO_UDP_CONN: + case DNS_SO_UDP_SEND: + events |= DNS_POLLOUT; + + break; + case DNS_SO_UDP_RECV: + events |= DNS_POLLIN; + + break; + case DNS_SO_TCP_CONN: + case DNS_SO_TCP_SEND: + events |= DNS_POLLOUT; + + break; + case DNS_SO_TCP_RECV: + events |= DNS_POLLIN; + + break; + } /* switch() */ + + switch (type) { + case DNS_LIBEVENT: + return DNS_POLL2EV(events); + default: + return events; + } /* switch() */ +} /* dns_so_events2() */ + + +int dns_so_events(struct dns_socket *so) { + return dns_so_events2(so, so->opts.events); +} /* dns_so_events() */ + + +int dns_so_pollfd(struct dns_socket *so) { + switch (so->state) { + case DNS_SO_UDP_CONN: + case DNS_SO_UDP_SEND: + case DNS_SO_UDP_RECV: + return so->udp; + case DNS_SO_TCP_CONN: + case DNS_SO_TCP_SEND: + case DNS_SO_TCP_RECV: + return so->tcp; + } /* switch() */ + + return -1; +} /* dns_so_pollfd() */ + + +int dns_so_poll(struct dns_socket *so, int timeout) { + return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout); +} /* dns_so_poll() */ + + +const struct dns_stat *dns_so_stat(struct dns_socket *so) { + return &so->stat; +} /* dns_so_stat() */ + + +struct dns_trace *dns_so_trace(struct dns_socket *so) { + return so->trace; +} /* dns_so_trace() */ + + +void dns_so_settrace(struct dns_socket *so, struct dns_trace *trace) { + struct dns_trace *otrace = so->trace; + so->trace = dns_trace_acquire_p(trace); + dns_trace_close(otrace); +} /* dns_so_settrace() */ + + +/* + * R E S O L V E R R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +enum dns_res_state { + DNS_R_INIT, + DNS_R_GLUE, + DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */ + + DNS_R_FILE, /* Lookup in local hosts database */ + + DNS_R_CACHE, /* Lookup in application cache */ + DNS_R_SUBMIT, + DNS_R_CHECK, + DNS_R_FETCH, + + DNS_R_BIND, /* Lookup in the network */ + DNS_R_SEARCH, + DNS_R_HINTS, + DNS_R_ITERATE, + DNS_R_FOREACH_NS, + DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */ + DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */ + DNS_R_FOREACH_A, + DNS_R_QUERY_A, + DNS_R_CNAME0_A, + DNS_R_CNAME1_A, + + DNS_R_FINISH, + DNS_R_SMART0_A, + DNS_R_SMART1_A, + DNS_R_DONE, + DNS_R_SERVFAIL, +}; /* enum dns_res_state */ + + +#define DNS_R_MAXDEPTH 8 +#define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1) + +struct dns_resolver { + struct dns_socket so; + + struct dns_resolv_conf *resconf; + struct dns_hosts *hosts; + struct dns_hints *hints; + struct dns_cache *cache; + struct dns_trace *trace; + + dns_atomic_t refcount; + + /* Reset zeroes everything below here. */ + + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + + enum dns_type qtype; + enum dns_class qclass; + + struct dns_clock elapsed; + + dns_resconf_i_t search; + + struct dns_rr_i smart; + + struct dns_packet *nodata; /* answer if nothing better */ + + unsigned sp; + + struct dns_res_frame { + enum dns_res_state state; + + int error; + int which; /* (B)IND, (F)ILE; index into resconf->lookup */ + int qflags; + + unsigned attempts; + + struct dns_packet *query, *answer, *hints; + + struct dns_rr_i hints_i, hints_j; + struct dns_rr hints_ns, ans_cname; + } stack[DNS_R_MAXDEPTH]; +}; /* struct dns_resolver */ + + +static int dns_res_tcp2type(int tcp) { + switch (tcp) { + case DNS_RESCONF_TCP_ONLY: + case DNS_RESCONF_TCP_SOCKS: + return SOCK_STREAM; + case DNS_RESCONF_TCP_DISABLE: + return SOCK_DGRAM; + default: + return 0; + } +} /* dns_res_tcp2type() */ + +struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) { + static const struct dns_resolver R_initializer + = { .refcount = 1, }; + struct dns_resolver *R = 0; + int type, error; + + /* + * Grab ref count early because the caller may have passed us a mortal + * reference, and we want to do the right thing if we return early + * from an error. + */ + if (resconf) + dns_resconf_acquire(resconf); + if (hosts) + dns_hosts_acquire(hosts); + if (hints) + dns_hints_acquire(hints); + if (cache) + dns_cache_acquire(cache); + + /* + * Don't try to load it ourselves because a NULL object might be an + * error from, say, dns_resconf_root(), and loading + * dns_resconf_local() by default would create undesirable surpises. + */ + if (!resconf || !hosts || !hints) { + if (!*_error) + *_error = EINVAL; + goto _error; + } + + if (!(R = malloc(sizeof *R))) + goto syerr; + + *R = R_initializer; + type = dns_res_tcp2type(resconf->options.tcp); + + if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error)) + goto error; + + R->resconf = resconf; + R->hosts = hosts; + R->hints = hints; + R->cache = cache; + + return R; +syerr: + error = dns_syerr(); +error: + *_error = error; +_error: + dns_res_close(R); + + dns_resconf_close(resconf); + dns_hosts_close(hosts); + dns_hints_close(hints); + dns_cache_close(cache); + + return 0; +} /* dns_res_open() */ + + +struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) { + struct dns_resolv_conf *resconf = 0; + struct dns_hosts *hosts = 0; + struct dns_hints *hints = 0; + struct dns_resolver *res = 0; + + if (!(resconf = dns_resconf_local(error))) + goto epilog; + + if (!(hosts = dns_hosts_local(error))) + goto epilog; + + if (!(hints = dns_hints_local(resconf, error))) + goto epilog; + + if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error))) + goto epilog; + +epilog: + dns_resconf_close(resconf); + dns_hosts_close(hosts); + dns_hints_close(hints); + + return res; +} /* dns_res_stub() */ + + +static void dns_res_frame_destroy(struct dns_resolver *R, struct dns_res_frame *frame) { + (void)R; + + dns_p_setptr(&frame->query, NULL); + dns_p_setptr(&frame->answer, NULL); + dns_p_setptr(&frame->hints, NULL); +} /* dns_res_frame_destroy() */ + + +static void dns_res_frame_init(struct dns_resolver *R, struct dns_res_frame *frame) { + memset(frame, '\0', sizeof *frame); + + /* + * NB: Can be invoked from dns_res_open, before R->resconf has been + * initialized. + */ + if (R->resconf) { + if (!R->resconf->options.recurse) + frame->qflags |= DNS_Q_RD; + if (R->resconf->options.edns0) + frame->qflags |= DNS_Q_EDNS0; + } +} /* dns_res_frame_init() */ + + +static void dns_res_frame_reset(struct dns_resolver *R, struct dns_res_frame *frame) { + dns_res_frame_destroy(R, frame); + dns_res_frame_init(R, frame); +} /* dns_res_frame_reset() */ + + +static dns_error_t dns_res_frame_prepare(struct dns_resolver *R, struct dns_res_frame *F, const char *qname, enum dns_type qtype, enum dns_class qclass) { + struct dns_packet *P = NULL; + + if (!(F < endof(R->stack))) + return DNS_EUNKNOWN; + + dns_p_movptr(&P, &F->query); + dns_res_frame_reset(R, F); + dns_p_movptr(&F->query, &P); + + return dns_q_make(&F->query, qname, qtype, qclass, F->qflags); +} /* dns_res_frame_prepare() */ + + +void dns_res_reset(struct dns_resolver *R) { + unsigned i; + + dns_so_reset(&R->so); + dns_p_setptr(&R->nodata, NULL); + + for (i = 0; i < lengthof(R->stack); i++) + dns_res_frame_destroy(R, &R->stack[i]); + + memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname)); + + for (i = 0; i < lengthof(R->stack); i++) + dns_res_frame_init(R, &R->stack[i]); +} /* dns_res_reset() */ + + +void dns_res_close(struct dns_resolver *R) { + if (!R || 1 < dns_res_release(R)) + return; + + dns_res_reset(R); + + dns_so_destroy(&R->so); + + dns_hints_close(R->hints); + dns_hosts_close(R->hosts); + dns_resconf_close(R->resconf); + dns_cache_close(R->cache); + dns_trace_close(R->trace); + + free(R); +} /* dns_res_close() */ + + +dns_refcount_t dns_res_acquire(struct dns_resolver *R) { + return dns_atomic_fetch_add(&R->refcount); +} /* dns_res_acquire() */ + + +dns_refcount_t dns_res_release(struct dns_resolver *R) { + return dns_atomic_fetch_sub(&R->refcount); +} /* dns_res_release() */ + + +struct dns_resolver *dns_res_mortal(struct dns_resolver *res) { + if (res) + dns_res_release(res); + return res; +} /* dns_res_mortal() */ + + +static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) { + size_t bufsiz = P0->end + P1->end; + struct dns_packet *P[3] = { P0, P1, 0 }; + struct dns_rr rr[3]; + int error, copy, i; + enum dns_section section; + +retry: + if (!(P[2] = dns_p_make(bufsiz, &error))) + goto error; + + dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) { + if ((error = dns_rr_copy(P[2], &rr[0], P[0]))) + goto error; + } + + for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) { + for (i = 0; i < 2; i++) { + dns_rr_foreach(&rr[i], P[i], .section = section) { + copy = 1; + + dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) { + if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) { + copy = 0; + + break; + } + } + + if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) { + if (error == DNS_ENOBUFS && bufsiz < 65535) { + dns_p_setptr(&P[2], NULL); + + bufsiz = DNS_PP_MAX(65535, bufsiz * 2); + + goto retry; + } + + goto error; + } + } /* foreach(rr) */ + } /* foreach(packet) */ + } /* foreach(section) */ + + return P[2]; +error: + *error_ = error; + + dns_p_free(P[2]); + + return 0; +} /* dns_res_merge() */ + + +static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { + struct dns_packet *P = dns_p_new(512); + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + enum dns_type qtype; + struct dns_rr rr; + unsigned sp; + int error; + + if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error)) + || qlen >= sizeof qname) + return 0; + + if (!(qtype = dns_rr_type(12, Q))) + return 0; + + if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0))) + return 0; + + for (sp = 0; sp <= R->sp; sp++) { + if (!R->stack[sp].answer) + continue; + + dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) { + rr.section = DNS_S_AN; + + if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) + return 0; + } + } + + if (dns_p_count(P, DNS_S_AN) > 0) + goto copy; + + /* Otherwise, look for a CNAME */ + for (sp = 0; sp <= R->sp; sp++) { + if (!R->stack[sp].answer) + continue; + + dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) { + rr.section = DNS_S_AN; + + if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) + return 0; + } + } + + if (!dns_p_count(P, DNS_S_AN)) + return 0; + +copy: + return dns_p_copy(dns_p_make(P->end, &error), P); +} /* dns_res_glue() */ + + +/* + * Sort NS records by three criteria: + * + * 1) Whether glue is present. + * 2) Whether glue record is original or of recursive lookup. + * 3) Randomly shuffle records which share the above criteria. + * + * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will + * be added during an iteration. + * + * FIXME: Only groks A glue, not AAAA glue. + */ +static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { + _Bool glued[2] = { 0 }; + struct dns_rr x = { 0 }, y = { 0 }; + struct dns_ns ns; + int cmp, error; + + if (!(error = dns_ns_parse(&ns, a, P))) + glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); + + if (!(error = dns_ns_parse(&ns, b, P))) + glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); + + if ((cmp = glued[1] - glued[0])) { + return cmp; + } else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0]))) { + return cmp; + } else { + return dns_rr_i_shuffle(a, b, i, P); + } +} /* dns_res_nameserv_cmp() */ + + +#define dgoto(sp, i) \ + do { R->stack[(sp)].state = (i); goto exec; } while (0) + +static int dns_res_exec(struct dns_resolver *R) { + struct dns_res_frame *F; + struct dns_packet *P; + union { + char host[DNS_D_MAXNAME + 1]; + char name[DNS_D_MAXNAME + 1]; + struct dns_ns ns; + struct dns_cname cname; + } u; + size_t len; + struct dns_rr rr; + int error; + +exec: + + F = &R->stack[R->sp]; + + switch (F->state) { + case DNS_R_INIT: + F->state++; + case DNS_R_GLUE: + if (R->sp == 0) + dgoto(R->sp, DNS_R_SWITCH); + + if (!F->query) + goto noquery; + + if (!(F->answer = dns_res_glue(R, F->query))) + dgoto(R->sp, DNS_R_SWITCH); + + if (!(len = dns_d_expand(u.name, sizeof u.name, 12, F->query, &error))) + goto error; + else if (len >= sizeof u.name) + goto toolong; + + dns_rr_foreach(&rr, F->answer, .name = u.name, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) { + dgoto(R->sp, DNS_R_FINISH); + } + + dns_rr_foreach(&rr, F->answer, .name = u.name, .type = DNS_T_CNAME, .section = DNS_S_AN) { + F->ans_cname = rr; + + dgoto(R->sp, DNS_R_CNAME0_A); + } + + F->state++; + case DNS_R_SWITCH: + while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) { + switch (R->resconf->lookup[F->which++]) { + case 'b': case 'B': + dgoto(R->sp, DNS_R_BIND); + case 'f': case 'F': + dgoto(R->sp, DNS_R_FILE); + case 'c': case 'C': + if (R->cache) + dgoto(R->sp, DNS_R_CACHE); + + break; + default: + break; + } + } + + /* + * FIXME: Examine more closely whether our logic is correct + * and DNS_R_SERVFAIL is the correct default response. + * + * Case 1: We got here because we never got an answer on the + * wire. All queries timed-out and we reached maximum + * attempts count. See DNS_R_FOREACH_NS. In that case + * DNS_R_SERVFAIL is the correct state, unless we want to + * return DNS_ETIMEDOUT. + * + * Case 2: We were a stub resolver and got an unsatisfactory + * answer (empty ANSWER section) which caused us to jump + * back to DNS_R_SEARCH and ultimately to DNS_R_SWITCH. We + * return the answer returned from the wire, which we + * stashed in R->nodata. + * + * Case 3: We reached maximum attempts count as in case #1, + * but never got an authoritative response which caused us + * to short-circuit. See end of DNS_R_QUERY_A case. We + * should probably prepare R->nodata as in case #2. + */ + if (R->sp == 0 && R->nodata) { /* XXX: can we just return nodata regardless? */ + dns_p_movptr(&F->answer, &R->nodata); + dgoto(R->sp, DNS_R_FINISH); + } + + dgoto(R->sp, DNS_R_SERVFAIL); + case DNS_R_FILE: + if (R->sp > 0) { + if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) + goto error; + + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + } else { + R->search = 0; + + while ((len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) { + if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) + goto error; + + if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) + goto error; + + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + } + } + + dgoto(R->sp, DNS_R_SWITCH); + case DNS_R_CACHE: + error = 0; + + if (!F->query && (error = dns_q_make(&F->query, R->qname, R->qtype, R->qclass, F->qflags))) + goto error; + + if (dns_p_setptr(&F->answer, R->cache->query(F->query, R->cache, &error))) { + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + + dgoto(R->sp, DNS_R_SWITCH); + } else if (error) + goto error; + + F->state++; + case DNS_R_SUBMIT: + if ((error = R->cache->submit(F->query, R->cache))) + goto error; + + F->state++; + case DNS_R_CHECK: + if ((error = R->cache->check(R->cache))) + goto error; + + F->state++; + case DNS_R_FETCH: + error = 0; + + if (dns_p_setptr(&F->answer, R->cache->fetch(R->cache, &error))) { + if (dns_p_count(F->answer, DNS_S_AN) > 0) + dgoto(R->sp, DNS_R_FINISH); + + dns_p_setptr(&F->answer, NULL); + + dgoto(R->sp, DNS_R_SWITCH); + } else if (error) + goto error; + + dgoto(R->sp, DNS_R_SWITCH); + case DNS_R_BIND: + if (R->sp > 0) { + if (!F->query) + goto noquery; + + dgoto(R->sp, DNS_R_HINTS); + } + + R->search = 0; + + F->state++; + case DNS_R_SEARCH: + /* + * XXX: We probably should only apply the domain search + * algorithm if R->sp == 0. + */ + if (!(len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) + dgoto(R->sp, DNS_R_SWITCH); + + if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) + goto error; + + F->state++; + case DNS_R_HINTS: + if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error))) + goto error; + + F->state++; + case DNS_R_ITERATE: + dns_rr_i_init(&F->hints_i, F->hints); + + F->hints_i.section = DNS_S_AUTHORITY; + F->hints_i.type = DNS_T_NS; + F->hints_i.sort = &dns_res_nameserv_cmp; + F->hints_i.args[0] = F->hints->end; + + F->state++; + case DNS_R_FOREACH_NS: + dns_rr_i_save(&F->hints_i); + + /* Load our next nameserver host. */ + if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) { + if (++F->attempts < R->resconf->options.attempts) + dgoto(R->sp, DNS_R_ITERATE); + + dgoto(R->sp, DNS_R_SWITCH); + } + + dns_rr_i_init(&F->hints_j, F->hints); + + /* Assume there are glue records */ + dgoto(R->sp, DNS_R_FOREACH_A); + case DNS_R_RESOLV0_NS: + /* Have we reached our max depth? */ + if (&F[1] >= endof(R->stack)) + dgoto(R->sp, DNS_R_FOREACH_NS); + + if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) + goto error; + if ((error = dns_res_frame_prepare(R, &F[1], u.ns.host, DNS_T_A, DNS_C_IN))) + goto error; + + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + case DNS_R_RESOLV1_NS: + if (!(len = dns_d_expand(u.host, sizeof u.host, 12, F[1].query, &error))) + goto error; + else if (len >= sizeof u.host) + goto toolong; + + dns_rr_foreach(&rr, F[1].answer, .name = u.host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) { + rr.section = DNS_S_AR; + + if ((error = dns_rr_copy(F->hints, &rr, F[1].answer))) + goto error; + + dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */ + } + + dgoto(R->sp, DNS_R_FOREACH_NS); + case DNS_R_FOREACH_A: { + struct dns_a a; + struct sockaddr_in sin; + + /* + * NOTE: Iterator initialized in DNS_R_FOREACH_NS because + * this state is re-entrant, but we need to reset + * .name to a valid pointer each time. + */ + if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) + goto error; + + F->hints_j.name = u.ns.host; + F->hints_j.type = DNS_T_A; + F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; + + if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { + if (!dns_rr_i_count(&F->hints_j)) + dgoto(R->sp, DNS_R_RESOLV0_NS); + + dgoto(R->sp, DNS_R_FOREACH_NS); + } + + if ((error = dns_a_parse(&a, &rr, F->hints))) + goto error; + + memset(&sin, '\0', sizeof sin); /* NB: silence valgrind */ + sin.sin_family = AF_INET; + sin.sin_addr = a.addr; + if (R->sp == 0) + sin.sin_port = dns_hints_port(R->hints, AF_INET, &sin.sin_addr); + else + sin.sin_port = htons(53); + + if (DNS_DEBUG) { + char addr[INET_ADDRSTRLEN + 1]; + dns_a_print(addr, sizeof addr, &a); + dns_header(F->query)->qid = dns_so_mkqid(&R->so); + DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp); + } + + dns_trace_setcname(R->trace, u.ns.host, (struct sockaddr *)&sin); + + if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin))) + goto error; + + F->state++; + } + case DNS_R_QUERY_A: + if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) + dgoto(R->sp, DNS_R_FOREACH_A); + + if ((error = dns_so_check(&R->so))) + goto error; + + if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) + goto error; + + if (DNS_DEBUG) { + DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp); + } + + if (dns_p_rcode(F->answer) == DNS_RC_FORMERR || + dns_p_rcode(F->answer) == DNS_RC_NOTIMP || + dns_p_rcode(F->answer) == DNS_RC_BADVERS) { + /* Temporarily disable EDNS0 and try again. */ + if (F->qflags & DNS_Q_EDNS0) { + F->qflags &= ~DNS_Q_EDNS0; + if ((error = dns_q_remake(&F->query, F->qflags))) + goto error; + + dgoto(R->sp, DNS_R_FOREACH_A); + } + } + + if ((error = dns_rr_parse(&rr, 12, F->query))) + goto error; + + if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error))) + goto error; + else if (len >= sizeof u.name) + goto toolong; + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) { + dgoto(R->sp, DNS_R_FINISH); /* Found */ + } + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) { + F->ans_cname = rr; + + dgoto(R->sp, DNS_R_CNAME0_A); + } + + /* + * XXX: The condition here should probably check whether + * R->sp == 0, because DNS_R_SEARCH runs regardless of + * options.recurse. See DNS_R_BIND. + */ + if (!R->resconf->options.recurse) { + /* Make first answer our tentative answer */ + if (!R->nodata) + dns_p_movptr(&R->nodata, &F->answer); + + dgoto(R->sp, DNS_R_SEARCH); + } + + dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) { + dns_p_movptr(&F->hints, &F->answer); + + dgoto(R->sp, DNS_R_ITERATE); + } + + /* XXX: Should this go further up? */ + if (dns_header(F->answer)->aa) + dgoto(R->sp, DNS_R_FINISH); + + /* XXX: Should we copy F->answer to R->nodata? */ + + dgoto(R->sp, DNS_R_FOREACH_A); + case DNS_R_CNAME0_A: + if (&F[1] >= endof(R->stack)) + dgoto(R->sp, DNS_R_FINISH); + + if ((error = dns_cname_parse(&u.cname, &F->ans_cname, F->answer))) + goto error; + if ((error = dns_res_frame_prepare(R, &F[1], u.cname.host, dns_rr_type(12, F->query), DNS_C_IN))) + goto error; + + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + case DNS_R_CNAME1_A: + if (!(P = dns_res_merge(F->answer, F[1].answer, &error))) + goto error; + + dns_p_setptr(&F->answer, P); + + dgoto(R->sp, DNS_R_FINISH); + case DNS_R_FINISH: + if (!F->answer) + goto noanswer; + + if (!R->resconf->options.smart || R->sp > 0) + dgoto(R->sp, DNS_R_DONE); + + R->smart.section = DNS_S_AN; + R->smart.type = R->qtype; + + dns_rr_i_init(&R->smart, F->answer); + + F->state++; + case DNS_R_SMART0_A: + if (&F[1] >= endof(R->stack)) + dgoto(R->sp, DNS_R_DONE); + + while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) { + union { + struct dns_ns ns; + struct dns_mx mx; + struct dns_srv srv; + } rd; + const char *qname; + enum dns_type qtype; + enum dns_class qclass; + + switch (rr.type) { + case DNS_T_NS: + if ((error = dns_ns_parse(&rd.ns, &rr, F->answer))) + goto error; + + qname = rd.ns.host; + qtype = DNS_T_A; + qclass = DNS_C_IN; + + break; + case DNS_T_MX: + if ((error = dns_mx_parse(&rd.mx, &rr, F->answer))) + goto error; + + qname = rd.mx.host; + qtype = DNS_T_A; + qclass = DNS_C_IN; + + break; + case DNS_T_SRV: + if ((error = dns_srv_parse(&rd.srv, &rr, F->answer))) + goto error; + + qname = rd.srv.target; + qtype = DNS_T_A; + qclass = DNS_C_IN; + + break; + default: + continue; + } /* switch() */ + + if ((error = dns_res_frame_prepare(R, &F[1], qname, qtype, qclass))) + goto error; + + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + } /* while() */ + + /* + * NOTE: SMTP specification says to fallback to A record. + * + * XXX: Should we add a mock MX answer? + */ + if (R->qtype == DNS_T_MX && R->smart.state.count == 0) { + if ((error = dns_res_frame_prepare(R, &F[1], R->qname, DNS_T_A, DNS_C_IN))) + goto error; + + R->smart.state.count++; + F->state++; + + dgoto(++R->sp, DNS_R_INIT); + } + + dgoto(R->sp, DNS_R_DONE); + case DNS_R_SMART1_A: + if (!F[1].answer) + goto noanswer; + + /* + * FIXME: For CNAME chains (which are typically illegal in + * this context), we should rewrite the record host name + * to the original smart qname. All the user cares about + * is locating that A/AAAA record. + */ + dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) { + rr.section = DNS_S_AR; + + if (dns_rr_exists(&rr, F[1].answer, F->answer)) + continue; + + while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) { + if (error != DNS_ENOBUFS) + goto error; + if ((error = dns_p_grow(&F->answer))) + goto error; + } + } + + dgoto(R->sp, DNS_R_SMART0_A); + case DNS_R_DONE: + if (!F->answer) + goto noanswer; + + if (R->sp > 0) + dgoto(--R->sp, F[-1].state); + + break; + case DNS_R_SERVFAIL: + if (!dns_p_setptr(&F->answer, dns_p_make(DNS_P_QBUFSIZ, &error))) + goto error; + + dns_header(F->answer)->qr = 1; + dns_header(F->answer)->rcode = DNS_RC_SERVFAIL; + + if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0))) + goto error; + + dgoto(R->sp, DNS_R_DONE); + default: + error = EINVAL; + + goto error; + } /* switch () */ + + return 0; +noquery: + error = DNS_ENOQUERY; + + goto error; +noanswer: + error = DNS_ENOANSWER; + + goto error; +toolong: + error = DNS_EILLEGAL; + + /* FALL THROUGH */ +error: + return error; +} /* dns_res_exec() */ + +#undef goto + + +void dns_res_clear(struct dns_resolver *R) { + switch (R->stack[R->sp].state) { + case DNS_R_CHECK: + R->cache->clear(R->cache); + break; + default: + dns_so_clear(&R->so); + break; + } +} /* dns_res_clear() */ + + +static int dns_res_events2(struct dns_resolver *R, enum dns_events type) { + int events; + + switch (R->stack[R->sp].state) { + case DNS_R_CHECK: + events = R->cache->events(R->cache); + + return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events; + default: + return dns_so_events2(&R->so, type); + } +} /* dns_res_events2() */ + + +int dns_res_events(struct dns_resolver *R) { + return dns_res_events2(R, R->so.opts.events); +} /* dns_res_events() */ + + +int dns_res_pollfd(struct dns_resolver *R) { + switch (R->stack[R->sp].state) { + case DNS_R_CHECK: + return R->cache->pollfd(R->cache); + default: + return dns_so_pollfd(&R->so); + } +} /* dns_res_pollfd() */ + + +time_t dns_res_timeout(struct dns_resolver *R) { + time_t elapsed; + + switch (R->stack[R->sp].state) { +#if 0 + case DNS_R_QUERY_AAAA: +#endif + case DNS_R_QUERY_A: + elapsed = dns_so_elapsed(&R->so); + + if (elapsed <= dns_resconf_timeout(R->resconf)) + return R->resconf->options.timeout - elapsed; + + break; + default: + break; + } /* switch() */ + + /* + * NOTE: We're not in a pollable state, or the user code hasn't + * called dns_res_check properly. The calling code is probably + * broken. Put them into a slow-burn pattern. + */ + return 1; +} /* dns_res_timeout() */ + + +time_t dns_res_elapsed(struct dns_resolver *R) { + return dns_elapsed(&R->elapsed); +} /* dns_res_elapsed() */ + + +int dns_res_poll(struct dns_resolver *R, int timeout) { + return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout); +} /* dns_res_poll() */ + + +int dns_res_submit2(struct dns_resolver *R, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass) { + dns_res_reset(R); + + /* Don't anchor; that can conflict with searchlist generation. */ + dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = qlen), 0); + + R->qtype = qtype; + R->qclass = qclass; + + dns_begin(&R->elapsed); + + dns_trace_res_submit(R->trace, R->qname, R->qtype, R->qclass, 0); + + return 0; +} /* dns_res_submit2() */ + + +int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) { + return dns_res_submit2(R, qname, strlen(qname), qtype, qclass); +} /* dns_res_submit() */ + + +int dns_res_check(struct dns_resolver *R) { + int error; + + if (R->stack[0].state != DNS_R_DONE) { + if ((error = dns_res_exec(R))) + return error; + } + + return 0; +} /* dns_res_check() */ + + +struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *_error) { + struct dns_packet *P = NULL; + int error; + + if (R->stack[0].state != DNS_R_DONE) { + error = DNS_EUNKNOWN; + goto error; + } + + if (!dns_p_movptr(&P, &R->stack[0].answer)) { + error = DNS_EFETCHED; + goto error; + } + + dns_trace_res_fetch(R->trace, P, 0); + + return P; +error: + *_error = error; + dns_trace_res_fetch(R->trace, NULL, error); + return NULL; +} /* dns_res_fetch() */ + + +static struct dns_packet *dns_res_fetch_and_study(struct dns_resolver *R, int *_error) { + struct dns_packet *P = NULL; + int error; + + if (!(P = dns_res_fetch(R, &error))) + goto error; + if ((error = dns_p_study(P))) + goto error; + + return P; +error: + *_error = error; + + dns_p_free(P); + + return NULL; +} /* dns_res_fetch_and_study() */ + + +struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) { + int error; + + if ((error = dns_res_submit(res, qname, qtype, qclass))) + goto error; + + while ((error = dns_res_check(res))) { + if (dns_res_elapsed(res) > timeout) + error = DNS_ETIMEDOUT; + + if (error != DNS_EAGAIN) + goto error; + + if ((error = dns_res_poll(res, 1))) + goto error; + } + + return dns_res_fetch(res, error_); +error: + *error_ = error; + + return 0; +} /* dns_res_query() */ + + +const struct dns_stat *dns_res_stat(struct dns_resolver *res) { + return dns_so_stat(&res->so); +} /* dns_res_stat() */ + + +void dns_res_sethints(struct dns_resolver *res, struct dns_hints *hints) { + dns_hints_acquire(hints); /* acquire first in case same hints object */ + dns_hints_close(res->hints); + res->hints = hints; +} /* dns_res_sethints() */ + + +struct dns_trace *dns_res_trace(struct dns_resolver *res) { + return res->trace; +} /* dns_res_trace() */ + + +void dns_res_settrace(struct dns_resolver *res, struct dns_trace *trace) { + struct dns_trace *otrace = res->trace; + res->trace = dns_trace_acquire_p(trace); + dns_trace_close(otrace); + dns_so_settrace(&res->so, trace); +} /* dns_res_settrace() */ + + +/* + * A D D R I N F O R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_addrinfo { + struct addrinfo hints; + struct dns_resolver *res; + struct dns_trace *trace; + + char qname[DNS_D_MAXNAME + 1]; + enum dns_type qtype; + unsigned short qport, port; + + struct { + unsigned long todo; + int state; + int atype; + enum dns_type qtype; + } af; + + struct dns_packet *answer; + struct dns_packet *glue; + + struct dns_rr_i i, g; + struct dns_rr rr; + + char cname[DNS_D_MAXNAME + 1]; + char i_cname[DNS_D_MAXNAME + 1], g_cname[DNS_D_MAXNAME + 1]; + + int g_depth; + + int state; + int found; + + struct dns_stat st; +}; /* struct dns_addrinfo */ + + +#define DNS_AI_AFMAX 32 +#define DNS_AI_AF2INDEX(af) (1UL << ((af) - 1)) + +static inline unsigned long dns_ai_af2index(int af) { + dns_static_assert(dns_same_type(unsigned long, DNS_AI_AF2INDEX(1), 1), "internal type mismatch"); + dns_static_assert(dns_same_type(unsigned long, ((struct dns_addrinfo *)0)->af.todo, 1), "internal type mismatch"); + + return (af > 0 && af <= DNS_AI_AFMAX)? DNS_AI_AF2INDEX(af) : 0; +} + +static int dns_ai_setaf(struct dns_addrinfo *ai, int af, int qtype) { + ai->af.atype = af; + ai->af.qtype = qtype; + + ai->af.todo &= ~dns_ai_af2index(af); + + return af; +} /* dns_ai_setaf() */ + +#define DNS_SM_RESTORE \ + do { pc = 0xff & (ai->af.state >> 0); i = 0xff & (ai->af.state >> 8); } while (0) +#define DNS_SM_SAVE \ + do { ai->af.state = ((0xff & pc) << 0) | ((0xff & i) << 8); } while (0) + +static int dns_ai_nextaf(struct dns_addrinfo *ai) { + int i, pc; + + dns_static_assert(AF_UNSPEC == 0, "AF_UNSPEC constant not 0"); + dns_static_assert(AF_INET <= DNS_AI_AFMAX, "AF_INET constant too large"); + dns_static_assert(AF_INET6 <= DNS_AI_AFMAX, "AF_INET6 constant too large"); + + DNS_SM_ENTER; + + if (ai->res) { + /* + * NB: On OpenBSD, at least, the types of entries resolved + * is the intersection of the /etc/resolv.conf families and + * the families permitted by the .ai_type hint. So if + * /etc/resolv.conf has "family inet4" and .ai_type + * is AF_INET6, then the address ::1 will return 0 entries + * even if AI_NUMERICHOST is specified in .ai_flags. + */ + while (i < (int)lengthof(ai->res->resconf->family)) { + int af = ai->res->resconf->family[i++]; + + if (af == AF_UNSPEC) { + DNS_SM_EXIT; + } else if (af < 0 || af > DNS_AI_AFMAX) { + continue; + } else if (!(DNS_AI_AF2INDEX(af) & ai->af.todo)) { + continue; + } else if (af == AF_INET) { + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); + } else if (af == AF_INET6) { + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); + } + } + } else { + /* + * NB: If we get here than AI_NUMERICFLAGS should be set and + * order shouldn't matter. + */ + if (DNS_AI_AF2INDEX(AF_INET) & ai->af.todo) + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); + if (DNS_AI_AF2INDEX(AF_INET6) & ai->af.todo) + DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); + } + + DNS_SM_LEAVE; + + return dns_ai_setaf(ai, AF_UNSPEC, 0); +} /* dns_ai_nextaf() */ + +#undef DNS_SM_RESTORE +#undef DNS_SM_SAVE + +static enum dns_type dns_ai_qtype(struct dns_addrinfo *ai) { + return (ai->qtype)? ai->qtype : ai->af.qtype; +} /* dns_ai_qtype() */ + +/* JW: This is not defined on mingw. */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif + +static dns_error_t dns_ai_parseport(unsigned short *port, const char *serv, const struct addrinfo *hints) { + const char *cp = serv; + unsigned long n = 0; + + while (*cp >= '0' && *cp <= '9' && n < 65536) { + n *= 10; + n += *cp++ - '0'; + } + + if (*cp == '\0') { + if (cp == serv || n >= 65536) + return DNS_ESERVICE; + + *port = n; + + return 0; + } + + if (hints->ai_flags & AI_NUMERICSERV) + return DNS_ESERVICE; + + /* TODO: try getaddrinfo(NULL, serv, { .ai_flags = AI_NUMERICSERV }) */ + + return DNS_ESERVICE; +} /* dns_ai_parseport() */ + + +struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *_error) { + static const struct dns_addrinfo ai_initializer; + struct dns_addrinfo *ai; + int error; + + if (res) { + dns_res_acquire(res); + } else if (!(hints->ai_flags & AI_NUMERICHOST)) { + /* + * NOTE: it's assumed that *_error is set from a previous + * API function call, such as dns_res_stub(). Should change + * this semantic, but it's applied elsewhere, too. + */ + if (!*_error) + *_error = EINVAL; + return NULL; + } + + if (!(ai = malloc(sizeof *ai))) + goto syerr; + + *ai = ai_initializer; + ai->hints = *hints; + + ai->res = res; + res = NULL; + + if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname)) + { error = ENAMETOOLONG; goto error; } + + ai->qtype = qtype; + ai->qport = 0; + + if (serv && (error = dns_ai_parseport(&ai->qport, serv, hints))) + goto error; + ai->port = ai->qport; + + /* + * FIXME: If an explicit A or AAAA record type conflicts with + * .ai_family or with resconf.family (i.e. AAAA specified but + * AF_INET6 not in interection of .ai_family and resconf.family), + * then what? + */ + switch (ai->qtype) { + case DNS_T_A: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET); + break; + case DNS_T_AAAA: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); + break; + default: /* 0, MX, SRV, etc */ + switch (ai->hints.ai_family) { + case AF_UNSPEC: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET) | DNS_AI_AF2INDEX(AF_INET6); + break; + case AF_INET: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET); + break; + case AF_INET6: + ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); + break; + default: + break; + } + } + + return ai; +syerr: + error = dns_syerr(); +error: + *_error = error; + + dns_ai_close(ai); + dns_res_close(res); + + return NULL; +} /* dns_ai_open() */ + + +void dns_ai_close(struct dns_addrinfo *ai) { + if (!ai) + return; + + dns_res_close(ai->res); + dns_trace_close(ai->trace); + + if (ai->answer != ai->glue) + dns_p_free(ai->glue); + + dns_p_free(ai->answer); + free(ai); +} /* dns_ai_close() */ + + +static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) { + struct sockaddr *saddr; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + const char *cname; + size_t clen; + + switch (type) { + case DNS_T_A: + saddr = memset(&sin, '\0', sizeof sin); + + sin.sin_family = AF_INET; + sin.sin_port = htons(ai->port); + + memcpy(&sin.sin_addr, any, sizeof sin.sin_addr); + + break; + case DNS_T_AAAA: + saddr = memset(&sin6, '\0', sizeof sin6); + + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(ai->port); + + memcpy(&sin6.sin6_addr, any, sizeof sin6.sin6_addr); + + break; + default: + return EINVAL; + } /* switch() */ + + if (ai->hints.ai_flags & AI_CANONNAME) { + cname = (*ai->cname)? ai->cname : ai->qname; + clen = strlen(cname); + } else { + cname = NULL; + clen = 0; + } + + if (!(*ent = malloc(sizeof **ent + dns_sa_len(saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0)))) + return dns_syerr(); + + memset(*ent, '\0', sizeof **ent); + + (*ent)->ai_family = saddr->sa_family; + (*ent)->ai_socktype = ai->hints.ai_socktype; + (*ent)->ai_protocol = ai->hints.ai_protocol; + + (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, saddr, dns_sa_len(saddr)); + (*ent)->ai_addrlen = dns_sa_len(saddr); + + if (ai->hints.ai_flags & AI_CANONNAME) + (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(saddr), cname, clen + 1); + + ai->found++; + + return 0; +} /* dns_ai_setent() */ + + +enum dns_ai_state { + DNS_AI_S_INIT, + DNS_AI_S_NEXTAF, + DNS_AI_S_NUMERIC, + DNS_AI_S_SUBMIT, + DNS_AI_S_CHECK, + DNS_AI_S_FETCH, + DNS_AI_S_FOREACH_I, + DNS_AI_S_INIT_G, + DNS_AI_S_ITERATE_G, + DNS_AI_S_FOREACH_G, + DNS_AI_S_SUBMIT_G, + DNS_AI_S_CHECK_G, + DNS_AI_S_FETCH_G, + DNS_AI_S_DONE, +}; /* enum dns_ai_state */ + +#define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0) + +int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) { + struct dns_packet *ans, *glue; + struct dns_rr rr; + char qname[DNS_D_MAXNAME + 1]; + union dns_any any; + size_t qlen, clen; + int error; + + *ent = 0; + +exec: + + switch (ai->state) { + case DNS_AI_S_INIT: + ai->state++; + case DNS_AI_S_NEXTAF: + if (!dns_ai_nextaf(ai)) + dns_ai_goto(DNS_AI_S_DONE); + + ai->state++; + case DNS_AI_S_NUMERIC: + if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) { + if (ai->af.atype == AF_INET) { + ai->state = DNS_AI_S_NEXTAF; + return dns_ai_setent(ent, &any, DNS_T_A, ai); + } else { + dns_ai_goto(DNS_AI_S_NEXTAF); + } + } + + if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) { + if (ai->af.atype == AF_INET6) { + ai->state = DNS_AI_S_NEXTAF; + return dns_ai_setent(ent, &any, DNS_T_AAAA, ai); + } else { + dns_ai_goto(DNS_AI_S_NEXTAF); + } + } + + if (ai->hints.ai_flags & AI_NUMERICHOST) + dns_ai_goto(DNS_AI_S_NEXTAF); + + ai->state++; + case DNS_AI_S_SUBMIT: + assert(ai->res); + + if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN))) + return error; + + ai->state++; + case DNS_AI_S_CHECK: + if ((error = dns_res_check(ai->res))) + return error; + + ai->state++; + case DNS_AI_S_FETCH: + if (!(ans = dns_res_fetch_and_study(ai->res, &error))) + return error; + if (ai->glue != ai->answer) + dns_p_free(ai->glue); + ai->glue = dns_p_movptr(&ai->answer, &ans); + + /* Search generator may have changed the qname. */ + if (!(qlen = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error))) + return error; + else if (qlen >= sizeof qname) + return DNS_EILLEGAL; + if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, qlen, ai->answer, &error)) + return error; + + dns_strlcpy(ai->i_cname, ai->cname, sizeof ai->i_cname); + dns_rr_i_init(&ai->i, ai->answer); + ai->i.section = DNS_S_AN; + ai->i.name = ai->i_cname; + ai->i.type = dns_ai_qtype(ai); + ai->i.sort = &dns_rr_i_order; + + ai->state++; + case DNS_AI_S_FOREACH_I: + if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error)) + dns_ai_goto(DNS_AI_S_NEXTAF); + + if ((error = dns_any_parse(&any, &rr, ai->answer))) + return error; + + ai->port = ai->qport; + + switch (rr.type) { + case DNS_T_A: + case DNS_T_AAAA: + return dns_ai_setent(ent, &any, rr.type, ai); + default: + if (!(clen = dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type))) + dns_ai_goto(DNS_AI_S_FOREACH_I); + + /* + * Find the "real" canonical name. Some authorities + * publish aliases where an RFC defines a canonical + * name. We trust that the resolver followed any + * CNAME chains on it's own, regardless of whether + * the "smart" option is enabled. + */ + if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, clen, ai->answer, &error)) + return error; + + if (rr.type == DNS_T_SRV) + ai->port = any.srv.port; + + break; + } /* switch() */ + + ai->state++; + case DNS_AI_S_INIT_G: + ai->g_depth = 0; + + ai->state++; + case DNS_AI_S_ITERATE_G: + dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname); + dns_rr_i_init(&ai->g, ai->glue); + ai->g.section = DNS_S_ALL & ~DNS_S_QD; + ai->g.name = ai->g_cname; + ai->g.type = ai->af.qtype; + + ai->state++; + case DNS_AI_S_FOREACH_G: + if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) { + if (dns_rr_i_count(&ai->g) > 0) + dns_ai_goto(DNS_AI_S_FOREACH_I); + else + dns_ai_goto(DNS_AI_S_SUBMIT_G); + } + + if ((error = dns_any_parse(&any, &rr, ai->glue))) + return error; + + return dns_ai_setent(ent, &any, rr.type, ai); + case DNS_AI_S_SUBMIT_G: + /* skip if already queried */ + if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error)) + dns_ai_goto(DNS_AI_S_FOREACH_I); + /* skip if we recursed (CNAME chains should have been handled in the resolver) */ + if (++ai->g_depth > 1) + dns_ai_goto(DNS_AI_S_FOREACH_I); + + if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN))) + return error; + + ai->state++; + case DNS_AI_S_CHECK_G: + if ((error = dns_res_check(ai->res))) + return error; + + ai->state++; + case DNS_AI_S_FETCH_G: + if (!(ans = dns_res_fetch_and_study(ai->res, &error))) + return error; + + glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error); + dns_p_setptr(&ans, NULL); + if (!glue) + return error; + + if (ai->glue != ai->answer) + dns_p_free(ai->glue); + ai->glue = glue; + + if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->g.name, strlen(ai->g.name), ai->glue, &error)) + dns_ai_goto(DNS_AI_S_FOREACH_I); + + dns_ai_goto(DNS_AI_S_ITERATE_G); + case DNS_AI_S_DONE: + if (ai->found) { + return ENOENT; /* TODO: Just return 0 */ + } else if (ai->answer) { + switch (dns_p_rcode(ai->answer)) { + case DNS_RC_NOERROR: + /* FALL THROUGH */ + case DNS_RC_NXDOMAIN: + return DNS_ENONAME; + default: + return DNS_EFAIL; + } + } else { + return DNS_EFAIL; + } + default: + return EINVAL; + } /* switch() */ +} /* dns_ai_nextent() */ + + +time_t dns_ai_elapsed(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_elapsed(ai->res) : 0; +} /* dns_ai_elapsed() */ + + +void dns_ai_clear(struct dns_addrinfo *ai) { + if (ai->res) + dns_res_clear(ai->res); +} /* dns_ai_clear() */ + + +int dns_ai_events(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_events(ai->res) : 0; +} /* dns_ai_events() */ + + +int dns_ai_pollfd(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_pollfd(ai->res) : -1; +} /* dns_ai_pollfd() */ + + +time_t dns_ai_timeout(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_timeout(ai->res) : 0; +} /* dns_ai_timeout() */ + + +int dns_ai_poll(struct dns_addrinfo *ai, int timeout) { + return (ai->res)? dns_res_poll(ai->res, timeout) : 0; +} /* dns_ai_poll() */ + + +size_t dns_ai_print(void *_dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + char addr[DNS_PP_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; + + dns_b_puts(&dst, "[ "); + dns_b_puts(&dst, ai->qname); + dns_b_puts(&dst, " IN "); + if (ai->qtype) { + dns_b_puts(&dst, dns_strtype(ai->qtype)); + } else if (ent->ai_family == AF_INET) { + dns_b_puts(&dst, dns_strtype(DNS_T_A)); + } else if (ent->ai_family == AF_INET6) { + dns_b_puts(&dst, dns_strtype(DNS_T_AAAA)); + } else { + dns_b_puts(&dst, "0"); + } + dns_b_puts(&dst, " ]\n"); + + dns_b_puts(&dst, ".ai_family = "); + switch (ent->ai_family) { + case AF_INET: + dns_b_puts(&dst, "AF_INET"); + break; + case AF_INET6: + dns_b_puts(&dst, "AF_INET6"); + break; + default: + dns_b_fmtju(&dst, ent->ai_family, 0); + break; + } + dns_b_putc(&dst, '\n'); + + dns_b_puts(&dst, ".ai_socktype = "); + switch (ent->ai_socktype) { + case SOCK_STREAM: + dns_b_puts(&dst, "SOCK_STREAM"); + break; + case SOCK_DGRAM: + dns_b_puts(&dst, "SOCK_DGRAM"); + break; + default: + dns_b_fmtju(&dst, ent->ai_socktype, 0); + break; + } + dns_b_putc(&dst, '\n'); + + dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr, NULL), addr, sizeof addr); + dns_b_puts(&dst, ".ai_addr = ["); + dns_b_puts(&dst, addr); + dns_b_puts(&dst, "]:"); + dns_b_fmtju(&dst, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0); + dns_b_putc(&dst, '\n'); + + dns_b_puts(&dst, ".ai_canonname = "); + dns_b_puts(&dst, (ent->ai_canonname)? ent->ai_canonname : "[NULL]"); + dns_b_putc(&dst, '\n'); + + return dns_b_strllen(&dst); +} /* dns_ai_print() */ + + +const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) { + return (ai->res)? dns_res_stat(ai->res) : &ai->st; +} /* dns_ai_stat() */ + + +struct dns_trace *dns_ai_trace(struct dns_addrinfo *ai) { + return ai->trace; +} /* dns_ai_trace() */ + + +void dns_ai_settrace(struct dns_addrinfo *ai, struct dns_trace *trace) { + struct dns_trace *otrace = ai->trace; + ai->trace = dns_trace_acquire_p(trace); + dns_trace_close(otrace); + if (ai->res) + dns_res_settrace(ai->res, trace); +} /* dns_ai_settrace() */ + + +/* + * M I S C E L L A N E O U S R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static const struct { + char name[16]; + enum dns_section type; +} dns_sections[] = { + { "QUESTION", DNS_S_QUESTION }, + { "QD", DNS_S_QUESTION }, + { "ANSWER", DNS_S_ANSWER }, + { "AN", DNS_S_ANSWER }, + { "AUTHORITY", DNS_S_AUTHORITY }, + { "NS", DNS_S_AUTHORITY }, + { "ADDITIONAL", DNS_S_ADDITIONAL }, + { "AR", DNS_S_ADDITIONAL }, +}; + +const char *(dns_strsection)(enum dns_section section, void *_dst, size_t lim) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + for (i = 0; i < lengthof(dns_sections); i++) { + if (dns_sections[i].type & section) { + dns_b_puts(&dst, dns_sections[i].name); + section &= ~dns_sections[i].type; + if (section) + dns_b_putc(&dst, '|'); + } + } + + if (section || dst.p == dst.base) + dns_b_fmtju(&dst, (0xffff & section), 0); + + return dns_b_tostring(&dst); +} /* dns_strsection() */ + + +enum dns_section dns_isection(const char *src) { + enum dns_section section = 0; + char sbuf[128]; + char *name, *next; + unsigned i; + + dns_strlcpy(sbuf, src, sizeof sbuf); + next = sbuf; + + while ((name = dns_strsep(&next, "|+, \t"))) { + for (i = 0; i < lengthof(dns_sections); i++) { + if (!strcasecmp(dns_sections[i].name, name)) { + section |= dns_sections[i].type; + break; + } + } + } + + return section; +} /* dns_isection() */ + + +static const struct { + char name[8]; + enum dns_class type; +} dns_classes[] = { + { "IN", DNS_C_IN }, +}; + +const char *(dns_strclass)(enum dns_class type, void *_dst, size_t lim) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + for (i = 0; i < lengthof(dns_classes); i++) { + if (dns_classes[i].type == type) { + dns_b_puts(&dst, dns_classes[i].name); + break; + } + } + + if (dst.p == dst.base) + dns_b_fmtju(&dst, (0xffff & type), 0); + + return dns_b_tostring(&dst); +} /* dns_strclass() */ + + +enum dns_class dns_iclass(const char *name) { + unsigned i, class; + + for (i = 0; i < lengthof(dns_classes); i++) { + if (!strcasecmp(dns_classes[i].name, name)) + return dns_classes[i].type; + } + + class = 0; + while (dns_isdigit(*name)) { + class *= 10; + class += *name++ - '0'; + } + + return DNS_PP_MIN(class, 0xffff); +} /* dns_iclass() */ + + +const char *(dns_strtype)(enum dns_type type, void *_dst, size_t lim) { + struct dns_buf dst = DNS_B_INTO(_dst, lim); + unsigned i; + + for (i = 0; i < lengthof(dns_rrtypes); i++) { + if (dns_rrtypes[i].type == type) { + dns_b_puts(&dst, dns_rrtypes[i].name); + break; + } + } + + if (dst.p == dst.base) + dns_b_fmtju(&dst, (0xffff & type), 0); + + return dns_b_tostring(&dst); +} /* dns_strtype() */ + + +enum dns_type dns_itype(const char *name) { + unsigned i, type; + + for (i = 0; i < lengthof(dns_rrtypes); i++) { + if (!strcasecmp(dns_rrtypes[i].name, name)) + return dns_rrtypes[i].type; + } + + type = 0; + while (dns_isdigit(*name)) { + type *= 10; + type += *name++ - '0'; + } + + return DNS_PP_MIN(type, 0xffff); +} /* dns_itype() */ + + +static char dns_opcodes[16][16] = { + [DNS_OP_QUERY] = "QUERY", + [DNS_OP_IQUERY] = "IQUERY", + [DNS_OP_STATUS] = "STATUS", + [DNS_OP_NOTIFY] = "NOTIFY", + [DNS_OP_UPDATE] = "UPDATE", +}; + +static const char *dns__strcode(int code, volatile char *dst, size_t lim) { + char _tmp[48] = ""; + struct dns_buf tmp; + size_t p; + + assert(lim > 0); + dns_b_fmtju(dns_b_into(&tmp, _tmp, DNS_PP_MIN(sizeof _tmp, lim - 1)), code, 0); + + /* copy downwards so first byte is copied last (see below) */ + p = (size_t)(tmp.p - tmp.base); + dst[p] = '\0'; + while (p--) + dst[p] = _tmp[p]; + + return (const char *)dst; +} + +const char *dns_stropcode(enum dns_opcode opcode) { + opcode = (unsigned)opcode % lengthof(dns_opcodes); + + if ('\0' == dns_opcodes[opcode][0]) + return dns__strcode(opcode, dns_opcodes[opcode], sizeof dns_opcodes[opcode]); + + return dns_opcodes[opcode]; +} /* dns_stropcode() */ + + +enum dns_opcode dns_iopcode(const char *name) { + unsigned opcode; + + for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) { + if (!strcasecmp(name, dns_opcodes[opcode])) + return opcode; + } + + opcode = 0; + while (dns_isdigit(*name)) { + opcode *= 10; + opcode += *name++ - '0'; + } + + return DNS_PP_MIN(opcode, 0x0f); +} /* dns_iopcode() */ + + +static char dns_rcodes[32][16] = { + [DNS_RC_NOERROR] = "NOERROR", + [DNS_RC_FORMERR] = "FORMERR", + [DNS_RC_SERVFAIL] = "SERVFAIL", + [DNS_RC_NXDOMAIN] = "NXDOMAIN", + [DNS_RC_NOTIMP] = "NOTIMP", + [DNS_RC_REFUSED] = "REFUSED", + [DNS_RC_YXDOMAIN] = "YXDOMAIN", + [DNS_RC_YXRRSET] = "YXRRSET", + [DNS_RC_NXRRSET] = "NXRRSET", + [DNS_RC_NOTAUTH] = "NOTAUTH", + [DNS_RC_NOTZONE] = "NOTZONE", + /* EDNS(0) extended RCODEs ... */ + [DNS_RC_BADVERS] = "BADVERS", +}; + +const char *dns_strrcode(enum dns_rcode rcode) { + rcode = (unsigned)rcode % lengthof(dns_rcodes); + + if ('\0' == dns_rcodes[rcode][0]) + return dns__strcode(rcode, dns_rcodes[rcode], sizeof dns_rcodes[rcode]); + + return dns_rcodes[rcode]; +} /* dns_strrcode() */ + + +enum dns_rcode dns_ircode(const char *name) { + unsigned rcode; + + for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) { + if (!strcasecmp(name, dns_rcodes[rcode])) + return rcode; + } + + rcode = 0; + while (dns_isdigit(*name)) { + rcode *= 10; + rcode += *name++ - '0'; + } + + return DNS_PP_MIN(rcode, 0xfff); +} /* dns_ircode() */ + + + +/* + * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if DNS_MAIN + +#include +#include +#include + +#include + +#if _WIN32 +#include +#endif + +#if !_WIN32 +#include +#endif + + +struct { + struct { + const char *path[8]; + unsigned count; + } resconf, nssconf, hosts, cache; + + const char *qname; + enum dns_type qtype; + + int (*sort)(); + + const char *trace; + + int verbose; + + struct { + struct dns_resolv_conf *resconf; + struct dns_hosts *hosts; + struct dns_trace *trace; + } memo; + + struct sockaddr_storage socks_host; + const char *socks_user; + const char *socks_password; +} MAIN = { + .sort = &dns_rr_i_packet, +}; + + +static void hexdump(const unsigned char *src, size_t len, FILE *fp) { + struct dns_hxd_lines_i lines = { 0 }; + char line[128]; + + while (dns_hxd_lines(line, sizeof line, src, len, &lines)) { + fputs(line, fp); + } +} /* hexdump() */ + + +DNS_NORETURN static void panic(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + +#if _WIN32 + vfprintf(stderr, fmt, ap); + + exit(EXIT_FAILURE); +#else + verrx(EXIT_FAILURE, fmt, ap); +#endif +} /* panic() */ + +#define panic_(fn, ln, fmt, ...) \ + panic(fmt "%0s", (fn), (ln), __VA_ARGS__) +#define panic(...) \ + panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "") + + +static void *grow(unsigned char *p, size_t size) { + void *tmp; + + if (!(tmp = realloc(p, size))) + panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno)); + + return tmp; +} /* grow() */ + + +static size_t add(size_t a, size_t b) { + if (~a < b) + panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b); + + return a + b; +} /* add() */ + + +static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) { + size_t size = add(osize, len); + + *dst = grow(*dst, size); + memcpy(*dst + osize, src, len); + + return size; +} /* append() */ + + +static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) { + size_t size = osize; + unsigned char buf[1024]; + size_t count; + + while ((count = fread(buf, 1, sizeof buf, fp))) + size = append(dst, size, buf, count); + + if (ferror(fp)) + panic("%s: %s", path, dns_strerror(errno)); + + return size; +} /* slurp() */ + + +static struct dns_resolv_conf *resconf(void) { + struct dns_resolv_conf **resconf = &MAIN.memo.resconf; + const char *path; + unsigned i; + int error; + + if (*resconf) + return *resconf; + + if (!(*resconf = dns_resconf_open(&error))) + panic("dns_resconf_open: %s", dns_strerror(error)); + + if (!MAIN.resconf.count) + MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf"; + + for (i = 0; i < MAIN.resconf.count; i++) { + path = MAIN.resconf.path[i]; + + if (0 == strcmp(path, "-")) + error = dns_resconf_loadfile(*resconf, stdin); + else + error = dns_resconf_loadpath(*resconf, path); + + if (error) + panic("%s: %s", path, dns_strerror(error)); + } + + for (i = 0; i < MAIN.nssconf.count; i++) { + path = MAIN.nssconf.path[i]; + + if (0 == strcmp(path, "-")) + error = dns_nssconf_loadfile(*resconf, stdin); + else + error = dns_nssconf_loadpath(*resconf, path); + + if (error) + panic("%s: %s", path, dns_strerror(error)); + } + + if (!MAIN.nssconf.count) { + path = "/etc/nsswitch.conf"; + + if (!(error = dns_nssconf_loadpath(*resconf, path))) + MAIN.nssconf.path[MAIN.nssconf.count++] = path; + else if (error != ENOENT) + panic("%s: %s", path, dns_strerror(error)); + } + + return *resconf; +} /* resconf() */ + + +static struct dns_hosts *hosts(void) { + struct dns_hosts **hosts = &MAIN.memo.hosts; + const char *path; + unsigned i; + int error; + + if (*hosts) + return *hosts; + + if (!MAIN.hosts.count) { + MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts"; + + /* Explicitly test dns_hosts_local() */ + if (!(*hosts = dns_hosts_local(&error))) + panic("%s: %s", "/etc/hosts", dns_strerror(error)); + + return *hosts; + } + + if (!(*hosts = dns_hosts_open(&error))) + panic("dns_hosts_open: %s", dns_strerror(error)); + + for (i = 0; i < MAIN.hosts.count; i++) { + path = MAIN.hosts.path[i]; + + if (0 == strcmp(path, "-")) + error = dns_hosts_loadfile(*hosts, stdin); + else + error = dns_hosts_loadpath(*hosts, path); + + if (error) + panic("%s: %s", path, dns_strerror(error)); + } + + return *hosts; +} /* hosts() */ + + +#if DNS_CACHE +#include "cache.h" + +static struct dns_cache *cache(void) { + static struct cache *cache; + const char *path; + unsigned i; + int error; + + if (cache) + return cache_resi(cache); + if (!MAIN.cache.count) + return NULL; + + if (!(cache = cache_open(&error))) + panic("%s: %s", MAIN.cache.path[0], dns_strerror(error)); + + for (i = 0; i < MAIN.cache.count; i++) { + path = MAIN.cache.path[i]; + + if (!strcmp(path, "-")) { + if ((error = cache_loadfile(cache, stdin, NULL, 0))) + panic("%s: %s", path, dns_strerror(error)); + } else if ((error = cache_loadpath(cache, path, NULL, 0))) + panic("%s: %s", path, dns_strerror(error)); + } + + return cache_resi(cache); +} /* cache() */ +#else +static struct dns_cache *cache(void) { return NULL; } +#endif + + +static struct dns_trace *trace(const char *mode) { + static char omode[64] = ""; + struct dns_trace **trace = &MAIN.memo.trace; + FILE *fp; + int error; + + if (*trace && 0 == strcmp(omode, mode)) + return *trace; + if (!MAIN.trace) + return NULL; + + if (!(fp = fopen(MAIN.trace, mode))) + panic("%s: %s", MAIN.trace, strerror(errno)); + dns_trace_close(*trace); + if (!(*trace = dns_trace_open(fp, &error))) + panic("%s: %s", MAIN.trace, dns_strerror(error)); + dns_strlcpy(omode, mode, sizeof omode); + + return *trace; +} + + +static void print_packet(struct dns_packet *P, FILE *fp) { + dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp); + + if (MAIN.verbose > 2) + hexdump(P->data, P->end, fp); +} /* print_packet() */ + + +static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + struct dns_packet *P = dns_p_new(512); + struct dns_packet *Q = dns_p_new(512); + enum dns_section section; + struct dns_rr rr; + int error; + union dns_any any; + char pretty[sizeof any * 2]; + size_t len; + + P->end = fread(P->data, 1, P->size, stdin); + + fputs(";; [HEADER]\n", stdout); + fprintf(stdout, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); + fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); + fprintf(stdout, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); + fprintf(stdout, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); + fprintf(stdout, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); + fprintf(stdout, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); + fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); + + section = 0; + + dns_rr_foreach(&rr, P, .sort = MAIN.sort) { + if (section != rr.section) + fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); + + if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) + fprintf(stdout, "%s\n", pretty); + + dns_rr_copy(Q, &rr, P); + + section = rr.section; + } + + fputs("; ; ; ; ; ; ; ;\n\n", stdout); + + section = 0; + +#if 0 + dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") { +#else + struct dns_rr rrset[32]; + struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort); + unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); + + for (unsigned i = 0; i < rrcount; i++) { + rr = rrset[i]; +#endif + if (section != rr.section) + fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section)); + + if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error))) + fprintf(stdout, "%s\n", pretty); + + section = rr.section; + } + + if (MAIN.verbose > 1) { + fprintf(stderr, "orig:%"PRIuZ"\n", P->end); + hexdump(P->data, P->end, stdout); + + fprintf(stderr, "copy:%"PRIuZ"\n", Q->end); + hexdump(Q->data, Q->end, stdout); + } + + return 0; +} /* parse_packet() */ + + +static int parse_domain(int argc, char *argv[]) { + char *dn; + + dn = (argc > 1)? argv[1] : "f.l.google.com"; + + printf("[%s]\n", dn); + + dn = dns_d_new(dn); + + do { + puts(dn); + } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn))); + + return 0; +} /* parse_domain() */ + + +static int trim_domain(int argc, char **argv) { + for (argc--, argv++; argc > 0; argc--, argv++) { + char name[DNS_D_MAXNAME + 1]; + + dns_d_trim(name, sizeof name, *argv, strlen(*argv), DNS_D_ANCHOR); + + puts(name); + } + + return 0; +} /* trim_domain() */ + + +static int expand_domain(int argc, char *argv[]) { + unsigned short rp = 0; + unsigned char *src = NULL; + unsigned char *dst; + struct dns_packet *pkt; + size_t lim = 0, len; + int error; + + if (argc > 1) + rp = atoi(argv[1]); + + len = slurp(&src, 0, stdin, "-"); + + if (!(pkt = dns_p_make(len, &error))) + panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error)); + + memcpy(pkt->data, src, len); + pkt->end = len; + + lim = 1; + dst = grow(NULL, lim); + + while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) { + lim = add(len, 1); + dst = grow(dst, lim); + } + + if (!len) + panic("expand: %s", dns_strerror(error)); + + fwrite(dst, 1, len, stdout); + fflush(stdout); + + free(src); + free(dst); + free(pkt); + + return 0; +} /* expand_domain() */ + + +static int show_resconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + unsigned i; + + resconf(); /* load it */ + + fputs("; SOURCES\n", stdout); + + for (i = 0; i < MAIN.resconf.count; i++) + fprintf(stdout, "; %s\n", MAIN.resconf.path[i]); + + for (i = 0; i < MAIN.nssconf.count; i++) + fprintf(stdout, "; %s\n", MAIN.nssconf.path[i]); + + fputs(";\n", stdout); + + dns_resconf_dump(resconf(), stdout); + + return 0; +} /* show_resconf() */ + + +static int show_nssconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + unsigned i; + + resconf(); + + fputs("# SOURCES\n", stdout); + + for (i = 0; i < MAIN.resconf.count; i++) + fprintf(stdout, "# %s\n", MAIN.resconf.path[i]); + + for (i = 0; i < MAIN.nssconf.count; i++) + fprintf(stdout, "# %s\n", MAIN.nssconf.path[i]); + + fputs("#\n", stdout); + + dns_nssconf_dump(resconf(), stdout); + + return 0; +} /* show_nssconf() */ + + +static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + unsigned i; + + hosts(); + + fputs("# SOURCES\n", stdout); + + for (i = 0; i < MAIN.hosts.count; i++) + fprintf(stdout, "# %s\n", MAIN.hosts.path[i]); + + fputs("#\n", stdout); + + dns_hosts_dump(hosts(), stdout); + + return 0; +} /* show_hosts() */ + + +static int query_hosts(int argc, char *argv[]) { + struct dns_packet *Q = dns_p_new(512); + struct dns_packet *A; + char qname[DNS_D_MAXNAME + 1]; + size_t qlen; + int error; + + if (!MAIN.qname) + MAIN.qname = (argc > 1)? argv[1] : "localhost"; + if (!MAIN.qtype) + MAIN.qtype = DNS_T_A; + + hosts(); + + if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) { + union { struct in_addr a; struct in6_addr a6; } addr; + int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET; + + if ((error = dns_pton(af, MAIN.qname, &addr))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + qlen = dns_ptr_qname(qname, sizeof qname, af, &addr); + } else + qlen = dns_strlcpy(qname, MAIN.qname, sizeof qname); + + if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0))) + panic("%s: %s", qname, dns_strerror(error)); + + if (!(A = dns_hosts_query(hosts(), Q, &error))) + panic("%s: %s", qname, dns_strerror(error)); + + print_packet(A, stdout); + + free(A); + + return 0; +} /* query_hosts() */ + + +static int search_list(int argc, char *argv[]) { + const char *qname = (argc > 1)? argv[1] : "f.l.google.com"; + unsigned long i = 0; + char name[DNS_D_MAXNAME + 1]; + + printf("[%s]\n", qname); + + while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i)) + puts(name); + + return 0; +} /* search_list() */ + + +static int permute_set(int argc, char *argv[]) { + unsigned lo, hi, i; + struct dns_k_permutor p; + + hi = (--argc > 0)? atoi(argv[argc]) : 8; + lo = (--argc > 0)? atoi(argv[argc]) : 0; + + fprintf(stderr, "[%u .. %u]\n", lo, hi); + + dns_k_permutor_init(&p, lo, hi); + + for (i = lo; i <= hi; i++) + fprintf(stdout, "%u\n", dns_k_permutor_step(&p)); +// printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i))); + + return 0; +} /* permute_set() */ + + +static int shuffle_16(int argc, char *argv[]) { + unsigned n, r; + + if (--argc > 0) { + n = 0xffff & atoi(argv[argc]); + r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random(); + + fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); + } else { + r = dns_random(); + + for (n = 0; n < 65536; n++) + fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); + } + + return 0; +} /* shuffle_16() */ + + +static int dump_random(int argc, char *argv[]) { + unsigned char b[32]; + unsigned i, j, n, r; + + n = (argc > 1)? atoi(argv[1]) : 32; + + while (n) { + i = 0; + + do { + r = dns_random(); + + for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) { + b[i] = 0xff & r; + r >>= 8; + } + } while (i < n && i < sizeof b); + + hexdump(b, i, stdout); + + n -= i; + } + + return 0; +} /* dump_random() */ + + +static int send_query(int argc, char *argv[]) { + struct dns_packet *A, *Q = dns_p_new(512); + char host[INET6_ADDRSTRLEN + 1]; + struct sockaddr_storage ss; + struct dns_socket *so; + int error, type; + + if (argc > 1) { + ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET; + + if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss, NULL)))) + panic("%s: %s", argv[1], dns_strerror(error)); + + *dns_sa_port(ss.ss_family, &ss) = htons(53); + } else + memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0])); + + if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss, NULL), host, sizeof host)) + panic("bad host address, or none provided"); + + if (!MAIN.qname) + MAIN.qname = "ipv6.google.com"; + if (!MAIN.qtype) + MAIN.qtype = DNS_T_AAAA; + + if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0))) + panic("dns_p_push: %s", dns_strerror(error)); + + dns_header(Q)->rd = 1; + + if (strstr(argv[0], "udp")) + type = SOCK_DGRAM; + else if (strstr(argv[0], "tcp")) + type = SOCK_STREAM; + else + type = dns_res_tcp2type(resconf()->options.tcp); + + fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype)); + + if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error))) + panic("dns_so_open: %s", dns_strerror(error)); + + while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) { + if (error != DNS_EAGAIN) + panic("dns_so_query: %s (%d)", dns_strerror(error), error); + if (dns_so_elapsed(so) > 10) + panic("query timed-out"); + + dns_so_poll(so, 1); + } + + print_packet(A, stdout); + + dns_so_close(so); + + return 0; +} /* send_query() */ + + +static int print_arpa(int argc, char *argv[]) { + const char *ip = (argc > 1)? argv[1] : "::1"; + int af = (strchr(ip, ':'))? AF_INET6 : AF_INET; + union { struct in_addr a4; struct in6_addr a6; } addr; + char host[DNS_D_MAXNAME + 1]; + + if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr)) + panic("%s: invalid address", ip); + + fprintf(stdout, "%s\n", host); + + return 0; +} /* print_arpa() */ + + +static int show_hints(int argc, char *argv[]) { + struct dns_hints *(*load)(struct dns_resolv_conf *, int *); + const char *which, *how, *who; + struct dns_hints *hints; + int error; + + which = (argc > 1)? argv[1] : "local"; + how = (argc > 2)? argv[2] : "plain"; + who = (argc > 3)? argv[3] : "google.com"; + + load = (0 == strcmp(which, "local")) + ? &dns_hints_local + : &dns_hints_root; + + if (!(hints = load(resconf(), &error))) + panic("%s: %s", argv[0], dns_strerror(error)); + + if (0 == strcmp(how, "plain")) { + dns_hints_dump(hints, stdout); + } else { + struct dns_packet *query, *answer; + + query = dns_p_new(512); + + if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) + panic("%s: %s", who, dns_strerror(error)); + + if (!(answer = dns_hints_query(hints, query, &error))) + panic("%s: %s", who, dns_strerror(error)); + + print_packet(answer, stdout); + + free(answer); + } + + dns_hints_close(hints); + + return 0; +} /* show_hints() */ + + +static int resolve_query(int argc DNS_NOTUSED, char *argv[]) { + _Bool recurse = !!strstr(argv[0], "recurse"); + struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; + struct dns_resolver *R; + struct dns_packet *ans; + const struct dns_stat *st; + int error; + + if (!MAIN.qname) + MAIN.qname = "www.google.com"; + if (!MAIN.qtype) + MAIN.qtype = DNS_T_A; + + resconf()->options.recurse = recurse; + + if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), + dns_opts(.socks_host=&MAIN.socks_host, + .socks_user=MAIN.socks_user, + .socks_password=MAIN.socks_password), &error))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + dns_res_settrace(R, trace("w+b")); + + if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + while ((error = dns_res_check(R))) { + if (error != DNS_EAGAIN) + panic("dns_res_check: %s (%d)", dns_strerror(error), error); + if (dns_res_elapsed(R) > 30) + panic("query timed-out"); + + dns_res_poll(R, 1); + } + + ans = dns_res_fetch(R, &error); + print_packet(ans, stdout); + free(ans); + + st = dns_res_stat(R); + putchar('\n'); + printf(";; queries: %"PRIuZ"\n", st->queries); + printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes); + printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes); + printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes); + printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes); + + dns_res_close(R); + + return 0; +} /* resolve_query() */ + + +static int resolve_addrinfo(int argc DNS_NOTUSED, char *argv[]) { + _Bool recurse = !!strstr(argv[0], "recurse"); + struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; + struct dns_resolver *res = NULL; + struct dns_addrinfo *ai = NULL; + struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME }; + struct addrinfo *ent; + char pretty[512]; + int error; + + if (!MAIN.qname) + MAIN.qname = "www.google.com"; + /* NB: MAIN.qtype of 0 means obey hints.ai_family */ + + resconf()->options.recurse = recurse; + + if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error))) + panic("%s: %s", MAIN.qname, dns_strerror(error)); + + dns_ai_settrace(ai, trace("w+b")); + + do { + switch (error = dns_ai_nextent(&ent, ai)) { + case 0: + dns_ai_print(pretty, sizeof pretty, ent, ai); + + fputs(pretty, stdout); + + free(ent); + + break; + case ENOENT: + break; + case DNS_EAGAIN: + if (dns_ai_elapsed(ai) > 30) + panic("query timed-out"); + + dns_ai_poll(ai, 1); + + break; + default: + panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error); + } + } while (error != ENOENT); + + dns_res_close(res); + dns_ai_close(ai); + + return 0; +} /* resolve_addrinfo() */ + + +static int dump_trace(int argc DNS_NOTUSED, char *argv[]) { + int error; + + if (!MAIN.trace) + panic("no trace file specified"); + + if ((error = dns_trace_dump(trace("r"), stdout))) + panic("dump_trace: %s", dns_strerror(error)); + + return 0; +} /* dump_trace() */ + + +static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + union { + struct sockaddr sa; + struct sockaddr_in sin; + } port; + int fd; + + memset(&port, 0, sizeof port); + port.sin.sin_family = AF_INET; + port.sin.sin_port = htons(5354); + port.sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + + if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0))) + panic("socket: %s", strerror(errno)); + + if (0 != bind(fd, &port.sa, sizeof port.sa)) + panic("127.0.0.1:5353: %s", dns_strerror(errno)); + + for (;;) { + struct dns_packet *pkt = dns_p_new(512); + struct sockaddr_storage ss; + socklen_t slen = sizeof ss; + ssize_t count; +#if defined(MSG_WAITALL) /* MinGW issue */ + int rflags = MSG_WAITALL; +#else + int rflags = 0; +#endif + + count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen); + + if (!count || count < 0) + panic("recvfrom: %s", strerror(errno)); + + pkt->end = count; + + dns_p_dump(pkt, stdout); + + (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen); + } + + return 0; +} /* echo_port() */ + + +static int isection(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_isection(name); + name = dns_strsection(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* isection() */ + + +static int iclass(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_iclass(name); + name = dns_strclass(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* iclass() */ + + +static int itype(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_itype(name); + name = dns_strtype(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* itype() */ + + +static int iopcode(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_iopcode(name); + name = dns_stropcode(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* iopcode() */ + + +static int ircode(int argc, char *argv[]) { + const char *name = (argc > 1)? argv[1] : ""; + int type; + + type = dns_ircode(name); + name = dns_strrcode(type); + + printf("%s (%d)\n", name, type); + + return 0; +} /* ircode() */ + + +#define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) } +#define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__) +#define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__) +#define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__) +#define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +static int sizes(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { + static const struct { const char *name; size_t size; } type[] = { + SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i), + SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns), + SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv), + SIZE(struct dns_sshfp, struct dns_txt, union dns_any), + SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i), + SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo), + SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long) + }; + unsigned i, max; + + for (i = 0, max = 0; i < lengthof(type); i++) + max = DNS_PP_MAX(max, strlen(type[i].name)); + + for (i = 0; i < lengthof(type); i++) + printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size); + + return 0; +} /* sizes() */ + + +static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = { + { "parse-packet", &parse_packet, "parse binary packet from stdin" }, + { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" }, + { "trim-domain", &trim_domain, "trim and anchor domain name" }, + { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" }, + { "show-resconf", &show_resconf, "show resolv.conf data" }, + { "show-hosts", &show_hosts, "show hosts data" }, + { "show-nssconf", &show_nssconf, "show nsswitch.conf data" }, + { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" }, + { "search-list", &search_list, "generate query search list from domain" }, + { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" }, + { "shuffle-16", &shuffle_16, "simple 16-bit permutation" }, + { "dump-random", &dump_random, "generate random bytes" }, + { "send-query", &send_query, "send query to host" }, + { "send-query-udp", &send_query, "send udp query to host" }, + { "send-query-tcp", &send_query, "send tcp query to host" }, + { "print-arpa", &print_arpa, "print arpa. zone name of address" }, + { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" }, + { "resolve-stub", &resolve_query, "resolve as stub resolver" }, + { "resolve-recurse", &resolve_query, "resolve as recursive resolver" }, + { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" }, + { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" }, +/* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */ + { "dump-trace", &dump_trace, "dump the contents of a trace file" }, + { "echo", &echo_port, "server echo mode, for nmap fuzzing" }, + { "isection", &isection, "parse section string" }, + { "iclass", &iclass, "parse class string" }, + { "itype", &itype, "parse type string" }, + { "iopcode", &iopcode, "parse opcode string" }, + { "ircode", &ircode, "parse rcode string" }, + { "sizes", &sizes, "print data structure sizes" }, +}; + + +static void print_usage(const char *progname, FILE *fp) { + static const char *usage = + " [OPTIONS] COMMAND [ARGS]\n" + " -c PATH Path to resolv.conf\n" + " -n PATH Path to nsswitch.conf\n" + " -l PATH Path to local hosts\n" + " -z PATH Path to zone cache\n" + " -q QNAME Query name\n" + " -t QTYPE Query type\n" + " -s HOW Sort records\n" + " -S ADDR Address of SOCKS server to use\n" + " -P PORT Port of SOCKS server to use\n" + " -A USER:PASSWORD Credentials for the SOCKS server\n" + " -f PATH Path to trace file\n" + " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n" + " -V Print version info\n" + " -h Print this usage message\n" + "\n"; + unsigned i, n, m; + + fputs(progname, fp); + fputs(usage, fp); + + for (i = 0, m = 0; i < lengthof(cmds); i++) { + if (strlen(cmds[i].cmd) > m) + m = strlen(cmds[i].cmd); + } + + for (i = 0; i < lengthof(cmds); i++) { + fprintf(fp, " %s ", cmds[i].cmd); + + for (n = strlen(cmds[i].cmd); n < m; n++) + putc(' ', fp); + + fputs(cmds[i].help, fp); + putc('\n', fp); + } + + fputs("\nReport bugs to William Ahern \n", fp); +} /* print_usage() */ + + +static void print_version(const char *progname, FILE *fp) { + fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel()); + fprintf(fp, "vendor %s\n", dns_vendor()); + fprintf(fp, "release %.8X\n", dns_v_rel()); + fprintf(fp, "abi %.8X\n", dns_v_abi()); + fprintf(fp, "api %.8X\n", dns_v_api()); +} /* print_version() */ + + +static void main_exit(void) { + dns_trace_close(MAIN.memo.trace); + MAIN.memo.trace = NULL; + dns_hosts_close(MAIN.memo.hosts); + MAIN.memo.hosts = NULL; + dns_resconf_close(MAIN.memo.resconf); + MAIN.memo.resconf = NULL; +} /* main_exit() */ + +int main(int argc, char **argv) { + extern int optind; + extern char *optarg; + const char *progname = argv[0]; + unsigned i; + int ch; + + atexit(&main_exit); + + while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:S:P:A:f:vVh"))) { + switch (ch) { + case 'c': + assert(MAIN.resconf.count < lengthof(MAIN.resconf.path)); + + MAIN.resconf.path[MAIN.resconf.count++] = optarg; + + break; + case 'n': + assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path)); + + MAIN.nssconf.path[MAIN.nssconf.count++] = optarg; + + break; + case 'l': + assert(MAIN.hosts.count < lengthof(MAIN.hosts.path)); + + MAIN.hosts.path[MAIN.hosts.count++] = optarg; + + break; + case 'z': + assert(MAIN.cache.count < lengthof(MAIN.cache.path)); + + MAIN.cache.path[MAIN.cache.count++] = optarg; + + break; + case 'q': + MAIN.qname = optarg; + + break; + case 't': + for (i = 0; i < lengthof(dns_rrtypes); i++) { + if (0 == strcasecmp(dns_rrtypes[i].name, optarg)) + { MAIN.qtype = dns_rrtypes[i].type; break; } + } + + if (MAIN.qtype) + break; + + for (i = 0; dns_isdigit(optarg[i]); i++) { + MAIN.qtype *= 10; + MAIN.qtype += optarg[i] - '0'; + } + + if (!MAIN.qtype) + panic("%s: invalid query type", optarg); + + break; + case 's': + if (0 == strcasecmp(optarg, "packet")) + MAIN.sort = &dns_rr_i_packet; + else if (0 == strcasecmp(optarg, "shuffle")) + MAIN.sort = &dns_rr_i_shuffle; + else if (0 == strcasecmp(optarg, "order")) + MAIN.sort = &dns_rr_i_order; + else + panic("%s: invalid sort method", optarg); + + break; + case 'S': { + dns_error_t error; + struct dns_resolv_conf *conf = resconf(); + conf->options.tcp = DNS_RESCONF_TCP_SOCKS; + + MAIN.socks_host.ss_family = (strchr(optarg, ':')) ? AF_INET6 : AF_INET; + if ((error = dns_pton(MAIN.socks_host.ss_family, optarg, + dns_sa_addr(MAIN.socks_host.ss_family, + &MAIN.socks_host, NULL)))) + panic("%s: %s", optarg, dns_strerror(error)); + + *dns_sa_port(MAIN.socks_host.ss_family, &MAIN.socks_host) = htons(1080); + + break; + } + case 'P': + if (! MAIN.socks_host.ss_family) + panic("-P without prior -S"); + *dns_sa_port(MAIN.socks_host.ss_family, &MAIN.socks_host) = htons(atoi(optarg)); + + break; + case 'A': { + char *password; + if (! MAIN.socks_host.ss_family) + panic("-A without prior -S"); + if (! (password = strchr(optarg, ':'))) + panic("Usage: -A USER:PASSWORD"); + *password = 0; + password += 1; + MAIN.socks_user = optarg; + MAIN.socks_password = password; + break; + } + case 'f': + MAIN.trace = optarg; + + break; + case 'v': + dns_debug = ++MAIN.verbose; + + break; + case 'V': + print_version(progname, stdout); + + return 0; + case 'h': + print_usage(progname, stdout); + + return 0; + default: + print_usage(progname, stderr); + + return EXIT_FAILURE; + } /* switch() */ + } /* while() */ + + argc -= optind; + argv += optind; + + for (i = 0; i < lengthof(cmds) && argv[0]; i++) { + if (0 == strcmp(cmds[i].cmd, argv[0])) + return cmds[i].run(argc, argv); + } + + print_usage(progname, stderr); + + return EXIT_FAILURE; +} /* main() */ + + +#endif /* DNS_MAIN */ + + +/* + * pop file-scoped compiler annotations + */ +#if __clang__ +#pragma clang diagnostic pop +#elif DNS_GNUC_PREREQ(4,6,0) +#pragma GCC diagnostic pop +#endif diff --git a/dirmngr/dns.h b/dirmngr/dns.h new file mode 100644 index 0000000..30d0b45 --- /dev/null +++ b/dirmngr/dns.h @@ -0,0 +1,1365 @@ +/* ========================================================================== + * dns.h - Recursive, Reentrant DNS Resolver. + * -------------------------------------------------------------------------- + * Copyright (c) 2009, 2010, 2012-2015 William Ahern + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * ========================================================================== + */ +#ifndef DNS_H +#define DNS_H + +#include /* size_t offsetof() */ +#include /* uint64_t */ +#include /* FILE */ +#include /* strlen(3) */ +#include /* struct timespec time_t */ + +#if _WIN32 +#include +#include +#else +#include /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */ +#include /* socklen_t */ +#include /* struct socket */ +#include /* POLLIN POLLOUT */ +#include /* struct in_addr struct in6_addr */ +#include /* struct addrinfo */ +#endif + + +/* + * V I S I B I L I T Y + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DNS_PUBLIC +#define DNS_PUBLIC +#endif + + +/* + * V E R S I O N + * + * Vendor: Entity for which versions numbers are relevant. (If forking + * change DNS_VENDOR to avoid confusion.) + * + * Three versions: + * + * REL Official "release"--bug fixes, new features, etc. + * ABI Changes to existing object sizes or parameter types + * API Changes that might effect application source. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_VENDOR "william@25thandClement.com" + +#define DNS_V_REL 0x20160809 +#define DNS_V_ABI 0x20160608 +#define DNS_V_API 0x20160809 + + +DNS_PUBLIC const char *dns_vendor(void); + +DNS_PUBLIC int dns_v_rel(void); +DNS_PUBLIC int dns_v_abi(void); +DNS_PUBLIC int dns_v_api(void); + + +/* + * E R R O R S + * + * Errors and exceptions are always returned through an int. This should + * hopefully make integration easier in the majority of circumstances, and + * also cut down on useless compiler warnings. + * + * System and library errors are returned together. POSIX guarantees that + * all system errors are positive integers. Library errors are always + * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits + * set to the three magic ASCII characters "dns". + * + * dns_strerror() returns static English string descriptions of all known + * errors, and punts the remainder to strerror(3). + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64) + +#define dns_error_t int /* for documentation only */ + +enum dns_errno { + DNS_ENOBUFS = DNS_EBASE, + DNS_EILLEGAL, + DNS_EORDER, + DNS_ESECTION, + DNS_EUNKNOWN, + DNS_EADDRESS, + DNS_ENOQUERY, + DNS_ENOANSWER, + DNS_EFETCHED, + DNS_ESERVICE, /* EAI_SERVICE */ + DNS_ENONAME, /* EAI_NONAME */ + DNS_EFAIL, /* EAI_FAIL */ + DNS_ECONNFIN, + DNS_EVERIFY, + DNS_ELAST, +}; /* dns_errno */ + +DNS_PUBLIC const char *dns_strerror(dns_error_t); + +DNS_PUBLIC int *dns_debug_p(void); + +#define dns_debug (*dns_debug_p()) /* was extern int dns_debug before 20160523 API */ + + +/* + * C O M P I L E R A N N O T A T I O N S + * + * GCC with -Wextra, and clang by default, complain about overrides in + * initializer lists. Overriding previous member initializers is well + * defined behavior in C. dns.c relies on this behavior to define default, + * overrideable member values when instantiating configuration objects. + * + * dns_quietinit() guards a compound literal expression with pragmas to + * silence these shrill warnings. This alleviates the burden of requiring + * third-party projects to adjust their compiler flags. + * + * NOTE: If you take the address of the compound literal, take the address + * of the transformed expression, otherwise the compound literal lifetime is + * tied to the scope of the GCC statement expression. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined __clang__ +#define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push") +#define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"") +#define DNS_PRAGMA_POP _Pragma("clang diagnostic pop") + +#define dns_quietinit(...) \ + DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP +#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 +#define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push") +#define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"") +#define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop") + +/* GCC parses the _Pragma operator less elegantly than clang. */ +#define dns_quietinit(...) \ + __extension__ ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP }) +#else +#define DNS_PRAGMA_PUSH +#define DNS_PRAGMA_QUIET +#define DNS_PRAGMA_POP +#define dns_quietinit(...) __VA_ARGS__ +#endif + +#if defined __GNUC__ +#define DNS_PRAGMA_EXTENSION __extension__ +#else +#define DNS_PRAGMA_EXTENSION +#endif + + +/* + * E V E N T S I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined(POLLIN) +#define DNS_POLLIN POLLIN +#else +#define DNS_POLLIN 1 +#endif + +#if defined(POLLOUT) +#define DNS_POLLOUT POLLOUT +#else +#define DNS_POLLOUT 2 +#endif + + +/* + * See Application Interface below for configuring libevent bitmasks instead + * of poll(2) bitmasks. + */ +#define DNS_EVREAD 2 +#define DNS_EVWRITE 4 + + +#define DNS_POLL2EV(set) \ + (((set) & DNS_POLLIN)? DNS_EVREAD : 0) | (((set) & DNS_POLLOUT)? DNS_EVWRITE : 0) + +#define DNS_EV2POLL(set) \ + (((set) & DNS_EVREAD)? DNS_POLLIN : 0) | (((set) & DNS_EVWRITE)? DNS_POLLOUT : 0) + + +/* + * E N U M E R A T I O N I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +enum dns_section { + DNS_S_QD = 0x01, +#define DNS_S_QUESTION DNS_S_QD + + DNS_S_AN = 0x02, +#define DNS_S_ANSWER DNS_S_AN + + DNS_S_NS = 0x04, +#define DNS_S_AUTHORITY DNS_S_NS + + DNS_S_AR = 0x08, +#define DNS_S_ADDITIONAL DNS_S_AR + + DNS_S_ALL = 0x0f +}; /* enum dns_section */ + + +enum dns_class { + DNS_C_IN = 1, + + DNS_C_ANY = 255 +}; /* enum dns_class */ + + +enum dns_type { + DNS_T_A = 1, + DNS_T_NS = 2, + DNS_T_CNAME = 5, + DNS_T_SOA = 6, + DNS_T_PTR = 12, + DNS_T_MX = 15, + DNS_T_TXT = 16, + DNS_T_AAAA = 28, + DNS_T_SRV = 33, + DNS_T_OPT = 41, + DNS_T_SSHFP = 44, + DNS_T_SPF = 99, + DNS_T_AXFR = 252, + + DNS_T_ALL = 255 +}; /* enum dns_type */ + + +enum dns_opcode { + DNS_OP_QUERY = 0, + DNS_OP_IQUERY = 1, + DNS_OP_STATUS = 2, + DNS_OP_NOTIFY = 4, + DNS_OP_UPDATE = 5, +}; /* dns_opcode */ + + +enum dns_rcode { + DNS_RC_NOERROR = 0, + DNS_RC_FORMERR = 1, + DNS_RC_SERVFAIL = 2, + DNS_RC_NXDOMAIN = 3, + DNS_RC_NOTIMP = 4, + DNS_RC_REFUSED = 5, + DNS_RC_YXDOMAIN = 6, + DNS_RC_YXRRSET = 7, + DNS_RC_NXRRSET = 8, + DNS_RC_NOTAUTH = 9, + DNS_RC_NOTZONE = 10, + + /* EDNS(0) extended RCODEs */ + DNS_RC_BADVERS = 16, +}; /* dns_rcode */ + + +/* + * NOTE: These string functions need a small buffer in case the literal + * integer value needs to be printed and returned. UNLESS this buffer is + * SPECIFIED, the returned string has ONLY BLOCK SCOPE. + */ +#define DNS_STRMAXLEN 47 /* "QUESTION|ANSWER|AUTHORITY|ADDITIONAL" */ + +DNS_PUBLIC const char *dns_strsection(enum dns_section, void *, size_t); +#define dns_strsection3(a, b, c) \ + dns_strsection((a), (b), (c)) +#define dns_strsection1(a) dns_strsection((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) +#define dns_strsection(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strsection, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC enum dns_section dns_isection(const char *); + +DNS_PUBLIC const char *dns_strclass(enum dns_class, void *, size_t); +#define dns_strclass3(a, b, c) dns_strclass((a), (b), (c)) +#define dns_strclass1(a) dns_strclass((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) +#define dns_strclass(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strclass, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC enum dns_class dns_iclass(const char *); + +DNS_PUBLIC const char *dns_strtype(enum dns_type, void *, size_t); +#define dns_strtype3(a, b, c) dns_strtype((a), (b), (c)) +#define dns_strtype1(a) dns_strtype((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) +#define dns_strtype(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strtype, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC enum dns_type dns_itype(const char *); + +DNS_PUBLIC const char *dns_stropcode(enum dns_opcode); + +DNS_PUBLIC enum dns_opcode dns_iopcode(const char *); + +DNS_PUBLIC const char *dns_strrcode(enum dns_rcode); + +DNS_PUBLIC enum dns_rcode dns_ircode(const char *); + + +/* + * A T O M I C I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +typedef unsigned long dns_atomic_t; + +typedef unsigned long dns_refcount_t; /* must be same value type as dns_atomic_t */ + + +/* + * C R Y P T O I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +typedef unsigned dns_random_f(void); + +DNS_PUBLIC dns_random_f **dns_random_p(void); + +#define dns_random (*dns_random_p()) /* was extern unsigned (*dns_random)(void) before 20160523 API */ + + +/* + * P A C K E T I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_header { + unsigned qid:16; + +#if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN) + unsigned qr:1; + unsigned opcode:4; + unsigned aa:1; + unsigned tc:1; + unsigned rd:1; + + unsigned ra:1; + unsigned unused:3; + unsigned rcode:4; +#else + unsigned rd:1; + unsigned tc:1; + unsigned aa:1; + unsigned opcode:4; + unsigned qr:1; + + unsigned rcode:4; + unsigned unused:3; + unsigned ra:1; +#endif + + unsigned qdcount:16; + unsigned ancount:16; + unsigned nscount:16; + unsigned arcount:16; +}; /* struct dns_header */ + +#define dns_header(p) (&(p)->header) + + +#ifndef DNS_P_QBUFSIZ +#define DNS_P_QBUFSIZ dns_p_calcsize(256 + 4) +#endif + +#ifndef DNS_P_DICTSIZE +#define DNS_P_DICTSIZE 16 +#endif + +struct dns_packet { + unsigned short dict[DNS_P_DICTSIZE]; + + struct dns_p_memo { + struct dns_s_memo { + unsigned short base, end; + } qd, an, ns, ar; + + struct { + unsigned short p; + unsigned short maxudp; + unsigned ttl; + } opt; + } memo; + + struct { struct dns_packet *cqe_next, *cqe_prev; } cqe; + + size_t size, end; + + int:16; /* tcp padding */ + + DNS_PRAGMA_EXTENSION union { + struct dns_header header; + unsigned char data[1]; + }; +}; /* struct dns_packet */ + +#define dns_p_calcsize(n) (offsetof(struct dns_packet, data) + DNS_PP_MAX(12, (n))) + +#define dns_p_sizeof(P) dns_p_calcsize((P)->end) + +/** takes size of maximum desired payload */ +#define dns_p_new(n) (dns_p_init((struct dns_packet *)&(union { unsigned char b[dns_p_calcsize((n))]; struct dns_packet p; }){ { 0 } }, dns_p_calcsize((n)))) + +/** takes size of entire packet structure as allocated */ +DNS_PUBLIC struct dns_packet *dns_p_init(struct dns_packet *, size_t); + +/** takes size of maximum desired payload */ +DNS_PUBLIC struct dns_packet *dns_p_make(size_t, int *); + +DNS_PUBLIC int dns_p_grow(struct dns_packet **); + +DNS_PUBLIC struct dns_packet *dns_p_copy(struct dns_packet *, const struct dns_packet *); + +#define dns_p_opcode(P) (dns_header(P)->opcode) + +DNS_PUBLIC enum dns_rcode dns_p_rcode(struct dns_packet *); + +DNS_PUBLIC unsigned dns_p_count(struct dns_packet *, enum dns_section); + +DNS_PUBLIC int dns_p_push(struct dns_packet *, enum dns_section, const void *, size_t, enum dns_type, enum dns_class, unsigned, const void *); + +DNS_PUBLIC void dns_p_dictadd(struct dns_packet *, unsigned short); + +DNS_PUBLIC struct dns_packet *dns_p_merge(struct dns_packet *, enum dns_section, struct dns_packet *, enum dns_section, int *); + +DNS_PUBLIC void dns_p_dump(struct dns_packet *, FILE *); + +DNS_PUBLIC int dns_p_study(struct dns_packet *); + + +/* + * D O M A I N N A M E I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_D_MAXLABEL 63 /* + 1 '\0' */ +#define DNS_D_MAXNAME 255 /* + 1 '\0' */ + +#define DNS_D_ANCHOR 1 /* anchor domain w/ root "." */ +#define DNS_D_CLEAVE 2 /* cleave sub-domain */ +#define DNS_D_TRIM 4 /* remove superfluous dots */ + +#define dns_d_new3(a, b, f) dns_d_init(&(char[DNS_D_MAXNAME + 1]){ 0 }, DNS_D_MAXNAME + 1, (a), (b), (f)) +#define dns_d_new2(a, f) dns_d_new3((a), strlen((a)), (f)) +#define dns_d_new1(a) dns_d_new3((a), strlen((a)), DNS_D_ANCHOR) +#define dns_d_new(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_d_new, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) + +DNS_PUBLIC char *dns_d_init(void *, size_t, const void *, size_t, int); + +DNS_PUBLIC size_t dns_d_anchor(void *, size_t, const void *, size_t); + +DNS_PUBLIC size_t dns_d_cleave(void *, size_t, const void *, size_t); + +DNS_PUBLIC size_t dns_d_comp(void *, size_t, const void *, size_t, struct dns_packet *, int *); + +DNS_PUBLIC size_t dns_d_expand(void *, size_t, unsigned short, struct dns_packet *, int *); + +DNS_PUBLIC unsigned short dns_d_skip(unsigned short, struct dns_packet *); + +DNS_PUBLIC int dns_d_push(struct dns_packet *, const void *, size_t); + +DNS_PUBLIC size_t dns_d_cname(void *, size_t, const void *, size_t, struct dns_packet *, int *error); + + +/* + * R E S O U R C E R E C O R D I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_rr { + enum dns_section section; + + struct { + unsigned short p; + unsigned short len; + } dn; + + enum dns_type type; + enum dns_class class; + unsigned ttl; + + struct { + unsigned short p; + unsigned short len; + } rd; +}; /* struct dns_rr */ + + +DNS_PUBLIC int dns_rr_copy(struct dns_packet *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_rr_parse(struct dns_rr *, unsigned short, struct dns_packet *); + +DNS_PUBLIC unsigned short dns_rr_skip(unsigned short, struct dns_packet *); + +DNS_PUBLIC int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *); + + +#define dns_rr_i_new(P, ...) \ + dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P)) + +struct dns_rr_i { + enum dns_section section; + const void *name; + enum dns_type type; + enum dns_class class; + const void *data; + + int follow; + + int (*sort)(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + unsigned args[2]; + + struct { + unsigned short next; + unsigned short count; + + unsigned exec; + unsigned regs[2]; + } state, saved; +}; /* struct dns_rr_i */ + +DNS_PUBLIC int dns_rr_i_packet(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + +DNS_PUBLIC int dns_rr_i_order(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + +DNS_PUBLIC int dns_rr_i_shuffle(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); + +DNS_PUBLIC struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *, struct dns_packet *); + +#define dns_rr_i_save(i) ((i)->saved = (i)->state) +#define dns_rr_i_rewind(i) ((i)->state = (i)->saved) +#define dns_rr_i_count(i) ((i)->state.count) + +DNS_PUBLIC unsigned dns_rr_grep(struct dns_rr *, unsigned, struct dns_rr_i *, struct dns_packet *, int *); + +#define dns_rr_foreach_(rr, P, ...) \ + for (struct dns_rr_i DNS_PP_XPASTE(i, __LINE__) = *dns_rr_i_new((P), __VA_ARGS__); dns_rr_grep((rr), 1, &DNS_PP_XPASTE(i, __LINE__), (P), &(int){ 0 }); ) + +#define dns_rr_foreach(...) dns_rr_foreach_(__VA_ARGS__) + + +/* + * A R E S O U R C E R E C O R D + */ + +struct dns_a { + struct in_addr addr; +}; /* struct dns_a */ + +DNS_PUBLIC int dns_a_parse(struct dns_a *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_a_push(struct dns_packet *, struct dns_a *); + +DNS_PUBLIC int dns_a_cmp(const struct dns_a *, const struct dns_a *); + +DNS_PUBLIC size_t dns_a_print(void *, size_t, struct dns_a *); + +DNS_PUBLIC size_t dns_a_arpa(void *, size_t, const struct dns_a *); + + +/* + * AAAA R E S O U R C E R E C O R D + */ + +struct dns_aaaa { + struct in6_addr addr; +}; /* struct dns_aaaa */ + +DNS_PUBLIC int dns_aaaa_parse(struct dns_aaaa *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_aaaa_push(struct dns_packet *, struct dns_aaaa *); + +DNS_PUBLIC int dns_aaaa_cmp(const struct dns_aaaa *, const struct dns_aaaa *); + +DNS_PUBLIC size_t dns_aaaa_print(void *, size_t, struct dns_aaaa *); + +DNS_PUBLIC size_t dns_aaaa_arpa(void *, size_t, const struct dns_aaaa *); + + +/* + * MX R E S O U R C E R E C O R D + */ + +struct dns_mx { + unsigned short preference; + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_mx */ + +DNS_PUBLIC int dns_mx_parse(struct dns_mx *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_mx_push(struct dns_packet *, struct dns_mx *); + +DNS_PUBLIC int dns_mx_cmp(const struct dns_mx *, const struct dns_mx *); + +DNS_PUBLIC size_t dns_mx_print(void *, size_t, struct dns_mx *); + +DNS_PUBLIC size_t dns_mx_cname(void *, size_t, struct dns_mx *); + + +/* + * NS R E S O U R C E R E C O R D + */ + +struct dns_ns { + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_ns */ + +DNS_PUBLIC int dns_ns_parse(struct dns_ns *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_ns_push(struct dns_packet *, struct dns_ns *); + +DNS_PUBLIC int dns_ns_cmp(const struct dns_ns *, const struct dns_ns *); + +DNS_PUBLIC size_t dns_ns_print(void *, size_t, struct dns_ns *); + +DNS_PUBLIC size_t dns_ns_cname(void *, size_t, struct dns_ns *); + + +/* + * CNAME R E S O U R C E R E C O R D + */ + +struct dns_cname { + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_cname */ + +DNS_PUBLIC int dns_cname_parse(struct dns_cname *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_cname_push(struct dns_packet *, struct dns_cname *); + +DNS_PUBLIC int dns_cname_cmp(const struct dns_cname *, const struct dns_cname *); + +DNS_PUBLIC size_t dns_cname_print(void *, size_t, struct dns_cname *); + +DNS_PUBLIC size_t dns_cname_cname(void *, size_t, struct dns_cname *); + + +/* + * SOA R E S O U R C E R E C O R D + */ + +struct dns_soa { + char mname[DNS_D_MAXNAME + 1]; + char rname[DNS_D_MAXNAME + 1]; + unsigned serial, refresh, retry, expire, minimum; +}; /* struct dns_soa */ + +DNS_PUBLIC int dns_soa_parse(struct dns_soa *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_soa_push(struct dns_packet *, struct dns_soa *); + +DNS_PUBLIC int dns_soa_cmp(const struct dns_soa *, const struct dns_soa *); + +DNS_PUBLIC size_t dns_soa_print(void *, size_t, struct dns_soa *); + + +/* + * PTR R E S O U R C E R E C O R D + */ + +struct dns_ptr { + char host[DNS_D_MAXNAME + 1]; +}; /* struct dns_ptr */ + +DNS_PUBLIC int dns_ptr_parse(struct dns_ptr *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_ptr_push(struct dns_packet *, struct dns_ptr *); + +DNS_PUBLIC int dns_ptr_cmp(const struct dns_ptr *, const struct dns_ptr *); + +DNS_PUBLIC size_t dns_ptr_print(void *, size_t, struct dns_ptr *); + +DNS_PUBLIC size_t dns_ptr_cname(void *, size_t, struct dns_ptr *); + +DNS_PUBLIC size_t dns_ptr_qname(void *, size_t, int, void *); + + +/* + * SRV R E S O U R C E R E C O R D + */ + +struct dns_srv { + unsigned short priority; + unsigned short weight; + unsigned short port; + char target[DNS_D_MAXNAME + 1]; +}; /* struct dns_srv */ + +DNS_PUBLIC int dns_srv_parse(struct dns_srv *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_srv_push(struct dns_packet *, struct dns_srv *); + +DNS_PUBLIC int dns_srv_cmp(const struct dns_srv *, const struct dns_srv *); + +DNS_PUBLIC size_t dns_srv_print(void *, size_t, struct dns_srv *); + +DNS_PUBLIC size_t dns_srv_cname(void *, size_t, struct dns_srv *); + + +/* + * OPT R E S O U R C E R E C O R D + */ + +#ifndef DNS_OPT_MINDATA +#define DNS_OPT_MINDATA 256 +#endif + +#define DNS_OPT_DNSSEC 0x8000 + +struct dns_opt { + enum dns_rcode rcode; + unsigned char version; + unsigned short flags; + + union { + unsigned short maxsize; /* deprecated as confusing */ + unsigned short maxudp; /* maximum UDP payload size */ + }; + + size_t size, len; + unsigned char data[DNS_OPT_MINDATA]; +}; /* struct dns_opt */ + +#define DNS_OPT_INIT(opt) { .size = sizeof (*opt) - offsetof(struct dns_opt, data) } + +DNS_PUBLIC struct dns_opt *dns_opt_init(struct dns_opt *, size_t); + +DNS_PUBLIC int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_opt_push(struct dns_packet *, struct dns_opt *); + +DNS_PUBLIC int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *); + +DNS_PUBLIC size_t dns_opt_print(void *, size_t, struct dns_opt *); + +DNS_PUBLIC unsigned int dns_opt_ttl(const struct dns_opt *); + +DNS_PUBLIC unsigned short dns_opt_class(const struct dns_opt *); + +DNS_PUBLIC dns_error_t dns_opt_data_push(struct dns_opt *, unsigned char, unsigned short, const void *); + + +/* + * SSHFP R E S O U R C E R E C O R D + */ + +struct dns_sshfp { + enum dns_sshfp_key { + DNS_SSHFP_RSA = 1, + DNS_SSHFP_DSA = 2, + } algo; + + enum dns_sshfp_digest { + DNS_SSHFP_SHA1 = 1, + } type; + + union { + unsigned char sha1[20]; + } digest; +}; /* struct dns_sshfp */ + +DNS_PUBLIC int dns_sshfp_parse(struct dns_sshfp *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_sshfp_push(struct dns_packet *, struct dns_sshfp *); + +DNS_PUBLIC int dns_sshfp_cmp(const struct dns_sshfp *, const struct dns_sshfp *); + +DNS_PUBLIC size_t dns_sshfp_print(void *, size_t, struct dns_sshfp *); + + +/* + * TXT R E S O U R C E R E C O R D + */ + +#ifndef DNS_TXT_MINDATA +#define DNS_TXT_MINDATA 1024 +#endif + +struct dns_txt { + size_t size, len; + unsigned char data[DNS_TXT_MINDATA]; +}; /* struct dns_txt */ + +DNS_PUBLIC struct dns_txt *dns_txt_init(struct dns_txt *, size_t); + +DNS_PUBLIC int dns_txt_parse(struct dns_txt *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_txt_push(struct dns_packet *, struct dns_txt *); + +DNS_PUBLIC int dns_txt_cmp(const struct dns_txt *, const struct dns_txt *); + +DNS_PUBLIC size_t dns_txt_print(void *, size_t, struct dns_txt *); + + +/* + * ANY R E S O U R C E R E C O R D + */ + +union dns_any { + struct dns_a a; + struct dns_aaaa aaaa; + struct dns_mx mx; + struct dns_ns ns; + struct dns_cname cname; + struct dns_soa soa; + struct dns_ptr ptr; + struct dns_srv srv; + struct dns_opt opt; + struct dns_sshfp sshfp; + struct dns_txt txt, spf, rdata; +}; /* union dns_any */ + +#define DNS_ANY_INIT(any) { .rdata = { .size = sizeof *(any) - offsetof(struct dns_txt, data) } } + +DNS_PUBLIC union dns_any *dns_any_init(union dns_any *, size_t); + +DNS_PUBLIC int dns_any_parse(union dns_any *, struct dns_rr *, struct dns_packet *); + +DNS_PUBLIC int dns_any_push(struct dns_packet *, union dns_any *, enum dns_type); + +DNS_PUBLIC int dns_any_cmp(const union dns_any *, enum dns_type, const union dns_any *, enum dns_type); + +DNS_PUBLIC size_t dns_any_print(void *, size_t, union dns_any *, enum dns_type); + +DNS_PUBLIC size_t dns_any_cname(void *, size_t, union dns_any *, enum dns_type); + + +/* + * H O S T S I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hosts; + +DNS_PUBLIC struct dns_hosts *dns_hosts_open(int *); + +DNS_PUBLIC void dns_hosts_close(struct dns_hosts *); + +DNS_PUBLIC dns_refcount_t dns_hosts_acquire(struct dns_hosts *); + +DNS_PUBLIC dns_refcount_t dns_hosts_release(struct dns_hosts *); + +DNS_PUBLIC struct dns_hosts *dns_hosts_mortal(struct dns_hosts *); + +DNS_PUBLIC struct dns_hosts *dns_hosts_local(int *); + +DNS_PUBLIC int dns_hosts_loadfile(struct dns_hosts *, FILE *); + +DNS_PUBLIC int dns_hosts_loadpath(struct dns_hosts *, const char *); + +DNS_PUBLIC int dns_hosts_dump(struct dns_hosts *, FILE *); + +DNS_PUBLIC int dns_hosts_insert(struct dns_hosts *, int, const void *, const void *, _Bool); + +DNS_PUBLIC struct dns_packet *dns_hosts_query(struct dns_hosts *, struct dns_packet *, int *); + + +/* + * R E S O L V . C O N F I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_resolv_conf { + struct sockaddr_storage nameserver[3]; + + char search[4][DNS_D_MAXNAME + 1]; + + /* (f)ile, (b)ind, (c)ache */ + char lookup[4 * (1 + (4 * 2))]; + + /* getaddrinfo family by preference order ("inet4", "inet6") */ + int family[3]; + + struct { + _Bool edns0; + + unsigned ndots; + + unsigned timeout; + + unsigned attempts; + + _Bool rotate; + + _Bool recurse; + + _Bool smart; + + enum { + DNS_RESCONF_TCP_ENABLE, + DNS_RESCONF_TCP_ONLY, + DNS_RESCONF_TCP_SOCKS, + DNS_RESCONF_TCP_DISABLE, + } tcp; + } options; + + struct sockaddr_storage iface; + + struct { /* PRIVATE */ + dns_atomic_t refcount; + } _; +}; /* struct dns_resolv_conf */ + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_open(int *); + +DNS_PUBLIC void dns_resconf_close(struct dns_resolv_conf *); + +DNS_PUBLIC dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *); + +DNS_PUBLIC dns_refcount_t dns_resconf_release(struct dns_resolv_conf *); + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *); + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_local(int *); + +DNS_PUBLIC struct dns_resolv_conf *dns_resconf_root(int *); + +DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *, const char *); + +DNS_PUBLIC int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_resconf_loadpath(struct dns_resolv_conf *, const char *); + +DNS_PUBLIC int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *); + +DNS_PUBLIC int dns_resconf_dump(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_nssconf_dump(struct dns_resolv_conf *, FILE *); + +DNS_PUBLIC int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short); + +typedef unsigned long dns_resconf_i_t; + +DNS_PUBLIC size_t dns_resconf_search(void *, size_t, const void *, size_t, struct dns_resolv_conf *, dns_resconf_i_t *); + + +/* + * H I N T S E R V E R I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_hints; + +DNS_PUBLIC struct dns_hints *dns_hints_open(struct dns_resolv_conf *, int *); + +DNS_PUBLIC void dns_hints_close(struct dns_hints *); + +DNS_PUBLIC dns_refcount_t dns_hints_acquire(struct dns_hints *); + +DNS_PUBLIC dns_refcount_t dns_hints_release(struct dns_hints *); + +DNS_PUBLIC struct dns_hints *dns_hints_mortal(struct dns_hints *); + +DNS_PUBLIC int dns_hints_insert(struct dns_hints *, const char *, const struct sockaddr *, unsigned); + +DNS_PUBLIC unsigned dns_hints_insert_resconf(struct dns_hints *, const char *, const struct dns_resolv_conf *, int *); + +DNS_PUBLIC struct dns_hints *dns_hints_local(struct dns_resolv_conf *, int *); + +DNS_PUBLIC struct dns_hints *dns_hints_root(struct dns_resolv_conf *, int *); + +DNS_PUBLIC struct dns_packet *dns_hints_query(struct dns_hints *, struct dns_packet *, int *); + +DNS_PUBLIC int dns_hints_dump(struct dns_hints *, FILE *); + + +struct dns_hints_i { + const char *zone; + + struct { + unsigned next; + unsigned seed; + } state; +}; /* struct dns_hints_i */ + +#define dns_hints_i_new(...) (&(struct dns_hints_i){ __VA_ARGS__ }) + +DNS_PUBLIC unsigned dns_hints_grep(struct sockaddr **, socklen_t *, unsigned, struct dns_hints_i *, struct dns_hints *); + + +/* + * C A C H E I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_cache { + void *state; + + dns_refcount_t (*acquire)(struct dns_cache *); + dns_refcount_t (*release)(struct dns_cache *); + + struct dns_packet *(*query)(struct dns_packet *, struct dns_cache *, int *); + + int (*submit)(struct dns_packet *, struct dns_cache *); + int (*check)(struct dns_cache *); + struct dns_packet *(*fetch)(struct dns_cache *, int *); + + int (*pollfd)(struct dns_cache *); + short (*events)(struct dns_cache *); + void (*clear)(struct dns_cache *); + + union { + long i; + void *p; + } arg[3]; + + struct { /* PRIVATE */ + dns_atomic_t refcount; + } _; +}; /* struct dns_cache */ + + +DNS_PUBLIC struct dns_cache *dns_cache_init(struct dns_cache *); + +DNS_PUBLIC void dns_cache_close(struct dns_cache *); + + +/* + * A P P L I C A T I O N I N T E R F A C E + * + * Options to change the behavior of the API. Applies across all the + * different components. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_OPTS_INITIALIZER_ { 0, 0 }, 0, 0 +#define DNS_OPTS_INITIALIZER { DNS_OPTS_INITIALIZER_ } +#define DNS_OPTS_INIT(...) { DNS_OPTS_INITIALIZER_, __VA_ARGS__ } + +#define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__))) + +struct dns_options { + /* + * If the callback closes *fd, it must set it to -1. Otherwise, the + * descriptor is queued and lazily closed at object destruction or + * by an explicit call to _clear(). This allows safe use of + * kqueue(2), epoll(2), et al -style persistent events. + */ + struct { + void *arg; + int (*cb)(int *fd, void *arg); + } closefd; + + /* bitmask for _events() routines */ + enum dns_events { + DNS_SYSPOLL, + DNS_LIBEVENT, + } events; + + /* Use this SOCKS server. */ + const struct sockaddr_storage *socks_host; + + /* Credentials for the SOCKS server (optional). */ + const char *socks_user; + const char *socks_password; +}; /* struct dns_options */ + + +/* + * S T A T S I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_stat { + size_t queries; + + struct { + struct { + size_t count, bytes; + } sent, rcvd; + } udp, tcp; +}; /* struct dns_stat */ + + +/* + * S O C K E T I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_socket; + +DNS_PUBLIC struct dns_socket *dns_so_open(const struct sockaddr *, int, const struct dns_options *, int *error); + +DNS_PUBLIC void dns_so_close(struct dns_socket *); + +DNS_PUBLIC void dns_so_reset(struct dns_socket *); + +DNS_PUBLIC unsigned short dns_so_mkqid(struct dns_socket *so); + +DNS_PUBLIC struct dns_packet *dns_so_query(struct dns_socket *, struct dns_packet *, struct sockaddr *, int *); + +DNS_PUBLIC int dns_so_submit(struct dns_socket *, struct dns_packet *, struct sockaddr *); + +DNS_PUBLIC int dns_so_check(struct dns_socket *); + +DNS_PUBLIC struct dns_packet *dns_so_fetch(struct dns_socket *, int *); + +DNS_PUBLIC time_t dns_so_elapsed(struct dns_socket *); + +DNS_PUBLIC void dns_so_clear(struct dns_socket *); + +DNS_PUBLIC int dns_so_events(struct dns_socket *); + +DNS_PUBLIC int dns_so_pollfd(struct dns_socket *); + +DNS_PUBLIC int dns_so_poll(struct dns_socket *, int); + +DNS_PUBLIC const struct dns_stat *dns_so_stat(struct dns_socket *); + +DNS_PUBLIC struct dns_trace *dns_so_trace(struct dns_socket *); + +DNS_PUBLIC void dns_so_settrace(struct dns_socket *, struct dns_trace *); + + +/* + * R E S O L V E R I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_resolver; + +DNS_PUBLIC struct dns_resolver *dns_res_open(struct dns_resolv_conf *, struct dns_hosts *hosts, struct dns_hints *, struct dns_cache *, const struct dns_options *, int *); + +DNS_PUBLIC struct dns_resolver *dns_res_stub(const struct dns_options *, int *); + +DNS_PUBLIC void dns_res_reset(struct dns_resolver *); + +DNS_PUBLIC void dns_res_close(struct dns_resolver *); + +DNS_PUBLIC dns_refcount_t dns_res_acquire(struct dns_resolver *); + +DNS_PUBLIC dns_refcount_t dns_res_release(struct dns_resolver *); + +DNS_PUBLIC struct dns_resolver *dns_res_mortal(struct dns_resolver *); + +DNS_PUBLIC int dns_res_submit(struct dns_resolver *, const char *, enum dns_type, enum dns_class); + +DNS_PUBLIC int dns_res_submit2(struct dns_resolver *, const char *, size_t, enum dns_type, enum dns_class); + +DNS_PUBLIC int dns_res_check(struct dns_resolver *); + +DNS_PUBLIC struct dns_packet *dns_res_fetch(struct dns_resolver *, int *); + +DNS_PUBLIC time_t dns_res_elapsed(struct dns_resolver *); + +DNS_PUBLIC void dns_res_clear(struct dns_resolver *); + +DNS_PUBLIC int dns_res_events(struct dns_resolver *); + +DNS_PUBLIC int dns_res_pollfd(struct dns_resolver *); + +DNS_PUBLIC time_t dns_res_timeout(struct dns_resolver *); + +DNS_PUBLIC int dns_res_poll(struct dns_resolver *, int); + +DNS_PUBLIC struct dns_packet *dns_res_query(struct dns_resolver *, const char *, enum dns_type, enum dns_class, int, int *); + +DNS_PUBLIC const struct dns_stat *dns_res_stat(struct dns_resolver *); + +DNS_PUBLIC void dns_res_sethints(struct dns_resolver *, struct dns_hints *); + +DNS_PUBLIC struct dns_trace *dns_res_trace(struct dns_resolver *); + +DNS_PUBLIC void dns_res_settrace(struct dns_resolver *, struct dns_trace *); + + +/* + * A D D R I N F O I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +struct dns_addrinfo; + +DNS_PUBLIC struct dns_addrinfo *dns_ai_open(const char *, const char *, enum dns_type, const struct addrinfo *, struct dns_resolver *, int *); + +DNS_PUBLIC void dns_ai_close(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_nextent(struct addrinfo **, struct dns_addrinfo *); + +DNS_PUBLIC size_t dns_ai_print(void *, size_t, struct addrinfo *, struct dns_addrinfo *); + +DNS_PUBLIC time_t dns_ai_elapsed(struct dns_addrinfo *); + +DNS_PUBLIC void dns_ai_clear(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_events(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_pollfd(struct dns_addrinfo *); + +DNS_PUBLIC time_t dns_ai_timeout(struct dns_addrinfo *); + +DNS_PUBLIC int dns_ai_poll(struct dns_addrinfo *, int); + +DNS_PUBLIC const struct dns_stat *dns_ai_stat(struct dns_addrinfo *); + +DNS_PUBLIC struct dns_trace *dns_ai_trace(struct dns_addrinfo *); + +DNS_PUBLIC void dns_ai_settrace(struct dns_addrinfo *, struct dns_trace *); + + +/* + * Q U E R Y T R A C I N G I N T E R F A C E + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_TRACE_ID_C(n) UINT64_C(n) +typedef uint64_t dns_trace_id_t; + +#define DNS_TRACE_ABI 0x20160803 + +struct dns_trace_event { + enum { + DNS_TE_RES_SUBMIT = 1, + DNS_TE_RES_FETCH = 99, + + DNS_TE_SO_SUBMIT = 100, + DNS_TE_SO_VERIFY, + DNS_TE_SO_FETCH = 199, + + DNS_TE_SYS_CONNECT = 200, + DNS_TE_SYS_SEND, + DNS_TE_SYS_RECV, + } type; + + size_t size; + dns_trace_id_t id; + struct timespec ts; + int abi; + + union { + struct { + char qname[DNS_D_MAXNAME + 1]; + enum dns_type qtype; + enum dns_class qclass; + int error; + } res_submit; + + struct { + int error; + } res_fetch; + + struct { + struct sockaddr_storage haddr; + char hname[DNS_D_MAXNAME + 1]; + int error; + } so_submit; + + struct { + int error; + } so_verify; + + struct { + int error; + } so_fetch; + + struct { + struct sockaddr_storage src, dst; + int socktype; + dns_error_t error; + } sys_connect, sys_send, sys_recv; + }; + + unsigned char data[]; +}; + +static inline size_t dns_te_datasize(const struct dns_trace_event *te) { + size_t n = offsetof(struct dns_trace_event, data); + return (n <= te->size)? te->size - n : 0; +} + +struct dns_trace; + +DNS_PUBLIC int dns_trace_abi(void); + +DNS_PUBLIC struct dns_trace *dns_trace_open(FILE *, dns_error_t *); + +DNS_PUBLIC void dns_trace_close(struct dns_trace *); + +DNS_PUBLIC dns_refcount_t dns_trace_acquire(struct dns_trace *); + +DNS_PUBLIC dns_refcount_t dns_trace_release(struct dns_trace *); + +DNS_PUBLIC dns_trace_id_t dns_trace_id(struct dns_trace *); + +DNS_PUBLIC dns_trace_id_t dns_trace_setid(struct dns_trace *, dns_trace_id_t); + +DNS_PUBLIC struct dns_trace_event *dns_trace_get(struct dns_trace *, struct dns_trace_event **, dns_error_t *); + +DNS_PUBLIC struct dns_trace_event *dns_trace_tag(struct dns_trace *, struct dns_trace_event *); + +DNS_PUBLIC dns_error_t dns_trace_put(struct dns_trace *, const struct dns_trace_event *, const void *, size_t); + +DNS_PUBLIC dns_error_t dns_trace_dump(struct dns_trace *, FILE *); + +DNS_PUBLIC struct dns_trace_event *dns_trace_fget(struct dns_trace_event **, FILE *, dns_error_t *); + +DNS_PUBLIC dns_error_t dns_trace_fput(const struct dns_trace_event *, const void *, size_t, FILE *); + + +/* + * U T I L I T Y I N T E R F A C E S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +DNS_PUBLIC size_t dns_strlcpy(char *, const char *, size_t); + +DNS_PUBLIC size_t dns_strlcat(char *, const char *, size_t); + + +/* + * M A C R O M A G I C S + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define DNS_PP_MIN(a, b) (((a) < (b))? (a) : (b)) +#define DNS_PP_MAX(a, b) (((a) > (b))? (a) : (b)) +#define DNS_PP_NARG_(a, b, c, d, e, f, g, h, i, j, k, N,...) N +#define DNS_PP_NARG(...) DNS_PP_NARG_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define DNS_PP_CALL(F, ...) F(__VA_ARGS__) +#define DNS_PP_PASTE(x, y) x##y +#define DNS_PP_XPASTE(x, y) DNS_PP_PASTE(x, y) +#define DNS_PP_STRINGIFY_(s) #s +#define DNS_PP_STRINGIFY(s) DNS_PP_STRINGIFY_(s) +#define DNS_PP_D1 0 +#define DNS_PP_D2 1 +#define DNS_PP_D3 2 +#define DNS_PP_D4 3 +#define DNS_PP_D5 4 +#define DNS_PP_D6 5 +#define DNS_PP_D7 6 +#define DNS_PP_D8 7 +#define DNS_PP_D9 8 +#define DNS_PP_D10 9 +#define DNS_PP_D11 10 +#define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N) + +#endif /* DNS_H */ diff --git a/dirmngr/http.c b/dirmngr/http.c new file mode 100644 index 0000000..14d60df --- /dev/null +++ b/dirmngr/http.c @@ -0,0 +1,2899 @@ +/* http.c - HTTP protocol handler + * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010, + * 2011 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +/* Simple HTTP client implementation. We try to keep the code as + self-contained as possible. There are some contraints however: + + - estream is required. We now require estream because it provides a + very useful and portable asprintf implementation and the fopencookie + function. + - stpcpy is required + - fixme: list other requirements. + + + - With HTTP_USE_NTBTLS or HTTP_USE_GNUTLS support for https is + provided (this also requires estream). + + - With HTTP_NO_WSASTARTUP the socket initialization is not done + under Windows. This is useful if the socket layer has already + been initialized elsewhere. This also avoids the installation of + an exit handler to cleanup the socket layer. +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else /*!HAVE_W32_SYSTEM*/ +# include +# include +# include +# include +# include +# include +# include +#endif /*!HAVE_W32_SYSTEM*/ + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +# undef USE_NPTH +#endif + +#ifdef USE_NPTH +# include +#endif + +#if defined (HTTP_USE_GNUTLS) && defined (HTTP_USE_NTBTLS) +# error Both, HTTP_USE_GNUTLS and HTTP_USE_NTBTLS, are defined. +#endif + +#ifdef HTTP_USE_NTBTLS +# include +#elif HTTP_USE_GNUTLS +# include +# include +#endif /*HTTP_USE_GNUTLS*/ + +#include /* We need the socket wrapper. */ + +#include "util.h" +#include "i18n.h" +#include "dns-stuff.h" +#include "http.h" + + +#ifdef USE_NPTH +# define my_select(a,b,c,d,e) npth_select ((a), (b), (c), (d), (e)) +# define my_accept(a,b,c) npth_accept ((a), (b), (c)) +#else +# define my_select(a,b,c,d,e) select ((a), (b), (c), (d), (e)) +# define my_accept(a,b,c) accept ((a), (b), (c)) +#endif + +#ifdef HAVE_W32_SYSTEM +#define sock_close(a) closesocket(a) +#else +#define sock_close(a) close(a) +#endif + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif +#ifndef INADDR_NONE /* Slowaris is missing that. */ +#define INADDR_NONE ((unsigned long)(-1)) +#endif /*INADDR_NONE*/ + +#define HTTP_PROXY_ENV "http_proxy" +#define MAX_LINELEN 20000 /* Max. length of a HTTP header line. */ +#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "01234567890@" \ + "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~" + +#if HTTP_USE_NTBTLS +typedef ntbtls_t tls_session_t; +# define USE_TLS 1 +#elif HTTP_USE_GNUTLS +typedef gnutls_session_t tls_session_t; +# define USE_TLS 1 +#else +typedef void *tls_session_t; +# undef USE_TLS +#endif + +static gpg_err_code_t do_parse_uri (parsed_uri_t uri, int only_local_part, + int no_scheme_check, int force_tls); +static gpg_error_t parse_uri (parsed_uri_t *ret_uri, const char *uri, + int no_scheme_check, int force_tls); +static int remove_escapes (char *string); +static int insert_escapes (char *buffer, const char *string, + const char *special); +static uri_tuple_t parse_tuple (char *string); +static gpg_error_t send_request (http_t hd, const char *httphost, + const char *auth,const char *proxy, + const char *srvtag,strlist_t headers); +static char *build_rel_path (parsed_uri_t uri); +static gpg_error_t parse_response (http_t hd); + +static assuan_fd_t connect_server (const char *server, unsigned short port, + unsigned int flags, const char *srvtag, + int *r_host_not_found); +static gpg_error_t write_server (int sock, const char *data, size_t length); + +static gpgrt_ssize_t cookie_read (void *cookie, void *buffer, size_t size); +static gpgrt_ssize_t cookie_write (void *cookie, + const void *buffer, size_t size); +static int cookie_close (void *cookie); + + +/* A socket object used to a allow ref counting of sockets. */ +struct my_socket_s +{ + assuan_fd_t fd; /* The actual socket - shall never be ASSUAN_INVALID_FD. */ + int refcount; /* Number of references to this socket. */ +}; +typedef struct my_socket_s *my_socket_t; + + +/* Cookie function structure and cookie object. */ +static es_cookie_io_functions_t cookie_functions = + { + cookie_read, + cookie_write, + NULL, + cookie_close + }; + +struct cookie_s +{ + /* Socket object or NULL if already closed. */ + my_socket_t sock; + + /* The session object or NULL if not used. */ + http_session_t session; + + /* True if TLS is to be used. */ + int use_tls; + + /* The remaining content length and a flag telling whether to use + the content length. */ + uint64_t content_length; + unsigned int content_length_valid:1; +}; +typedef struct cookie_s *cookie_t; + +/* The session object. */ +struct http_session_s +{ + int refcount; /* Number of references to this object. */ +#ifdef HTTP_USE_GNUTLS + gnutls_certificate_credentials_t certcred; +#endif /*HTTP_USE_GNUTLS*/ +#ifdef USE_TLS + tls_session_t tls_session; + struct { + int done; /* Verifciation has been done. */ + int rc; /* TLS verification return code. */ + unsigned int status; /* Verification status. */ + } verify; + char *servername; /* Malloced server name. */ +#endif /*USE_TLS*/ + /* A callback function to log details of TLS certifciates. */ + void (*cert_log_cb) (http_session_t, gpg_error_t, const char *, + const void **, size_t *); +}; + + +/* An object to save header lines. */ +struct header_s +{ + struct header_s *next; + char *value; /* The value of the header (malloced). */ + char name[1]; /* The name of the header (canonicalized). */ +}; +typedef struct header_s *header_t; + + +/* Our handle context. */ +struct http_context_s +{ + unsigned int status_code; + my_socket_t sock; + unsigned int in_data:1; + unsigned int is_http_0_9:1; + estream_t fp_read; + estream_t fp_write; + void *write_cookie; + void *read_cookie; + http_session_t session; + parsed_uri_t uri; + http_req_t req_type; + char *buffer; /* Line buffer. */ + size_t buffer_size; + unsigned int flags; + header_t headers; /* Received headers. */ +}; + + +/* The global callback for the verification function. */ +static gpg_error_t (*tls_callback) (http_t, http_session_t, int); + +/* The list of files with trusted CA certificates. */ +static strlist_t tls_ca_certlist; + +/* The global callback for net activity. */ +static void (*netactivity_cb)(void); + + + +#if defined(HAVE_W32_SYSTEM) && !defined(HTTP_NO_WSASTARTUP) + +#if GNUPG_MAJOR_VERSION == 1 +#define REQ_WINSOCK_MAJOR 1 +#define REQ_WINSOCK_MINOR 1 +#else +#define REQ_WINSOCK_MAJOR 2 +#define REQ_WINSOCK_MINOR 2 +#endif + + +static void +deinit_sockets (void) +{ + WSACleanup(); +} + +static void +init_sockets (void) +{ + static int initialized; + static WSADATA wsdata; + + if (initialized) + return; + + if ( WSAStartup( MAKEWORD (REQ_WINSOCK_MINOR, REQ_WINSOCK_MAJOR), &wsdata ) ) + { + log_error ("error initializing socket library: ec=%d\n", + (int)WSAGetLastError () ); + return; + } + if ( LOBYTE(wsdata.wVersion) != REQ_WINSOCK_MAJOR + || HIBYTE(wsdata.wVersion) != REQ_WINSOCK_MINOR ) + { + log_error ("socket library version is %x.%x - but %d.%d needed\n", + LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion), + REQ_WINSOCK_MAJOR, REQ_WINSOCK_MINOR); + WSACleanup(); + return; + } + atexit ( deinit_sockets ); + initialized = 1; +} +#endif /*HAVE_W32_SYSTEM && !HTTP_NO_WSASTARTUP*/ + + +/* Create a new socket object. Returns NULL and closes FD if not + enough memory is available. */ +static my_socket_t +_my_socket_new (int lnr, assuan_fd_t fd) +{ + my_socket_t so; + + so = xtrymalloc (sizeof *so); + if (!so) + { + int save_errno = errno; + assuan_sock_close (fd); + gpg_err_set_errno (save_errno); + return NULL; + } + so->fd = fd; + so->refcount = 1; + /* log_debug ("http.c:socket_new(%d): object %p for fd %d created\n", */ + /* lnr, so, so->fd); */ + (void)lnr; + return so; +} +#define my_socket_new(a) _my_socket_new (__LINE__, (a)) + +/* Bump up the reference counter for the socket object SO. */ +static my_socket_t +_my_socket_ref (int lnr, my_socket_t so) +{ + so->refcount++; + /* log_debug ("http.c:socket_ref(%d) object %p for fd %d refcount now %d\n", */ + /* lnr, so, so->fd, so->refcount); */ + (void)lnr; + return so; +} +#define my_socket_ref(a) _my_socket_ref (__LINE__,(a)) + + +/* Bump down the reference counter for the socket object SO. If SO + has no more references, close the socket and release the + object. */ +static void +_my_socket_unref (int lnr, my_socket_t so, + void (*preclose)(void*), void *preclosearg) +{ + if (so) + { + so->refcount--; + /* log_debug ("http.c:socket_unref(%d): object %p for fd %d ref now %d\n", */ + /* lnr, so, so->fd, so->refcount); */ + (void)lnr; + if (!so->refcount) + { + if (preclose) + preclose (preclosearg); + assuan_sock_close (so->fd); + xfree (so); + } + } +} +#define my_socket_unref(a,b,c) _my_socket_unref (__LINE__,(a),(b),(c)) + + +#ifdef HTTP_USE_GNUTLS +static ssize_t +my_gnutls_read (gnutls_transport_ptr_t ptr, void *buffer, size_t size) +{ + my_socket_t sock = ptr; +#if USE_NPTH + return npth_read (sock->fd, buffer, size); +#else + return read (sock->fd, buffer, size); +#endif +} +static ssize_t +my_gnutls_write (gnutls_transport_ptr_t ptr, const void *buffer, size_t size) +{ + my_socket_t sock = ptr; +#if USE_NPTH + return npth_write (sock->fd, buffer, size); +#else + return write (sock->fd, buffer, size); +#endif +} +#endif /*HTTP_USE_GNUTLS*/ + + + + +/* This notification function is called by estream whenever stream is + closed. Its purpose is to mark the closing in the handle so + that a http_close won't accidentally close the estream. The function + http_close removes this notification so that it won't be called if + http_close was used before an es_fclose. */ +static void +fp_onclose_notification (estream_t stream, void *opaque) +{ + http_t hd = opaque; + + if (hd->fp_read && hd->fp_read == stream) + hd->fp_read = NULL; + else if (hd->fp_write && hd->fp_write == stream) + hd->fp_write = NULL; +} + + +/* + * Helper function to create an HTTP header with hex encoded data. A + * new buffer is returned. This buffer is the concatenation of the + * string PREFIX, the hex-encoded DATA of length LEN and the string + * SUFFIX. On error NULL is returned and ERRNO set. + */ +static char * +make_header_line (const char *prefix, const char *suffix, + const void *data, size_t len ) +{ + static unsigned char bintoasc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + const unsigned char *s = data; + char *buffer, *p; + + buffer = xtrymalloc (strlen (prefix) + (len+2)/3*4 + strlen (suffix) + 1); + if (!buffer) + return NULL; + p = stpcpy (buffer, prefix); + for ( ; len >= 3 ; len -= 3, s += 3 ) + { + *p++ = bintoasc[(s[0] >> 2) & 077]; + *p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077]; + *p++ = bintoasc[(((s[1]<<2)&074)|((s[2]>>6)&03))&077]; + *p++ = bintoasc[s[2]&077]; + *p = 0; + } + if ( len == 2 ) + { + *p++ = bintoasc[(s[0] >> 2) & 077]; + *p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077]; + *p++ = bintoasc[((s[1]<<2)&074)]; + *p++ = '='; + } + else if ( len == 1 ) + { + *p++ = bintoasc[(s[0] >> 2) & 077]; + *p++ = bintoasc[(s[0] <<4)&060]; + *p++ = '='; + *p++ = '='; + } + *p = 0; + strcpy (p, suffix); + return buffer; +} + + + + +/* Register a non-standard global TLS callback function. If no + verification is desired a callback needs to be registered which + always returns NULL. */ +void +http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int)) +{ + tls_callback = cb; +} + + +/* Register a CA certificate for future use. The certificate is + expected to be in FNAME. PEM format is assume if FNAME has a + suffix of ".pem". If FNAME is NULL the list of CA files is + removed. */ +void +http_register_tls_ca (const char *fname) +{ + strlist_t sl; + + if (!fname) + { + free_strlist (tls_ca_certlist); + tls_ca_certlist = NULL; + } + else + { + /* Warn if we can't access right now, but register it anyway in + case it becomes accessible later */ + if (access (fname, F_OK)) + log_info (_("can't access '%s': %s\n"), fname, + gpg_strerror (gpg_error_from_syserror())); + sl = add_to_strlist (&tls_ca_certlist, fname); + if (*sl->d && !strcmp (sl->d + strlen (sl->d) - 4, ".pem")) + sl->flags = 1; + } +} + + +/* Register a callback which is called every time the HTTP mode has + * made a successful connection to some server. */ +void +http_register_netactivity_cb (void (*cb)(void)) +{ + netactivity_cb = cb; +} + + +/* Call the netactivity callback if any. */ +static void +notify_netactivity (void) +{ + if (netactivity_cb) + netactivity_cb (); +} + + + +#ifdef USE_TLS +/* Free the TLS session associated with SESS, if any. */ +static void +close_tls_session (http_session_t sess) +{ + if (sess->tls_session) + { +# if HTTP_USE_NTBTLS + /* FIXME!! + Possibly, ntbtls_get_transport and close those streams. + Somehow get SOCK to call my_socket_unref. + */ + ntbtls_release (sess->tls_session); +# elif HTTP_USE_GNUTLS + my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session); + my_socket_unref (sock, NULL, NULL); + gnutls_deinit (sess->tls_session); + if (sess->certcred) + gnutls_certificate_free_credentials (sess->certcred); +# endif /*HTTP_USE_GNUTLS*/ + xfree (sess->servername); + sess->tls_session = NULL; + } +} +#endif /*USE_TLS*/ + + +/* Release a session. Take care not to release it while it is being + used by a http context object. */ +static void +session_unref (int lnr, http_session_t sess) +{ + if (!sess) + return; + + sess->refcount--; + /* log_debug ("http.c:session_unref(%d): sess %p ref now %d\n", */ + /* lnr, sess, sess->refcount); */ + (void)lnr; + if (sess->refcount) + return; + +#ifdef USE_TLS + close_tls_session (sess); +#endif /*USE_TLS*/ + + xfree (sess); +} +#define http_session_unref(a) session_unref (__LINE__, (a)) + +void +http_session_release (http_session_t sess) +{ + http_session_unref (sess); +} + + +/* Create a new session object which is currently used to enable TLS + * support. It may eventually allow reusing existing connections. + * Valid values for FLAGS are: + * HTTP_FLAG_TRUST_DEF - Use the CAs set with http_register_tls_ca + * HTTP_FLAG_TRUST_SYS - Also use the CAs defined by the system + */ +gpg_error_t +http_session_new (http_session_t *r_session, const char *tls_priority, + const char *intended_hostname, unsigned int flags) +{ + gpg_error_t err; + http_session_t sess; + + *r_session = NULL; + + sess = xtrycalloc (1, sizeof *sess); + if (!sess) + return gpg_error_from_syserror (); + sess->refcount = 1; + +#if HTTP_USE_NTBTLS + { + (void)tls_priority; + + /* ntbtls_set_debug (99, NULL, NULL); */ + + err = ntbtls_new (&sess->tls_session, NTBTLS_CLIENT); + if (err) + { + log_error ("ntbtls_new failed: %s\n", gpg_strerror (err)); + goto leave; + } + } +#elif HTTP_USE_GNUTLS + { + const char *errpos; + int rc; + strlist_t sl; + int add_system_cas = !!(flags & HTTP_FLAG_TRUST_SYS); + int is_hkps_pool; + + rc = gnutls_certificate_allocate_credentials (&sess->certcred); + if (rc < 0) + { + log_error ("gnutls_certificate_allocate_credentials failed: %s\n", + gnutls_strerror (rc)); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + is_hkps_pool = (intended_hostname + && !ascii_strcasecmp (intended_hostname, + "hkps.pool.sks-keyservers.net")); + + /* If the user has not specified a CA list, and they are looking + * for the hkps pool from sks-keyservers.net, then default to + * Kristian's certificate authority: */ + if (!tls_ca_certlist && is_hkps_pool) + { + char *pemname = make_filename_try (gnupg_datadir (), + "sks-keyservers.netCA.pem", NULL); + if (!pemname) + { + err = gpg_error_from_syserror (); + log_error ("setting CA from file '%s' failed: %s\n", + pemname, gpg_strerror (err)); + } + else + { + rc = gnutls_certificate_set_x509_trust_file + (sess->certcred, pemname, GNUTLS_X509_FMT_PEM); + if (rc < 0) + log_info ("setting CA from file '%s' failed: %s\n", + pemname, gnutls_strerror (rc)); + xfree (pemname); + } + } + + /* Add configured certificates to the session. */ + if ((flags & HTTP_FLAG_TRUST_DEF)) + { + for (sl = tls_ca_certlist; sl; sl = sl->next) + { + rc = gnutls_certificate_set_x509_trust_file + (sess->certcred, sl->d, + (sl->flags & 1)? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER); + if (rc < 0) + log_info ("setting CA from file '%s' failed: %s\n", + sl->d, gnutls_strerror (rc)); + } + if (!tls_ca_certlist && !is_hkps_pool) + add_system_cas = 1; + } + + /* Add system certificates to the session. */ + if (add_system_cas) + { +#if GNUTLS_VERSION_NUMBER >= 0x030014 + static int shown; + + rc = gnutls_certificate_set_x509_system_trust (sess->certcred); + if (rc < 0) + log_info ("setting system CAs failed: %s\n", gnutls_strerror (rc)); + else if (!shown) + { + shown = 1; + log_info ("number of system provided CAs: %d\n", rc); + } +#endif /* gnutls >= 3.0.20 */ + } + + rc = gnutls_init (&sess->tls_session, GNUTLS_CLIENT); + if (rc < 0) + { + log_error ("gnutls_init failed: %s\n", gnutls_strerror (rc)); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + /* A new session has the transport ptr set to (void*(-1), we need + it to be NULL. */ + gnutls_transport_set_ptr (sess->tls_session, NULL); + + rc = gnutls_priority_set_direct (sess->tls_session, + tls_priority? tls_priority : "NORMAL", + &errpos); + if (rc < 0) + { + log_error ("gnutls_priority_set_direct failed at '%s': %s\n", + errpos, gnutls_strerror (rc)); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + rc = gnutls_credentials_set (sess->tls_session, + GNUTLS_CRD_CERTIFICATE, sess->certcred); + if (rc < 0) + { + log_error ("gnutls_credentials_set failed: %s\n", gnutls_strerror (rc)); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + } +#else /*!HTTP_USE_GNUTLS*/ + { + (void)tls_priority; + } +#endif /*!HTTP_USE_GNUTLS*/ + + /* log_debug ("http.c:session_new: sess %p created\n", sess); */ + err = 0; + +#if USE_TLS + leave: +#endif /*USE_TLS*/ + if (err) + http_session_unref (sess); + else + *r_session = sess; + + return err; +} + + +/* Increment the reference count for session SESS. Passing NULL for + SESS is allowed. */ +http_session_t +http_session_ref (http_session_t sess) +{ + if (sess) + { + sess->refcount++; + /* log_debug ("http.c:session_ref: sess %p ref now %d\n", sess, */ + /* sess->refcount); */ + } + return sess; +} + + +void +http_session_set_log_cb (http_session_t sess, + void (*cb)(http_session_t, gpg_error_t, + const char *hostname, + const void **certs, size_t *certlens)) +{ + sess->cert_log_cb = cb; +} + + + + +/* Start a HTTP retrieval and on success store at R_HD a context + pointer for completing the request and to wait for the response. + If HTTPHOST is not NULL it is used for the Host header instead of a + Host header derived from the URL. */ +gpg_error_t +http_open (http_t *r_hd, http_req_t reqtype, const char *url, + const char *httphost, + const char *auth, unsigned int flags, const char *proxy, + http_session_t session, const char *srvtag, strlist_t headers) +{ + gpg_error_t err; + http_t hd; + + *r_hd = NULL; + + if (!(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST)) + return gpg_err_make (default_errsource, GPG_ERR_INV_ARG); + + /* Create the handle. */ + hd = xtrycalloc (1, sizeof *hd); + if (!hd) + return gpg_error_from_syserror (); + hd->req_type = reqtype; + hd->flags = flags; + hd->session = http_session_ref (session); + + err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS)); + if (!err) + err = send_request (hd, httphost, auth, proxy, srvtag, headers); + + if (err) + { + my_socket_unref (hd->sock, NULL, NULL); + if (hd->fp_read) + es_fclose (hd->fp_read); + if (hd->fp_write) + es_fclose (hd->fp_write); + http_session_unref (hd->session); + xfree (hd); + } + else + *r_hd = hd; + return err; +} + + +/* This function is useful to connect to a generic TCP service using + this http abstraction layer. This has the advantage of providing + service tags and an estream interface. */ +gpg_error_t +http_raw_connect (http_t *r_hd, const char *server, unsigned short port, + unsigned int flags, const char *srvtag) +{ + gpg_error_t err = 0; + http_t hd; + cookie_t cookie; + int hnf; + + *r_hd = NULL; + + if ((flags & HTTP_FLAG_FORCE_TOR)) + { + int mode; + + if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode) + { + log_error ("Tor support is not available\n"); + return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); + } + } + + /* Create the handle. */ + hd = xtrycalloc (1, sizeof *hd); + if (!hd) + return gpg_error_from_syserror (); + hd->req_type = HTTP_REQ_OPAQUE; + hd->flags = flags; + + /* Connect. */ + { + assuan_fd_t sock; + + sock = connect_server (server, port, hd->flags, srvtag, &hnf); + if (sock == ASSUAN_INVALID_FD) + { + err = gpg_err_make (default_errsource, + (hnf? GPG_ERR_UNKNOWN_HOST + : gpg_err_code_from_syserror ())); + xfree (hd); + return err; + } + hd->sock = my_socket_new (sock); + if (!hd->sock) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + xfree (hd); + return err; + } + } + + /* Setup estreams for reading and writing. */ + cookie = xtrycalloc (1, sizeof *cookie); + if (!cookie) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + cookie->sock = my_socket_ref (hd->sock); + hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); + if (!hd->fp_write) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + my_socket_unref (cookie->sock, NULL, NULL); + xfree (cookie); + goto leave; + } + hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE. */ + + cookie = xtrycalloc (1, sizeof *cookie); + if (!cookie) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + cookie->sock = my_socket_ref (hd->sock); + hd->fp_read = es_fopencookie (cookie, "r", cookie_functions); + if (!hd->fp_read) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + my_socket_unref (cookie->sock, NULL, NULL); + xfree (cookie); + goto leave; + } + hd->read_cookie = cookie; /* Cookie now owned by FP_READ. */ + + /* Register close notification to interlock the use of es_fclose in + http_close and in user code. */ + err = es_onclose (hd->fp_write, 1, fp_onclose_notification, hd); + if (!err) + err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd); + + leave: + if (err) + { + if (hd->fp_read) + es_fclose (hd->fp_read); + if (hd->fp_write) + es_fclose (hd->fp_write); + my_socket_unref (hd->sock, NULL, NULL); + xfree (hd); + } + else + *r_hd = hd; + return err; +} + + + + +void +http_start_data (http_t hd) +{ + if (!hd->in_data) + { + es_fputs ("\r\n", hd->fp_write); + es_fflush (hd->fp_write); + hd->in_data = 1; + } + else + es_fflush (hd->fp_write); +} + + +gpg_error_t +http_wait_response (http_t hd) +{ + gpg_error_t err; + cookie_t cookie; + + /* Make sure that we are in the data. */ + http_start_data (hd); + + /* Close the write stream. Note that the reference counted socket + object keeps the actual system socket open. */ + cookie = hd->write_cookie; + if (!cookie) + return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); + + es_fclose (hd->fp_write); + hd->fp_write = NULL; + /* The close has released the cookie and thus we better set it to NULL. */ + hd->write_cookie = NULL; + + /* Shutdown one end of the socket is desired. As per HTTP/1.0 this + is not required but some very old servers (e.g. the original pksd + keyserver didn't worked without it. */ + if ((hd->flags & HTTP_FLAG_SHUTDOWN)) + shutdown (hd->sock->fd, 1); + hd->in_data = 0; + + /* Create a new cookie and a stream for reading. */ + cookie = xtrycalloc (1, sizeof *cookie); + if (!cookie) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + cookie->sock = my_socket_ref (hd->sock); + cookie->session = http_session_ref (hd->session); + cookie->use_tls = hd->uri->use_tls; + + hd->read_cookie = cookie; + hd->fp_read = es_fopencookie (cookie, "r", cookie_functions); + if (!hd->fp_read) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + my_socket_unref (cookie->sock, NULL, NULL); + http_session_unref (cookie->session); + xfree (cookie); + hd->read_cookie = NULL; + return err; + } + + err = parse_response (hd); + + if (!err) + err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd); + + return err; +} + + +/* Convenience function to send a request and wait for the response. + Closes the handle on error. If PROXY is not NULL, this value will + be used as an HTTP proxy and any enabled $http_proxy gets + ignored. */ +gpg_error_t +http_open_document (http_t *r_hd, const char *document, + const char *auth, unsigned int flags, const char *proxy, + http_session_t session, + const char *srvtag, strlist_t headers) +{ + gpg_error_t err; + + err = http_open (r_hd, HTTP_REQ_GET, document, NULL, auth, flags, + proxy, session, srvtag, headers); + if (err) + return err; + + err = http_wait_response (*r_hd); + if (err) + http_close (*r_hd, 0); + + return err; +} + + +void +http_close (http_t hd, int keep_read_stream) +{ + if (!hd) + return; + + /* First remove the close notifications for the streams. */ + if (hd->fp_read) + es_onclose (hd->fp_read, 0, fp_onclose_notification, hd); + if (hd->fp_write) + es_onclose (hd->fp_write, 0, fp_onclose_notification, hd); + + /* Now we can close the streams. */ + my_socket_unref (hd->sock, NULL, NULL); + if (hd->fp_read && !keep_read_stream) + es_fclose (hd->fp_read); + if (hd->fp_write) + es_fclose (hd->fp_write); + http_session_unref (hd->session); + http_release_parsed_uri (hd->uri); + while (hd->headers) + { + header_t tmp = hd->headers->next; + xfree (hd->headers->value); + xfree (hd->headers); + hd->headers = tmp; + } + xfree (hd->buffer); + xfree (hd); +} + + +estream_t +http_get_read_ptr (http_t hd) +{ + return hd?hd->fp_read:NULL; +} + +estream_t +http_get_write_ptr (http_t hd) +{ + return hd?hd->fp_write:NULL; +} + +unsigned int +http_get_status_code (http_t hd) +{ + return hd?hd->status_code:0; +} + +/* Return information pertaining to TLS. If TLS is not in use for HD, + NULL is returned. WHAT is used ask for specific information: + + (NULL) := Only check whether TLS is is use. Returns an + unspecified string if TLS is in use. That string may + even be the empty string. + */ +const char * +http_get_tls_info (http_t hd, const char *what) +{ + (void)what; + + if (!hd) + return NULL; + + return hd->uri->use_tls? "":NULL; +} + + + +static gpg_error_t +parse_uri (parsed_uri_t *ret_uri, const char *uri, + int no_scheme_check, int force_tls) +{ + gpg_err_code_t ec; + + *ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri)); + if (!*ret_uri) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + strcpy ((*ret_uri)->buffer, uri); + ec = do_parse_uri (*ret_uri, 0, no_scheme_check, force_tls); + if (ec) + { + xfree (*ret_uri); + *ret_uri = NULL; + } + return gpg_err_make (default_errsource, ec); +} + + +/* + * Parse an URI and put the result into the newly allocated RET_URI. + * On success the caller must use http_release_parsed_uri() to + * releases the resources. If NO_SCHEME_CHECK is set, the function + * tries to parse the URL in the same way it would do for an HTTP + * style URI. + */ +gpg_error_t +http_parse_uri (parsed_uri_t *ret_uri, const char *uri, + int no_scheme_check) +{ + return parse_uri (ret_uri, uri, no_scheme_check, 0); +} + + +void +http_release_parsed_uri (parsed_uri_t uri) +{ + if (uri) + { + uri_tuple_t r, r2; + + for (r = uri->query; r; r = r2) + { + r2 = r->next; + xfree (r); + } + xfree (uri); + } +} + + +static gpg_err_code_t +do_parse_uri (parsed_uri_t uri, int only_local_part, + int no_scheme_check, int force_tls) +{ + uri_tuple_t *tail; + char *p, *p2, *p3, *pp; + int n; + + p = uri->buffer; + n = strlen (uri->buffer); + + /* Initialize all fields to an empty string or an empty list. */ + uri->scheme = uri->host = uri->path = p + n; + uri->port = 0; + uri->params = uri->query = NULL; + uri->use_tls = 0; + uri->is_http = 0; + uri->opaque = 0; + uri->v6lit = 0; + uri->onion = 0; + + /* A quick validity check. */ + if (strspn (p, VALID_URI_CHARS) != n) + return GPG_ERR_BAD_URI; /* Invalid characters found. */ + + if (!only_local_part) + { + /* Find the scheme. */ + if (!(p2 = strchr (p, ':')) || p2 == p) + return GPG_ERR_BAD_URI; /* No scheme. */ + *p2++ = 0; + for (pp=p; *pp; pp++) + *pp = tolower (*(unsigned char*)pp); + uri->scheme = p; + if (!strcmp (uri->scheme, "http") && !force_tls) + { + uri->port = 80; + uri->is_http = 1; + } + else if (!strcmp (uri->scheme, "hkp") && !force_tls) + { + uri->port = 11371; + uri->is_http = 1; + } +#ifdef USE_TLS + else if (!strcmp (uri->scheme, "https") || !strcmp (uri->scheme,"hkps") + || (force_tls && (!strcmp (uri->scheme, "http") + || !strcmp (uri->scheme,"hkp")))) + { + uri->port = 443; + uri->is_http = 1; + uri->use_tls = 1; + } +#endif /*USE_TLS*/ + else if (!no_scheme_check) + return GPG_ERR_INV_URI; /* Unsupported scheme */ + + p = p2; + + if (*p == '/' && p[1] == '/' ) /* There seems to be a hostname. */ + { + p += 2; + if ((p2 = strchr (p, '/'))) + *p2++ = 0; + + /* Check for username/password encoding */ + if ((p3 = strchr (p, '@'))) + { + uri->auth = p; + *p3++ = '\0'; + p = p3; + } + + for (pp=p; *pp; pp++) + *pp = tolower (*(unsigned char*)pp); + + /* Handle an IPv6 literal */ + if( *p == '[' && (p3=strchr( p, ']' )) ) + { + *p3++ = '\0'; + /* worst case, uri->host should have length 0, points to \0 */ + uri->host = p + 1; + uri->v6lit = 1; + p = p3; + } + else + uri->host = p; + + if ((p3 = strchr (p, ':'))) + { + *p3++ = '\0'; + uri->port = atoi (p3); + } + + if ((n = remove_escapes (uri->host)) < 0) + return GPG_ERR_BAD_URI; + if (n != strlen (uri->host)) + return GPG_ERR_BAD_URI; /* Hostname incudes a Nul. */ + p = p2 ? p2 : NULL; + } + else if (uri->is_http) + return GPG_ERR_INV_URI; /* No Leading double slash for HTTP. */ + else + { + uri->opaque = 1; + uri->path = p; + if (is_onion_address (uri->path)) + uri->onion = 1; + return 0; + } + + } /* End global URI part. */ + + /* Parse the pathname part if any. */ + if (p && *p) + { + /* TODO: Here we have to check params. */ + + /* Do we have a query part? */ + if ((p2 = strchr (p, '?'))) + *p2++ = 0; + + uri->path = p; + if ((n = remove_escapes (p)) < 0) + return GPG_ERR_BAD_URI; + if (n != strlen (p)) + return GPG_ERR_BAD_URI; /* Path includes a Nul. */ + p = p2 ? p2 : NULL; + + /* Parse a query string if any. */ + if (p && *p) + { + tail = &uri->query; + for (;;) + { + uri_tuple_t elem; + + if ((p2 = strchr (p, '&'))) + *p2++ = 0; + if (!(elem = parse_tuple (p))) + return GPG_ERR_BAD_URI; + *tail = elem; + tail = &elem->next; + + if (!p2) + break; /* Ready. */ + p = p2; + } + } + } + + if (is_onion_address (uri->host)) + uri->onion = 1; + + return 0; +} + + +/* + * Remove all %xx escapes; this is done in-place. Returns: New length + * of the string. + */ +static int +remove_escapes (char *string) +{ + int n = 0; + unsigned char *p, *s; + + for (p = s = (unsigned char*)string; *s; s++) + { + if (*s == '%') + { + if (s[1] && s[2] && isxdigit (s[1]) && isxdigit (s[2])) + { + s++; + *p = *s >= '0' && *s <= '9' ? *s - '0' : + *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10; + *p <<= 4; + s++; + *p |= *s >= '0' && *s <= '9' ? *s - '0' : + *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10; + p++; + n++; + } + else + { + *p++ = *s++; + if (*s) + *p++ = *s++; + if (*s) + *p++ = *s++; + if (*s) + *p = 0; + return -1; /* Bad URI. */ + } + } + else + { + *p++ = *s; + n++; + } + } + *p = 0; /* Make sure to keep a string terminator. */ + return n; +} + + +/* If SPECIAL is NULL this function escapes in forms mode. */ +static size_t +escape_data (char *buffer, const void *data, size_t datalen, + const char *special) +{ + int forms = !special; + const unsigned char *s; + size_t n = 0; + + if (forms) + special = "%;?&="; + + for (s = data; datalen; s++, datalen--) + { + if (forms && *s == ' ') + { + if (buffer) + *buffer++ = '+'; + n++; + } + else if (forms && *s == '\n') + { + if (buffer) + memcpy (buffer, "%0D%0A", 6); + n += 6; + } + else if (forms && *s == '\r' && datalen > 1 && s[1] == '\n') + { + if (buffer) + memcpy (buffer, "%0D%0A", 6); + n += 6; + s++; + datalen--; + } + else if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s)) + { + if (buffer) + *(unsigned char*)buffer++ = *s; + n++; + } + else + { + if (buffer) + { + snprintf (buffer, 4, "%%%02X", *s); + buffer += 3; + } + n += 3; + } + } + return n; +} + + +static int +insert_escapes (char *buffer, const char *string, + const char *special) +{ + return escape_data (buffer, string, strlen (string), special); +} + + +/* Allocate a new string from STRING using standard HTTP escaping as + well as escaping of characters given in SPECIALS. A common pattern + for SPECIALS is "%;?&=". However it depends on the needs, for + example "+" and "/: often needs to be escaped too. Returns NULL on + failure and sets ERRNO. If SPECIAL is NULL a dedicated forms + encoding mode is used. */ +char * +http_escape_string (const char *string, const char *specials) +{ + int n; + char *buf; + + n = insert_escapes (NULL, string, specials); + buf = xtrymalloc (n+1); + if (buf) + { + insert_escapes (buf, string, specials); + buf[n] = 0; + } + return buf; +} + +/* Allocate a new string from {DATA,DATALEN} using standard HTTP + escaping as well as escaping of characters given in SPECIALS. A + common pattern for SPECIALS is "%;?&=". However it depends on the + needs, for example "+" and "/: often needs to be escaped too. + Returns NULL on failure and sets ERRNO. If SPECIAL is NULL a + dedicated forms encoding mode is used. */ +char * +http_escape_data (const void *data, size_t datalen, const char *specials) +{ + int n; + char *buf; + + n = escape_data (NULL, data, datalen, specials); + buf = xtrymalloc (n+1); + if (buf) + { + escape_data (buf, data, datalen, specials); + buf[n] = 0; + } + return buf; +} + + +static uri_tuple_t +parse_tuple (char *string) +{ + char *p = string; + char *p2; + int n; + uri_tuple_t tuple; + + if ((p2 = strchr (p, '='))) + *p2++ = 0; + if ((n = remove_escapes (p)) < 0) + return NULL; /* Bad URI. */ + if (n != strlen (p)) + return NULL; /* Name with a Nul in it. */ + tuple = xtrycalloc (1, sizeof *tuple); + if (!tuple) + return NULL; /* Out of core. */ + tuple->name = p; + if (!p2) /* We have only the name, so we assume an empty value string. */ + { + tuple->value = p + strlen (p); + tuple->valuelen = 0; + tuple->no_value = 1; /* Explicitly mark that we have seen no '='. */ + } + else /* Name and value. */ + { + if ((n = remove_escapes (p2)) < 0) + { + xfree (tuple); + return NULL; /* Bad URI. */ + } + tuple->value = p2; + tuple->valuelen = n; + } + return tuple; +} + + +/* Return true if STRING is likely "hostname:port" or only "hostname". */ +static int +is_hostname_port (const char *string) +{ + int colons = 0; + + if (!string || !*string) + return 0; + for (; *string; string++) + { + if (*string == ':') + { + if (colons) + return 0; + if (!string[1]) + return 0; + colons++; + } + else if (!colons && strchr (" \t\f\n\v_@[]/", *string)) + return 0; /* Invalid characters in hostname. */ + else if (colons && !digitp (string)) + return 0; /* Not a digit in the port. */ + } + return 1; +} + + +/* + * Send a HTTP request to the server + * Returns 0 if the request was successful + */ +static gpg_error_t +send_request (http_t hd, const char *httphost, const char *auth, + const char *proxy, const char *srvtag, strlist_t headers) +{ + gpg_error_t err; + const char *server; + char *request, *p; + unsigned short port; + const char *http_proxy = NULL; + char *proxy_authstr = NULL; + char *authstr = NULL; + int sock; + int hnf; + + if (hd->uri->use_tls && !hd->session) + { + log_error ("TLS requested but no session object provided\n"); + return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); + } +#ifdef USE_TLS + if (hd->uri->use_tls && !hd->session->tls_session) + { + log_error ("TLS requested but no GNUTLS context available\n"); + return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); + } +#endif /*USE_TLS*/ + + if ((hd->flags & HTTP_FLAG_FORCE_TOR)) + { + int mode; + + if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode) + { + log_error ("Tor support is not available\n"); + return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); + } + } + + server = *hd->uri->host ? hd->uri->host : "localhost"; + port = hd->uri->port ? hd->uri->port : 80; + + /* Try to use SNI. */ +#ifdef USE_TLS + if (hd->uri->use_tls) + { +# if HTTP_USE_GNUTLS + int rc; +# endif + + xfree (hd->session->servername); + hd->session->servername = xtrystrdup (httphost? httphost : server); + if (!hd->session->servername) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + return err; + } + +# if HTTP_USE_NTBTLS + err = ntbtls_set_hostname (hd->session->tls_session, + hd->session->servername); + if (err) + { + log_info ("ntbtls_set_hostname failed: %s\n", gpg_strerror (err)); + return err; + } +# elif HTTP_USE_GNUTLS + rc = gnutls_server_name_set (hd->session->tls_session, + GNUTLS_NAME_DNS, + hd->session->servername, + strlen (hd->session->servername)); + if (rc < 0) + log_info ("gnutls_server_name_set failed: %s\n", gnutls_strerror (rc)); +# endif /*HTTP_USE_GNUTLS*/ + } +#endif /*USE_TLS*/ + + if ( (proxy && *proxy) + || ( (hd->flags & HTTP_FLAG_TRY_PROXY) + && (http_proxy = getenv (HTTP_PROXY_ENV)) + && *http_proxy )) + { + parsed_uri_t uri; + int save_errno; + + if (proxy) + http_proxy = proxy; + + err = parse_uri (&uri, http_proxy, 0, 0); + if (gpg_err_code (err) == GPG_ERR_INV_URI + && is_hostname_port (http_proxy)) + { + /* Retry assuming a "hostname:port" string. */ + char *tmpname = strconcat ("http://", http_proxy, NULL); + if (tmpname && !parse_uri (&uri, tmpname, 0, 0)) + err = 0; + xfree (tmpname); + } + + if (err) + ; + else if (!strcmp (uri->scheme, "http") || !strcmp (uri->scheme, "socks4")) + ; + else if (!strcmp (uri->scheme, "socks5h")) + err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED); + else + err = gpg_err_make (default_errsource, GPG_ERR_INV_URI); + + if (err) + { + log_error ("invalid HTTP proxy (%s): %s\n", + http_proxy, gpg_strerror (err)); + return gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION); + } + + if (uri->auth) + { + remove_escapes (uri->auth); + proxy_authstr = make_header_line ("Proxy-Authorization: Basic ", + "\r\n", + uri->auth, strlen(uri->auth)); + if (!proxy_authstr) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + http_release_parsed_uri (uri); + return err; + } + } + + sock = connect_server (*uri->host ? uri->host : "localhost", + uri->port ? uri->port : 80, + hd->flags, srvtag, &hnf); + save_errno = errno; + http_release_parsed_uri (uri); + if (sock == ASSUAN_INVALID_FD) + gpg_err_set_errno (save_errno); + } + else + { + sock = connect_server (server, port, hd->flags, srvtag, &hnf); + } + + if (sock == ASSUAN_INVALID_FD) + { + xfree (proxy_authstr); + return gpg_err_make (default_errsource, + (hnf? GPG_ERR_UNKNOWN_HOST + : gpg_err_code_from_syserror ())); + } + hd->sock = my_socket_new (sock); + if (!hd->sock) + { + xfree (proxy_authstr); + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + } + + + +#if HTTP_USE_NTBTLS + if (hd->uri->use_tls) + { + estream_t in, out; + + my_socket_ref (hd->sock); + + in = es_fdopen_nc (hd->sock->fd, "rb"); + if (!in) + { + err = gpg_error_from_syserror (); + xfree (proxy_authstr); + return err; + } + + out = es_fdopen_nc (hd->sock->fd, "wb"); + if (!out) + { + err = gpg_error_from_syserror (); + es_fclose (in); + xfree (proxy_authstr); + return err; + } + + err = ntbtls_set_transport (hd->session->tls_session, in, out); + if (err) + { + log_info ("TLS set_transport failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + xfree (proxy_authstr); + return err; + } + + while ((err = ntbtls_handshake (hd->session->tls_session))) + { + switch (err) + { + default: + log_info ("TLS handshake failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + xfree (proxy_authstr); + return err; + } + } + + hd->session->verify.done = 0; + if (tls_callback) + err = tls_callback (hd, hd->session, 0); + else + err = http_verify_server_credentials (hd->session); + if (err) + { + log_info ("TLS connection authentication failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + xfree (proxy_authstr); + return err; + } + } +#elif HTTP_USE_GNUTLS + if (hd->uri->use_tls) + { + int rc; + + my_socket_ref (hd->sock); + gnutls_transport_set_ptr (hd->session->tls_session, hd->sock); + gnutls_transport_set_pull_function (hd->session->tls_session, + my_gnutls_read); + gnutls_transport_set_push_function (hd->session->tls_session, + my_gnutls_write); + + do + { + rc = gnutls_handshake (hd->session->tls_session); + } + while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN); + if (rc < 0) + { + if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED + || rc == GNUTLS_E_FATAL_ALERT_RECEIVED) + { + gnutls_alert_description_t alertno; + const char *alertstr; + + alertno = gnutls_alert_get (hd->session->tls_session); + alertstr = gnutls_alert_get_name (alertno); + log_info ("TLS handshake failed: %s (alert %d)\n", + alertstr, (int)alertno); + if (alertno == GNUTLS_A_UNRECOGNIZED_NAME && server) + log_info (" (sent server name '%s')\n", server); + } + else + log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc)); + xfree (proxy_authstr); + return gpg_err_make (default_errsource, GPG_ERR_NETWORK); + } + + hd->session->verify.done = 0; + if (tls_callback) + err = tls_callback (hd, hd->session, 0); + else + err = http_verify_server_credentials (hd->session); + if (err) + { + log_info ("TLS connection authentication failed: %s\n", + gpg_strerror (err)); + xfree (proxy_authstr); + return err; + } + } +#endif /*HTTP_USE_GNUTLS*/ + + if (auth || hd->uri->auth) + { + char *myauth; + + if (auth) + { + myauth = xtrystrdup (auth); + if (!myauth) + { + xfree (proxy_authstr); + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + } + remove_escapes (myauth); + } + else + { + remove_escapes (hd->uri->auth); + myauth = hd->uri->auth; + } + + authstr = make_header_line ("Authorization: Basic ", "\r\n", + myauth, strlen (myauth)); + if (auth) + xfree (myauth); + + if (!authstr) + { + xfree (proxy_authstr); + return gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + } + } + + p = build_rel_path (hd->uri); + if (!p) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + if (http_proxy && *http_proxy) + { + request = es_bsprintf + ("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s", + hd->req_type == HTTP_REQ_GET ? "GET" : + hd->req_type == HTTP_REQ_HEAD ? "HEAD" : + hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", + hd->uri->use_tls? "https" : "http", + httphost? httphost : server, + port, *p == '/' ? "" : "/", p, + authstr ? authstr : "", + proxy_authstr ? proxy_authstr : ""); + } + else + { + char portstr[35]; + + if (port == 80) + *portstr = 0; + else + snprintf (portstr, sizeof portstr, ":%u", port); + + request = es_bsprintf + ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s", + hd->req_type == HTTP_REQ_GET ? "GET" : + hd->req_type == HTTP_REQ_HEAD ? "HEAD" : + hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS", + *p == '/' ? "" : "/", p, + httphost? httphost : server, + portstr, + authstr? authstr:""); + } + xfree (p); + if (!request) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + xfree (authstr); + xfree (proxy_authstr); + return err; + } + + /* log_debug ("request:\n%s\nEND request\n", request); */ + + /* First setup estream so that we can write even the first line + using estream. This is also required for the sake of gnutls. */ + { + cookie_t cookie; + + cookie = xtrycalloc (1, sizeof *cookie); + if (!cookie) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + cookie->sock = my_socket_ref (hd->sock); + hd->write_cookie = cookie; + cookie->use_tls = hd->uri->use_tls; + cookie->session = http_session_ref (hd->session); + + hd->fp_write = es_fopencookie (cookie, "w", cookie_functions); + if (!hd->fp_write) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + my_socket_unref (cookie->sock, NULL, NULL); + xfree (cookie); + hd->write_cookie = NULL; + } + else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write)) + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + else + err = 0; + + if (!err) + { + for (;headers; headers=headers->next) + { + if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write)) + || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write))) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + break; + } + } + } + } + + leave: + es_free (request); + xfree (authstr); + xfree (proxy_authstr); + + return err; +} + + +/* + * Build the relative path from the parsed URI. Minimal + * implementation. May return NULL in case of memory failure; errno + * is then set accordingly. + */ +static char * +build_rel_path (parsed_uri_t uri) +{ + uri_tuple_t r; + char *rel_path, *p; + int n; + + /* Count the needed space. */ + n = insert_escapes (NULL, uri->path, "%;?&"); + /* TODO: build params. */ + for (r = uri->query; r; r = r->next) + { + n++; /* '?'/'&' */ + n += insert_escapes (NULL, r->name, "%;?&="); + if (!r->no_value) + { + n++; /* '=' */ + n += insert_escapes (NULL, r->value, "%;?&="); + } + } + n++; + + /* Now allocate and copy. */ + p = rel_path = xtrymalloc (n); + if (!p) + return NULL; + n = insert_escapes (p, uri->path, "%;?&"); + p += n; + /* TODO: add params. */ + for (r = uri->query; r; r = r->next) + { + *p++ = r == uri->query ? '?' : '&'; + n = insert_escapes (p, r->name, "%;?&="); + p += n; + if (!r->no_value) + { + *p++ = '='; + /* TODO: Use valuelen. */ + n = insert_escapes (p, r->value, "%;?&="); + p += n; + } + } + *p = 0; + return rel_path; +} + + +/* Transform a header name into a standard capitalized format; e.g. + "Content-Type". Conversion stops at the colon. As usual we don't + use the localized versions of ctype.h. */ +static void +capitalize_header_name (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'; + } +} + + +/* Store an HTTP header line in LINE away. Line continuation is + supported as well as merging of headers with the same name. This + function may modify LINE. */ +static gpg_err_code_t +store_header (http_t hd, char *line) +{ + size_t n; + char *p, *value; + header_t h; + + n = strlen (line); + if (n && line[n-1] == '\n') + { + line[--n] = 0; + if (n && line[n-1] == '\r') + line[--n] = 0; + } + if (!n) /* we are never called to hit this. */ + return GPG_ERR_BUG; + if (*line == ' ' || *line == '\t') + { + /* Continuation. This won't happen too often as it is not + recommended. We use a straightforward implementaion. */ + if (!hd->headers) + return GPG_ERR_PROTOCOL_VIOLATION; + n += strlen (hd->headers->value); + p = xtrymalloc (n+1); + if (!p) + return gpg_err_code_from_syserror (); + strcpy (stpcpy (p, hd->headers->value), line); + xfree (hd->headers->value); + hd->headers->value = p; + return 0; + } + + capitalize_header_name (line); + p = strchr (line, ':'); + if (!p) + return GPG_ERR_PROTOCOL_VIOLATION; + *p++ = 0; + while (*p == ' ' || *p == '\t') + p++; + value = p; + + for (h=hd->headers; h; h = h->next) + if ( !strcmp (h->name, line) ) + break; + if (h) + { + /* We have already seen a line with that name. Thus we assume + it is a comma separated list and merge them. */ + p = xtrymalloc (strlen (h->value) + 1 + strlen (value)+ 1); + if (!p) + return gpg_err_code_from_syserror (); + strcpy (stpcpy (stpcpy (p, h->value), ","), value); + xfree (h->value); + h->value = p; + return 0; + } + + /* Append a new header. */ + h = xtrymalloc (sizeof *h + strlen (line)); + if (!h) + return gpg_err_code_from_syserror (); + strcpy (h->name, line); + h->value = xtrymalloc (strlen (value)+1); + if (!h->value) + { + xfree (h); + return gpg_err_code_from_syserror (); + } + strcpy (h->value, value); + h->next = hd->headers; + hd->headers = h; + + return 0; +} + + +/* Return the header NAME from the last response. The returned value + is valid as along as HD has not been closed and no other request + has been send. If the header was not found, NULL is returned. NAME + must be canonicalized, that is the first letter of each dash + delimited part must be uppercase and all other letters lowercase. */ +const char * +http_get_header (http_t hd, const char *name) +{ + header_t h; + + for (h=hd->headers; h; h = h->next) + if ( !strcmp (h->name, name) ) + return h->value; + return NULL; +} + + +/* Return a newly allocated and NULL terminated array with pointers to + header names. The array must be released with xfree() and its + content is only values as long as no other request has been + send. */ +const char ** +http_get_header_names (http_t hd) +{ + const char **array; + size_t n; + header_t h; + + for (n=0, h = hd->headers; h; h = h->next) + n++; + array = xtrycalloc (n+1, sizeof *array); + if (array) + { + for (n=0, h = hd->headers; h; h = h->next) + array[n++] = h->name; + } + + return array; +} + + +/* + * Parse the response from a server. + * Returns: Errorcode and sets some files in the handle + */ +static gpg_err_code_t +parse_response (http_t hd) +{ + char *line, *p, *p2; + size_t maxlen, len; + cookie_t cookie = hd->read_cookie; + const char *s; + + /* Delete old header lines. */ + while (hd->headers) + { + header_t tmp = hd->headers->next; + xfree (hd->headers->value); + xfree (hd->headers); + hd->headers = tmp; + } + + /* Wait for the status line. */ + do + { + maxlen = MAX_LINELEN; + len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen); + line = hd->buffer; + if (!line) + return gpg_err_code_from_syserror (); /* Out of core. */ + if (!maxlen) + return GPG_ERR_TRUNCATED; /* Line has been truncated. */ + if (!len) + return GPG_ERR_EOF; + + if ((hd->flags & HTTP_FLAG_LOG_RESP)) + log_info ("RESP: '%.*s'\n", + (int)strlen(line)-(*line&&line[1]?2:0),line); + } + while (!*line); + + if ((p = strchr (line, '/'))) + *p++ = 0; + if (!p || strcmp (line, "HTTP")) + return 0; /* Assume http 0.9. */ + + if ((p2 = strpbrk (p, " \t"))) + { + *p2++ = 0; + p2 += strspn (p2, " \t"); + } + if (!p2) + return 0; /* Also assume http 0.9. */ + p = p2; + /* TODO: Add HTTP version number check. */ + if ((p2 = strpbrk (p, " \t"))) + *p2++ = 0; + if (!isdigit ((unsigned int)p[0]) || !isdigit ((unsigned int)p[1]) + || !isdigit ((unsigned int)p[2]) || p[3]) + { + /* Malformed HTTP status code - assume http 0.9. */ + hd->is_http_0_9 = 1; + hd->status_code = 200; + return 0; + } + hd->status_code = atoi (p); + + /* Skip all the header lines and wait for the empty line. */ + do + { + maxlen = MAX_LINELEN; + len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen); + line = hd->buffer; + if (!line) + return gpg_err_code_from_syserror (); /* Out of core. */ + /* Note, that we can silently ignore truncated lines. */ + if (!len) + return GPG_ERR_EOF; + /* Trim line endings of empty lines. */ + if ((*line == '\r' && line[1] == '\n') || *line == '\n') + *line = 0; + if ((hd->flags & HTTP_FLAG_LOG_RESP)) + log_info ("RESP: '%.*s'\n", + (int)strlen(line)-(*line&&line[1]?2:0),line); + if (*line) + { + gpg_err_code_t ec = store_header (hd, line); + if (ec) + return ec; + } + } + while (len && *line); + + cookie->content_length_valid = 0; + if (!(hd->flags & HTTP_FLAG_IGNORE_CL)) + { + s = http_get_header (hd, "Content-Length"); + if (s) + { + cookie->content_length_valid = 1; + cookie->content_length = string_to_u64 (s); + } + } + + return 0; +} + +#if 0 +static int +start_server () +{ + struct sockaddr_in mya; + struct sockaddr_in peer; + int fd, client; + fd_set rfds; + int addrlen; + int i; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) + { + log_error ("socket() failed: %s\n", strerror (errno)); + return -1; + } + i = 1; + if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (byte *) & i, sizeof (i))) + log_info ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno)); + + mya.sin_family = AF_INET; + memset (&mya.sin_addr, 0, sizeof (mya.sin_addr)); + mya.sin_port = htons (11371); + + if (bind (fd, (struct sockaddr *) &mya, sizeof (mya))) + { + log_error ("bind to port 11371 failed: %s\n", strerror (errno)); + sock_close (fd); + return -1; + } + + if (listen (fd, 5)) + { + log_error ("listen failed: %s\n", strerror (errno)); + sock_close (fd); + return -1; + } + + for (;;) + { + FD_ZERO (&rfds); + FD_SET (fd, &rfds); + + if (my_select (fd + 1, &rfds, NULL, NULL, NULL) <= 0) + continue; /* ignore any errors */ + + if (!FD_ISSET (fd, &rfds)) + continue; + + addrlen = sizeof peer; + client = my_accept (fd, (struct sockaddr *) &peer, &addrlen); + if (client == -1) + continue; /* oops */ + + log_info ("connect from %s\n", inet_ntoa (peer.sin_addr)); + + fflush (stdout); + fflush (stderr); + if (!fork ()) + { + int c; + FILE *fp; + + fp = fdopen (client, "r"); + while ((c = getc (fp)) != EOF) + putchar (c); + fclose (fp); + exit (0); + } + sock_close (client); + } + + + return 0; +} +#endif + +/* Actually connect to a server. Returns the file descriptor or -1 on + error. ERRNO is set on error. */ +static assuan_fd_t +connect_server (const char *server, unsigned short port, + unsigned int flags, const char *srvtag, int *r_host_not_found) +{ + gpg_error_t err; + assuan_fd_t sock = ASSUAN_INVALID_FD; + unsigned int srvcount = 0; + int hostfound = 0; + int anyhostaddr = 0; + int srv, connected; + int last_errno = 0; + struct srventry *serverlist = NULL; + int ret; + + *r_host_not_found = 0; +#if defined(HAVE_W32_SYSTEM) && !defined(HTTP_NO_WSASTARTUP) + init_sockets (); +#endif /*Windows*/ + + /* Onion addresses require special treatment. */ + if (is_onion_address (server)) + { +#ifdef ASSUAN_SOCK_TOR + + sock = assuan_sock_connect_byname (server, port, 0, NULL, + ASSUAN_SOCK_TOR); + if (sock == ASSUAN_INVALID_FD) + { + if (errno == EHOSTUNREACH) + *r_host_not_found = 1; + log_error ("can't connect to '%s': %s\n", server, strerror (errno)); + } + else + notify_netactivity (); + return sock; + +#else /*!ASSUAN_SOCK_TOR*/ + + gpg_err_set_errno (ENETUNREACH); + return -1; /* Out of core. */ + +#endif /*!HASSUAN_SOCK_TOR*/ + } + + /* Do the SRV thing */ + if (srvtag) + { + /* We're using SRV, so append the tags. */ + if (1 + strlen (srvtag) + 6 + strlen (server) + 1 + <= DIMof (struct srventry, target)) + { + char *srvname = xtrymalloc (DIMof (struct srventry, target)); + + if (!srvname) /* Out of core */ + { + serverlist = NULL; + srvcount = 0; + } + else + { + stpcpy (stpcpy (stpcpy (stpcpy (srvname,"_"), srvtag), + "._tcp."), server); + err = get_dns_srv (srvname, &serverlist, &srvcount); + if (err) + log_info ("getting SRV '%s' failed: %s\n", + srvname, gpg_strerror (err)); + xfree (srvname); + /* Note that on error SRVCOUNT is zero. */ + } + } + } + + if (!serverlist) + { + /* Either we're not using SRV, or the SRV lookup failed. Make + up a fake SRV record. */ + serverlist = xtrycalloc (1, sizeof *serverlist); + if (!serverlist) + return -1; /* Out of core. */ + serverlist->port = port; + strncpy (serverlist->target, server, DIMof (struct srventry, target)); + serverlist->target[DIMof (struct srventry, target)-1] = '\0'; + srvcount = 1; + } + + connected = 0; + for (srv=0; srv < srvcount && !connected; srv++) + { + dns_addrinfo_t aibuf, ai; + + err = resolve_dns_name (serverlist[srv].target, port, 0, SOCK_STREAM, + &aibuf, NULL); + if (err) + { + log_info ("resolving '%s' failed: %s\n", + serverlist[srv].target, gpg_strerror (err)); + continue; /* Not found - try next one. */ + } + hostfound = 1; + + for (ai = aibuf; ai && !connected; ai = ai->next) + { + if (ai->family == AF_INET && (flags & HTTP_FLAG_IGNORE_IPv4)) + continue; + if (ai->family == AF_INET6 && (flags & HTTP_FLAG_IGNORE_IPv6)) + continue; + + if (sock != ASSUAN_INVALID_FD) + assuan_sock_close (sock); + sock = assuan_sock_new (ai->family, ai->socktype, ai->protocol); + if (sock == ASSUAN_INVALID_FD) + { + int save_errno = errno; + log_error ("error creating socket: %s\n", strerror (errno)); + free_dns_addrinfo (aibuf); + xfree (serverlist); + errno = save_errno; + return ASSUAN_INVALID_FD; + } + + anyhostaddr = 1; + ret = assuan_sock_connect (sock, ai->addr, ai->addrlen); + if (ret) + last_errno = errno; + else + { + connected = 1; + notify_netactivity (); + } + } + free_dns_addrinfo (aibuf); + } + + xfree (serverlist); + + if (!connected) + { + if (!hostfound) + log_error ("can't connect to '%s': %s\n", + server, "host not found"); + else if (!anyhostaddr) + log_error ("can't connect to '%s': %s\n", + server, "no IP address for host"); + else + { +#ifdef HAVE_W32_SYSTEM + log_error ("can't connect to '%s': ec=%d\n", + server, (int)WSAGetLastError()); +#else + log_error ("can't connect to '%s': %s\n", + server, strerror (last_errno)); +#endif + } + if (!hostfound || (hostfound && !anyhostaddr)) + *r_host_not_found = 1; + if (sock != ASSUAN_INVALID_FD) + assuan_sock_close (sock); + gpg_err_set_errno (last_errno); + return ASSUAN_INVALID_FD; + } + return sock; +} + + +static gpg_error_t +write_server (int sock, const char *data, size_t length) +{ + int nleft; + int nwritten; + + nleft = length; + while (nleft > 0) + { +#if defined(HAVE_W32_SYSTEM) +# if defined(USE_NPTH) + npth_unprotect (); +# endif + nwritten = send (sock, data, nleft, 0); +# if defined(USE_NPTH) + npth_protect (); +# endif + if ( nwritten == SOCKET_ERROR ) + { + log_info ("network write failed: ec=%d\n", (int)WSAGetLastError ()); + return gpg_error (GPG_ERR_NETWORK); + } +#else /*!HAVE_W32_SYSTEM*/ +# ifdef USE_NPTH + nwritten = npth_write (sock, data, nleft); +# else + nwritten = write (sock, data, nleft); +# endif + if (nwritten == -1) + { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 50000; + my_select (0, NULL, NULL, NULL, &tv); + continue; + } + log_info ("network write failed: %s\n", strerror (errno)); + return gpg_error_from_syserror (); + } +#endif /*!HAVE_W32_SYSTEM*/ + nleft -= nwritten; + data += nwritten; + } + + return 0; +} + + + +/* Read handler for estream. */ +static gpgrt_ssize_t +cookie_read (void *cookie, void *buffer, size_t size) +{ + cookie_t c = cookie; + int nread; + + if (c->content_length_valid) + { + if (!c->content_length) + return 0; /* EOF */ + if (c->content_length < size) + size = c->content_length; + } + +#if HTTP_USE_NTBTLS + if (c->use_tls && c->session && c->session->tls_session) + { + estream_t in, out; + + ntbtls_get_stream (c->session->tls_session, &in, &out); + nread = es_fread (buffer, 1, size, in); + log_debug ("TLS network read: %d/%u\n", nread, size); + } + else +#elif HTTP_USE_GNUTLS + if (c->use_tls && c->session && c->session->tls_session) + { + again: + nread = gnutls_record_recv (c->session->tls_session, buffer, size); + if (nread < 0) + { + if (nread == GNUTLS_E_INTERRUPTED) + goto again; + if (nread == GNUTLS_E_AGAIN) + { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 50000; + my_select (0, NULL, NULL, NULL, &tv); + goto again; + } + if (nread == GNUTLS_E_REHANDSHAKE) + goto again; /* A client is allowed to just ignore this request. */ + if (nread == GNUTLS_E_PREMATURE_TERMINATION) + { + /* The server terminated the connection. Close the TLS + session, and indicate EOF using a short read. */ + close_tls_session (c->session); + return 0; + } + log_info ("TLS network read failed: %s\n", gnutls_strerror (nread)); + gpg_err_set_errno (EIO); + return -1; + } + } + else +#endif /*HTTP_USE_GNUTLS*/ + { + do + { +#ifdef HAVE_W32_SYSTEM + /* Under Windows we need to use recv for a socket. */ +# if defined(USE_NPTH) + npth_unprotect (); +# endif + nread = recv (c->sock->fd, buffer, size, 0); +# if defined(USE_NPTH) + npth_protect (); +# endif + +#else /*!HAVE_W32_SYSTEM*/ + +# ifdef USE_NPTH + nread = npth_read (c->sock->fd, buffer, size); +# else + nread = read (c->sock->fd, buffer, size); +# endif + +#endif /*!HAVE_W32_SYSTEM*/ + } + while (nread == -1 && errno == EINTR); + } + + if (c->content_length_valid && nread > 0) + { + if (nread < c->content_length) + c->content_length -= nread; + else + c->content_length = 0; + } + + return (gpgrt_ssize_t)nread; +} + +/* Write handler for estream. */ +static gpgrt_ssize_t +cookie_write (void *cookie, const void *buffer_arg, size_t size) +{ + const char *buffer = buffer_arg; + cookie_t c = cookie; + int nwritten = 0; + +#if HTTP_USE_NTBTLS + if (c->use_tls && c->session && c->session->tls_session) + { + estream_t in, out; + + ntbtls_get_stream (c->session->tls_session, &in, &out); + if (size == 0) + es_fflush (out); + else + nwritten = es_fwrite (buffer, 1, size, out); + log_debug ("TLS network write: %d/%u\n", nwritten, size); + } + else +#elif HTTP_USE_GNUTLS + if (c->use_tls && c->session && c->session->tls_session) + { + int nleft = size; + while (nleft > 0) + { + nwritten = gnutls_record_send (c->session->tls_session, + buffer, nleft); + if (nwritten <= 0) + { + if (nwritten == GNUTLS_E_INTERRUPTED) + continue; + if (nwritten == GNUTLS_E_AGAIN) + { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 50000; + my_select (0, NULL, NULL, NULL, &tv); + continue; + } + log_info ("TLS network write failed: %s\n", + gnutls_strerror (nwritten)); + gpg_err_set_errno (EIO); + return -1; + } + nleft -= nwritten; + buffer += nwritten; + } + } + else +#endif /*HTTP_USE_GNUTLS*/ + { + if ( write_server (c->sock->fd, buffer, size) ) + { + gpg_err_set_errno (EIO); + nwritten = -1; + } + else + nwritten = size; + } + + return (gpgrt_ssize_t)nwritten; +} + + +#ifdef HTTP_USE_GNUTLS +/* Wrapper for gnutls_bye used by my_socket_unref. */ +static void +send_gnutls_bye (void *opaque) +{ + tls_session_t tls_session = opaque; + int ret; + + again: + do + ret = gnutls_bye (tls_session, GNUTLS_SHUT_RDWR); + while (ret == GNUTLS_E_INTERRUPTED); + if (ret == GNUTLS_E_AGAIN) + { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 50000; + my_select (0, NULL, NULL, NULL, &tv); + goto again; + } +} +#endif /*HTTP_USE_GNUTLS*/ + +/* Close handler for estream. */ +static int +cookie_close (void *cookie) +{ + cookie_t c = cookie; + + if (!c) + return 0; + +#if HTTP_USE_NTBTLS + if (c->use_tls && c->session && c->session->tls_session) + { + /* FIXME!! Possibly call ntbtls_close_notify for close + of write stream. */ + my_socket_unref (c->sock, NULL, NULL); + } + else +#elif HTTP_USE_GNUTLS + if (c->use_tls && c->session && c->session->tls_session) + my_socket_unref (c->sock, send_gnutls_bye, c->session->tls_session); + else +#endif /*HTTP_USE_GNUTLS*/ + if (c->sock) + my_socket_unref (c->sock, NULL, NULL); + + if (c->session) + http_session_unref (c->session); + xfree (c); + return 0; +} + + + + +/* Verify the credentials of the server. Returns 0 on success and + store the result in the session object. */ +gpg_error_t +http_verify_server_credentials (http_session_t sess) +{ +#if HTTP_USE_NTBTLS + (void)sess; + return 0; /* FIXME!! */ +#elif HTTP_USE_GNUTLS + static const char const errprefix[] = "TLS verification of peer failed"; + int rc; + unsigned int status; + const char *hostname; + const gnutls_datum_t *certlist; + unsigned int certlistlen; + gnutls_x509_crt_t cert; + gpg_error_t err = 0; + + sess->verify.done = 1; + sess->verify.status = 0; + sess->verify.rc = GNUTLS_E_CERTIFICATE_ERROR; + + if (gnutls_certificate_type_get (sess->tls_session) != GNUTLS_CRT_X509) + { + log_error ("%s: %s\n", errprefix, "not an X.509 certificate"); + sess->verify.rc = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + return gpg_error (GPG_ERR_GENERAL); + } + + rc = gnutls_certificate_verify_peers2 (sess->tls_session, &status); + if (rc) + { + log_error ("%s: %s\n", errprefix, gnutls_strerror (rc)); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + } + else if (status) + { + log_error ("%s: status=0x%04x\n", errprefix, status); +#if GNUTLS_VERSION_NUMBER >= 0x030104 + { + gnutls_datum_t statusdat; + + if (!gnutls_certificate_verification_status_print + (status, GNUTLS_CRT_X509, &statusdat, 0)) + { + log_info ("%s: %s\n", errprefix, statusdat.data); + gnutls_free (statusdat.data); + } + } +#endif /*gnutls >= 3.1.4*/ + + sess->verify.status = status; + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + } + + hostname = sess->servername; + if (!hostname || !strchr (hostname, '.')) + { + log_error ("%s: %s\n", errprefix, "hostname missing"); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + } + + certlist = gnutls_certificate_get_peers (sess->tls_session, &certlistlen); + if (!certlistlen) + { + log_error ("%s: %s\n", errprefix, "server did not send a certificate"); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + + /* Need to stop here. */ + if (err) + return err; + } + + rc = gnutls_x509_crt_init (&cert); + if (rc < 0) + { + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + if (err) + return err; + } + + rc = gnutls_x509_crt_import (cert, &certlist[0], GNUTLS_X509_FMT_DER); + if (rc < 0) + { + log_error ("%s: %s: %s\n", errprefix, "error importing certificate", + gnutls_strerror (rc)); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + } + + if (!gnutls_x509_crt_check_hostname (cert, hostname)) + { + log_error ("%s: %s\n", errprefix, "hostname does not match"); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + } + + gnutls_x509_crt_deinit (cert); + + if (!err) + sess->verify.rc = 0; + + if (sess->cert_log_cb) + { + const void *bufarr[10]; + size_t buflenarr[10]; + size_t n; + + for (n = 0; n < certlistlen && n < DIM (bufarr)-1; n++) + { + bufarr[n] = certlist[n].data; + buflenarr[n] = certlist[n].size; + } + bufarr[n] = NULL; + buflenarr[n] = 0; + sess->cert_log_cb (sess, err, hostname, bufarr, buflenarr); + } + + return err; +#else /*!HTTP_USE_GNUTLS*/ + (void)sess; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + +/* Return the first query variable with the specified key. If there + is no such variable, return NULL. */ +struct uri_tuple_s * +uri_query_lookup (parsed_uri_t uri, const char *key) +{ + struct uri_tuple_s *t; + + for (t = uri->query; t; t = t->next) + if (strcmp (t->name, key) == 0) + return t; + + return NULL; +} diff --git a/dirmngr/http.h b/dirmngr/http.h new file mode 100644 index 0000000..2a36fda --- /dev/null +++ b/dirmngr/http.h @@ -0,0 +1,163 @@ +/* http.h - HTTP protocol handler + * Copyright (C) 1999, 2000, 2001, 2003, 2006, + * 2010 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ +#ifndef GNUPG_COMMON_HTTP_H +#define GNUPG_COMMON_HTTP_H + +#include + +struct uri_tuple_s +{ + struct uri_tuple_s *next; + const char *name; /* A pointer into name. */ + char *value; /* A pointer to value (a Nul is always appended). */ + size_t valuelen; /* The real length of the value; we need it + because the value may contain embedded Nuls. */ + int no_value; /* True if no value has been given in the URL. */ +}; +typedef struct uri_tuple_s *uri_tuple_t; + +struct parsed_uri_s +{ + /* All these pointers point into BUFFER; most stuff is not escaped. */ + char *scheme; /* Pointer to the scheme string (always lowercase). */ + unsigned int is_http:1; /* This is a HTTP style URI. */ + unsigned int use_tls:1; /* Whether TLS should be used. */ + unsigned int opaque:1;/* Unknown scheme; PATH has the rest. */ + unsigned int v6lit:1; /* Host was given as a literal v6 address. */ + unsigned int onion:1; /* .onion address given. */ + char *auth; /* username/password for basic auth. */ + char *host; /* Host (converted to lowercase). */ + unsigned short port; /* Port (always set if the host is set). */ + char *path; /* Path. */ + uri_tuple_t params; /* ";xxxxx" */ + uri_tuple_t query; /* "?xxx=yyy" */ + char buffer[1]; /* Buffer which holds a (modified) copy of the URI. */ +}; +typedef struct parsed_uri_s *parsed_uri_t; + +struct uri_tuple_s *uri_query_lookup (parsed_uri_t uri, const char *key); + +typedef enum + { + HTTP_REQ_GET = 1, + HTTP_REQ_HEAD = 2, + HTTP_REQ_POST = 3, + HTTP_REQ_OPAQUE = 4 /* Internal use. */ + } +http_req_t; + +/* We put the flag values into an enum, so that gdb can display them. */ +enum + { + HTTP_FLAG_TRY_PROXY = 1, /* Try to use a proxy. */ + HTTP_FLAG_SHUTDOWN = 2, /* Close sending end after the request. */ + HTTP_FLAG_FORCE_TOR = 4, /* Force a TOR connection. */ + HTTP_FLAG_LOG_RESP = 8, /* Log the server response. */ + HTTP_FLAG_FORCE_TLS = 16, /* Force the use of TLS. */ + HTTP_FLAG_IGNORE_CL = 32, /* Ignore content-length. */ + HTTP_FLAG_IGNORE_IPv4 = 64, /* Do not use IPv4. */ + HTTP_FLAG_IGNORE_IPv6 = 128, /* Do not use IPv6. */ + HTTP_FLAG_TRUST_DEF = 256, /* Use the default CAs. */ + HTTP_FLAG_TRUST_SYS = 512 /* Also use the system defined CAs. */ + }; + + +struct http_session_s; +typedef struct http_session_s *http_session_t; + +struct http_context_s; +typedef struct http_context_s *http_t; + +void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int)); +void http_register_tls_ca (const char *fname); +void http_register_netactivity_cb (void (*cb)(void)); + + +gpg_error_t http_session_new (http_session_t *r_session, + const char *tls_priority, + const char *intended_hostname, + unsigned int flags); +http_session_t http_session_ref (http_session_t sess); +void http_session_release (http_session_t sess); + +void http_session_set_log_cb (http_session_t sess, + void (*cb)(http_session_t, gpg_error_t, + const char *, + const void **, size_t *)); + + +gpg_error_t http_parse_uri (parsed_uri_t *ret_uri, const char *uri, + int no_scheme_check); + +void http_release_parsed_uri (parsed_uri_t uri); + +gpg_error_t http_raw_connect (http_t *r_hd, + const char *server, unsigned short port, + unsigned int flags, const char *srvtag); + +gpg_error_t http_open (http_t *r_hd, http_req_t reqtype, + const char *url, + const char *httphost, + const char *auth, + unsigned int flags, + const char *proxy, + http_session_t session, + const char *srvtag, + strlist_t headers); + +void http_start_data (http_t hd); + +gpg_error_t http_wait_response (http_t hd); + +void http_close (http_t hd, int keep_read_stream); + +gpg_error_t http_open_document (http_t *r_hd, + const char *document, + const char *auth, + unsigned int flags, + const char *proxy, + http_session_t session, + const char *srvtag, + strlist_t headers); + +estream_t http_get_read_ptr (http_t hd); +estream_t http_get_write_ptr (http_t hd); +unsigned int http_get_status_code (http_t hd); +const char *http_get_tls_info (http_t hd, const char *what); +const char *http_get_header (http_t hd, const char *name); +const char **http_get_header_names (http_t hd); +gpg_error_t http_verify_server_credentials (http_session_t sess); + +char *http_escape_string (const char *string, const char *specials); +char *http_escape_data (const void *data, size_t datalen, const char *specials); + + +#endif /*GNUPG_COMMON_HTTP_H*/ diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c new file mode 100644 index 0000000..1087bb5 --- /dev/null +++ b/dirmngr/ks-action.c @@ -0,0 +1,403 @@ +/* ks-action.c - OpenPGP keyserver actions + * Copyright (C) 2011 Free Software Foundation, Inc. + * Copyright (C) 2011, 2014 Werner Koch + * Copyright (C) 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include + +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "ks-engine.h" +#include "ks-action.h" +#if USE_LDAP +# include "ldap-parse-uri.h" +#endif + +/* Called by the engine's help functions to print the actual help. */ +gpg_error_t +ks_print_help (ctrl_t ctrl, const char *text) +{ + return dirmngr_status_help (ctrl, text); +} + + +/* Called by the engine's help functions to print the actual help. */ +gpg_error_t +ks_printf_help (ctrl_t ctrl, const char *format, ...) +{ + va_list arg_ptr; + gpg_error_t err; + char *buf; + + va_start (arg_ptr, format); + buf = es_vbsprintf (format, arg_ptr); + err = buf? 0 : gpg_error_from_syserror (); + va_end (arg_ptr); + if (!err) + err = dirmngr_status_help (ctrl, buf); + es_free (buf); + return err; +} + + +/* Run the help command for the engine responsible for URI. */ +gpg_error_t +ks_action_help (ctrl_t ctrl, const char *url) +{ + gpg_error_t err; + parsed_uri_t parsed_uri; /* The broken down URI. */ + + if (!url || !*url) + { + ks_print_help (ctrl, "Known schemata:\n"); + parsed_uri = NULL; + } + else + { +#if USE_LDAP + if (ldap_uri_p (url)) + err = ldap_parse_uri (&parsed_uri, url); + else +#endif + { + err = http_parse_uri (&parsed_uri, url, 1); + } + + if (err) + return err; + } + + /* Call all engines to give them a chance to print a help sting. */ + err = ks_hkp_help (ctrl, parsed_uri); + if (!err) + err = ks_http_help (ctrl, parsed_uri); + if (!err) + err = ks_finger_help (ctrl, parsed_uri); + if (!err) + err = ks_kdns_help (ctrl, parsed_uri); +#if USE_LDAP + if (!err) + err = ks_ldap_help (ctrl, parsed_uri); +#endif + + if (!parsed_uri) + ks_print_help (ctrl, + "(Use an URL for engine specific help.)"); + else + http_release_parsed_uri (parsed_uri); + return err; +} + + +/* Resolve all host names. This is useful for looking at the status + of configured keyservers. */ +gpg_error_t +ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers) +{ + gpg_error_t err = 0; + int any_server = 0; + uri_item_t uri; + + for (uri = keyservers; !err && uri; uri = uri->next) + { + if (uri->parsed_uri->is_http) + { + any_server = 1; + err = ks_hkp_resolve (ctrl, uri->parsed_uri); + if (err) + break; + } + } + + if (!any_server) + err = gpg_error (GPG_ERR_NO_KEYSERVER); + return err; +} + + +/* Search all configured keyservers for keys matching PATTERNS and + write the result to the provided output stream. */ +gpg_error_t +ks_action_search (ctrl_t ctrl, uri_item_t keyservers, + strlist_t patterns, estream_t outfp) +{ + gpg_error_t err = 0; + int any_server = 0; + int any_results = 0; + uri_item_t uri; + estream_t infp; + + if (!patterns) + return gpg_error (GPG_ERR_NO_USER_ID); + + /* FIXME: We only take care of the first pattern. To fully support + multiple patterns we might either want to run several queries in + parallel and merge them. We also need to decide what to do with + errors - it might not be the best idea to ignore an error from + one server and silently continue with another server. For now we + stop at the first error, unless the server responds with '404 Not + Found', in which case we try the next server. */ + for (uri = keyservers; !err && uri; uri = uri->next) + { + int is_http = uri->parsed_uri->is_http; + int is_ldap = 0; + unsigned int http_status = 0; +#if USE_LDAP + is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0 + || strcmp (uri->parsed_uri->scheme, "ldaps") == 0 + || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); +#endif + if (is_http || is_ldap) + { + any_server = 1; +#if USE_LDAP + if (is_ldap) + err = ks_ldap_search (ctrl, uri->parsed_uri, patterns->d, &infp); + else +#endif + { + err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, + &infp, &http_status); + } + + if (err == gpg_error (GPG_ERR_NO_DATA) + && http_status == 404 /* not found */) + { + /* No record found. Clear error and try next server. */ + err = 0; + continue; + } + + if (!err) + { + err = copy_stream (infp, outfp); + es_fclose (infp); + any_results = 1; + break; + } + } + } + + if (!any_server) + err = gpg_error (GPG_ERR_NO_KEYSERVER); + else if (err == 0 && !any_results) + err = gpg_error (GPG_ERR_NO_DATA); + return err; +} + + +/* Get the requested keys (matching PATTERNS) using all configured + keyservers and write the result to the provided output stream. */ +gpg_error_t +ks_action_get (ctrl_t ctrl, uri_item_t keyservers, + strlist_t patterns, estream_t outfp) +{ + gpg_error_t err = 0; + gpg_error_t first_err = 0; + int any_server = 0; + int any_data = 0; + strlist_t sl; + uri_item_t uri; + estream_t infp; + + if (!patterns) + return gpg_error (GPG_ERR_NO_USER_ID); + + /* FIXME: We only take care of the first keyserver. To fully + support multiple keyservers we need to track the result for each + pattern and use the next keyserver if one key was not found. The + keyservers might not all be fully synced thus it is not clear + whether the first keyserver has the freshest copy of the key. + Need to think about a better strategy. */ + for (uri = keyservers; !err && uri; uri = uri->next) + { + int is_http = uri->parsed_uri->is_http; + int is_ldap = 0; + +#if USE_LDAP + is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0 + || strcmp (uri->parsed_uri->scheme, "ldaps") == 0 + || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); +#endif + + if (is_http || is_ldap) + { + any_server = 1; + for (sl = patterns; !err && sl; sl = sl->next) + { +#if USE_LDAP + if (is_ldap) + err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp); + else +#endif + { + err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp); + } + + if (err) + { + /* It is possible that a server does not carry a + key, thus we only save the error and continue + with the next pattern. FIXME: It is an open + question how to return such an error condition to + the caller. */ + first_err = err; + err = 0; + } + else + { + err = copy_stream (infp, outfp); + /* Reading from the keyserver should never fail, thus + return this error. */ + if (!err) + any_data = 1; + es_fclose (infp); + infp = NULL; + } + } + } + if (any_data) + break; /* Stop loop after a keyserver returned something. */ + } + + if (!any_server) + err = gpg_error (GPG_ERR_NO_KEYSERVER); + else if (!err && first_err && !any_data) + err = first_err; + return err; +} + + +/* Retrieve keys from URL and write the result to the provided output + stream OUTFP. */ +gpg_error_t +ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp) +{ + gpg_error_t err = 0; + estream_t infp; + parsed_uri_t parsed_uri; /* The broken down URI. */ + + if (!url) + return gpg_error (GPG_ERR_INV_URI); + + err = http_parse_uri (&parsed_uri, url, 1); + if (err) + return err; + + if (parsed_uri->is_http) + { + err = ks_http_fetch (ctrl, url, &infp); + if (!err) + { + err = copy_stream (infp, outfp); + es_fclose (infp); + } + } + else if (!parsed_uri->opaque) + { + err = gpg_error (GPG_ERR_INV_URI); + } + else if (!strcmp (parsed_uri->scheme, "finger")) + { + err = ks_finger_fetch (ctrl, parsed_uri, &infp); + if (!err) + { + err = copy_stream (infp, outfp); + es_fclose (infp); + } + } + else if (!strcmp (parsed_uri->scheme, "kdns")) + { + err = ks_kdns_fetch (ctrl, parsed_uri, &infp); + if (!err) + { + err = copy_stream (infp, outfp); + es_fclose (infp); + } + } + else + err = gpg_error (GPG_ERR_INV_URI); + + http_release_parsed_uri (parsed_uri); + return err; +} + + + +/* Send an OpenPGP key to all keyservers. The key in {DATA,DATALEN} + is expected to be in OpenPGP binary transport format. The metadata + in {INFO,INFOLEN} is in colon-separated format (concretely, it is + the output of 'for x in keys sigs; do gpg --list-$x --with-colons + KEYID; done'. This function may modify DATA and INFO. If this is + a problem, then the caller should create a copy. */ +gpg_error_t +ks_action_put (ctrl_t ctrl, uri_item_t keyservers, + void *data, size_t datalen, + void *info, size_t infolen) +{ + gpg_error_t err = 0; + gpg_error_t first_err = 0; + int any_server = 0; + uri_item_t uri; + + (void) info; + (void) infolen; + + for (uri = keyservers; !err && uri; uri = uri->next) + { + int is_http = uri->parsed_uri->is_http; + int is_ldap = 0; + +#if USE_LDAP + is_ldap = (strcmp (uri->parsed_uri->scheme, "ldap") == 0 + || strcmp (uri->parsed_uri->scheme, "ldaps") == 0 + || strcmp (uri->parsed_uri->scheme, "ldapi") == 0); +#endif + + if (is_http || is_ldap) + { + any_server = 1; +#if USE_LDAP + if (is_ldap) + err = ks_ldap_put (ctrl, uri->parsed_uri, data, datalen, + info, infolen); + else +#endif + { + err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen); + } + if (err) + { + first_err = err; + err = 0; + } + } + } + + if (!any_server) + err = gpg_error (GPG_ERR_NO_KEYSERVER); + else if (!err && first_err) + err = first_err; + return err; +} diff --git a/dirmngr/ks-action.h b/dirmngr/ks-action.h new file mode 100644 index 0000000..d576ef0 --- /dev/null +++ b/dirmngr/ks-action.h @@ -0,0 +1,36 @@ +/* ks-action.h - OpenPGP keyserver actions definitions + * Copyright (C) 2011 Free Software Foundation, Inc. + * 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#ifndef DIRMNGR_KS_ACTION_H +#define DIRMNGR_KS_ACTION_H 1 + +gpg_error_t ks_action_help (ctrl_t ctrl, const char *url); +gpg_error_t ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers); +gpg_error_t ks_action_search (ctrl_t ctrl, uri_item_t keyservers, + strlist_t patterns, estream_t outfp); +gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers, + strlist_t patterns, estream_t outfp); +gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp); +gpg_error_t ks_action_put (ctrl_t ctrl, uri_item_t keyservers, + void *data, size_t datalen, + void *info, size_t infolen); + + +#endif /*DIRMNGR_KS_ACTION_H*/ diff --git a/dirmngr/ks-engine-finger.c b/dirmngr/ks-engine-finger.c new file mode 100644 index 0000000..b1f02ad --- /dev/null +++ b/dirmngr/ks-engine-finger.c @@ -0,0 +1,124 @@ +/* ks-engine-finger.c - Finger OpenPGP key access + * Copyright (C) 2011 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 3 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, see . + */ + +#include + +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "userids.h" +#include "ks-engine.h" + +/* Print a help output for the schemata supported by this module. */ +gpg_error_t +ks_finger_help (ctrl_t ctrl, parsed_uri_t uri) +{ + char const data[] = + "Handler for FINGER:\n" + " finger:@\n" + "Supported methods: fetch\n" + "Example:\n" + " finger:joe@example.org\n"; + gpg_error_t err; + + if (!uri) + err = ks_print_help (ctrl, " finger"); + else if (!strcmp (uri->scheme, "finger")) + err = ks_print_help (ctrl, data); + else + err = 0; + + return err; +} + + +/* Get the key from URI which is expected to specify a finger scheme. + On success R_FP has an open stream to read the data. */ +gpg_error_t +ks_finger_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp) +{ + gpg_error_t err; + estream_t fp; + char *server; + char *name; + http_t http; + + (void)ctrl; + *r_fp = NULL; + + if (strcmp (uri->scheme, "finger") || !uri->opaque || !uri->path) + return gpg_error (GPG_ERR_INV_ARG); + + name = xtrystrdup (uri->path); + if (!name) + return gpg_error_from_syserror (); + + server = strchr (name, '@'); + if (!server) + { + err = gpg_error (GPG_ERR_INV_URI); + xfree (name); + return err; + } + *server++ = 0; + + err = http_raw_connect (&http, server, 79, + (opt.use_tor? HTTP_FLAG_FORCE_TOR : 0), NULL); + if (err) + { + xfree (name); + return err; + } + + fp = http_get_write_ptr (http); + if (!fp) + { + err = gpg_error (GPG_ERR_INTERNAL); + http_close (http, 0); + xfree (name); + return err; + } + + if (es_fputs (name, fp) || es_fputs ("\r\n", fp) || es_fflush (fp)) + { + err = gpg_error_from_syserror (); + http_close (http, 0); + xfree (name); + return err; + } + xfree (name); + es_fclose (fp); + + fp = http_get_read_ptr (http); + if (!fp) + { + err = gpg_error (GPG_ERR_INTERNAL); + http_close (http, 0); + return err; + } + + http_close (http, 1 /* Keep read ptr. */); + + *r_fp = fp; + return 0; +} diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c new file mode 100644 index 0000000..a6c22f8 --- /dev/null +++ b/dirmngr/ks-engine-hkp.c @@ -0,0 +1,1517 @@ +/* ks-engine-hkp.c - HKP keyserver engine + * Copyright (C) 2011, 2012 Free Software Foundation, Inc. + * Copyright (C) 2011, 2012, 2014 Werner Koch + * + * 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 3 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, see . + */ + +#include + +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else /*!HAVE_W32_SYSTEM*/ +# include +# include +# include +#endif /*!HAVE_W32_SYSTEM*/ + +#include "dirmngr.h" +#include "misc.h" +#include "userids.h" +#include "dns-stuff.h" +#include "ks-engine.h" + +/* Substitutes for missing Mingw macro. The EAI_SYSTEM mechanism + seems not to be available (probably because there is only one set + of error codes anyway). For now we use WSAEINVAL. */ +#ifndef EAI_OVERFLOW +# define EAI_OVERFLOW EAI_FAIL +#endif +#ifdef HAVE_W32_SYSTEM +# ifndef EAI_SYSTEM +# define EAI_SYSTEM WSAEINVAL +# endif +#endif + + +/* Number of seconds after a host is marked as resurrected. */ +#define RESURRECT_INTERVAL (3600*3) /* 3 hours */ + +/* To match the behaviour of our old gpgkeys helper code we escape + more characters than actually needed. */ +#define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~" + +/* How many redirections do we allow. */ +#define MAX_REDIRECTS 2 + +/* Number of retries done for a dead host etc. */ +#define SEND_REQUEST_RETRIES 3 + +/* Objects used to maintain information about hosts. */ +struct hostinfo_s; +typedef struct hostinfo_s *hostinfo_t; +struct hostinfo_s +{ + time_t lastfail; /* Time we tried to connect and failed. */ + time_t lastused; /* Time of last use. */ + int *pool; /* A -1 terminated array with indices into + HOSTTABLE or NULL if NAME is not a pool + name. */ + int poolidx; /* Index into POOL with the used host. -1 if not set. */ + unsigned int v4:1; /* Host supports AF_INET. */ + unsigned int v6:1; /* Host supports AF_INET6. */ + unsigned int onion:1;/* NAME is an onion (Tor HS) address. */ + unsigned int dead:1; /* Host is currently unresponsive. */ + time_t died_at; /* The time the host was marked dead. If this is + 0 the host has been manually marked dead. */ + char *cname; /* Canonical name of the host. Only set if this + is a pool. */ + char *v4addr; /* A string with the v4 IP address of the host. + NULL if NAME has a numeric IP address or no v4 + address is available. */ + char *v6addr; /* A string with the v6 IP address of the host. + NULL if NAME has a numeric IP address or no v6 + address is available. */ + unsigned short port; /* The port used by the host, 0 if unknown. */ + char name[1]; /* The hostname. */ +}; + + +/* An array of hostinfo_t for all hosts requested by the caller or + resolved from a pool name and its allocated size.*/ +static hostinfo_t *hosttable; +static int hosttable_size; + +/* The number of host slots we initially allocate for HOSTTABLE. */ +#define INITIAL_HOSTTABLE_SIZE 10 + + +/* Create a new hostinfo object, fill in NAME and put it into + HOSTTABLE. Return the index into hosttable on success or -1 on + error. */ +static int +create_new_hostinfo (const char *name) +{ + hostinfo_t hi, *newtable; + int newsize; + int idx, rc; + + hi = xtrymalloc (sizeof *hi + strlen (name)); + if (!hi) + return -1; + strcpy (hi->name, name); + hi->pool = NULL; + hi->poolidx = -1; + hi->lastused = (time_t)(-1); + hi->lastfail = (time_t)(-1); + hi->v4 = 0; + hi->v6 = 0; + hi->onion = 0; + hi->dead = 0; + hi->died_at = 0; + hi->cname = NULL; + hi->v4addr = NULL; + hi->v6addr = NULL; + hi->port = 0; + + /* Add it to the hosttable. */ + for (idx=0; idx < hosttable_size; idx++) + if (!hosttable[idx]) + { + hosttable[idx] = hi; + return idx; + } + /* Need to extend the hosttable. */ + newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE; + newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable); + if (!newtable) + { + xfree (hi); + return -1; + } + hosttable = newtable; + idx = hosttable_size; + hosttable_size = newsize; + rc = idx; + hosttable[idx++] = hi; + while (idx < hosttable_size) + hosttable[idx++] = NULL; + + return rc; +} + + +/* Find the host NAME in our table. Return the index into the + hosttable or -1 if not found. */ +static int +find_hostinfo (const char *name) +{ + int idx; + + for (idx=0; idx < hosttable_size; idx++) + if (hosttable[idx] && !ascii_strcasecmp (hosttable[idx]->name, name)) + return idx; + return -1; +} + + +static int +sort_hostpool (const void *xa, const void *xb) +{ + int a = *(int *)xa; + int b = *(int *)xb; + + assert (a >= 0 && a < hosttable_size); + assert (b >= 0 && b < hosttable_size); + assert (hosttable[a]); + assert (hosttable[b]); + + return ascii_strcasecmp (hosttable[a]->name, hosttable[b]->name); +} + + +/* Return true if the host with the hosttable index TBLIDX is in POOL. */ +static int +host_in_pool_p (int *pool, int tblidx) +{ + int i, pidx; + + for (i=0; (pidx = pool[i]) != -1; i++) + if (pidx == tblidx && hosttable[pidx]) + return 1; + return 0; +} + + +/* Select a random host. Consult TABLE which indices into the global + hosttable. Returns index into TABLE or -1 if no host could be + selected. */ +static int +select_random_host (int *table) +{ + int *tbl; + size_t tblsize; + int pidx, idx; + + /* We create a new table so that we randomly select only from + currently alive hosts. */ + for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++) + if (hosttable[pidx] && !hosttable[pidx]->dead) + tblsize++; + if (!tblsize) + return -1; /* No hosts. */ + + tbl = xtrymalloc (tblsize * sizeof *tbl); + if (!tbl) + return -1; + for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++) + if (hosttable[pidx] && !hosttable[pidx]->dead) + tbl[tblsize++] = pidx; + + if (tblsize == 1) /* Save a get_uint_nonce. */ + pidx = tbl[0]; + else + pidx = tbl[get_uint_nonce () % tblsize]; + + xfree (tbl); + return pidx; +} + + +/* Figure out if a set of DNS records looks like a pool. */ +static int +arecords_is_pool (dns_addrinfo_t aibuf) +{ + dns_addrinfo_t ai; + int n_v6, n_v4; + + n_v6 = n_v4 = 0; + for (ai = aibuf; ai; ai = ai->next) + { + if (ai->family == AF_INET6) + n_v6++; + else if (ai->family == AF_INET) + n_v4++; + } + + return n_v6 > 1 || n_v4 > 1; +} + + +/* Add the host AI under the NAME into the HOSTTABLE. If PORT is not + zero, it specifies which port to use to talk to the host. If NAME + specifies a pool (as indicated by IS_POOL), update the given + reference table accordingly. */ +static void +add_host (const char *name, int is_pool, + const dns_addrinfo_t ai, unsigned short port, + int *reftbl, size_t reftblsize, int *refidx) +{ + gpg_error_t tmperr; + char *tmphost; + int idx, tmpidx; + int is_numeric = 0; + int i; + + idx = find_hostinfo (name); + + if (!is_pool && !is_ip_address (name)) + { + /* This is a hostname but not a pool. Use the name + as given without going through resolve_dns_addr. */ + tmphost = xtrystrdup (name); + if (!tmphost) + tmperr = gpg_error_from_syserror (); + else + tmperr = 0; + } + else + { + tmperr = resolve_dns_addr (ai->addr, ai->addrlen, + DNS_WITHBRACKET, &tmphost); + if (tmphost && is_ip_address (tmphost)) + is_numeric = 1; + } + + if (tmperr) + { + log_info ("resolve_dns_addr failed while checking '%s': %s\n", + name, gpg_strerror (tmperr)); + } + else if ((*refidx) + 1 >= reftblsize) + { + log_error ("resolve_dns_addr for '%s': '%s'" + " [index table full - ignored]\n", name, tmphost); + } + else + { + if (!is_pool && is_ip_address (name)) + /* Update the original entry. */ + tmpidx = idx; + else + tmpidx = find_hostinfo (tmphost); + log_info ("resolve_dns_addr for '%s': '%s'%s\n", + name, tmphost, + tmpidx == -1? "" : " [already known]"); + + if (tmpidx == -1) /* Create a new entry. */ + tmpidx = create_new_hostinfo (tmphost); + + if (tmpidx == -1) + { + log_error ("map_host for '%s' problem: %s - '%s'" + " [ignored]\n", + name, strerror (errno), tmphost); + } + else /* Set or update the entry. */ + { + char *ipaddr = NULL; + + if (port) + hosttable[tmpidx]->port = port; + + if (!is_numeric) + { + xfree (tmphost); + tmperr = resolve_dns_addr (ai->addr, ai->addrlen, + (DNS_NUMERICHOST + | DNS_WITHBRACKET), + &tmphost); + if (tmperr) + log_info ("resolve_dns_addr failed: %s\n", + gpg_strerror (tmperr)); + else + { + ipaddr = tmphost; + tmphost = NULL; + } + } + + if (ai->family == AF_INET6) + { + hosttable[tmpidx]->v6 = 1; + xfree (hosttable[tmpidx]->v6addr); + hosttable[tmpidx]->v6addr = ipaddr; + } + else if (ai->family == AF_INET) + { + hosttable[tmpidx]->v4 = 1; + xfree (hosttable[tmpidx]->v4addr); + hosttable[tmpidx]->v4addr = ipaddr; + } + else + BUG (); + + for (i=0; i < *refidx; i++) + if (reftbl[i] == tmpidx) + break; + if (!(i < *refidx) && tmpidx != idx) + reftbl[(*refidx)++] = tmpidx; + } + } + xfree (tmphost); +} + + +/* Map the host name NAME to the actual to be used host name. This + allows us to manage round robin DNS names. We use our own strategy + to choose one of the hosts. For example we skip those hosts which + failed for some time and we stick to one host for a time + independent of DNS retry times. If FORCE_RESELECT is true a new + host is always selected. The selected host is stored as a malloced + string at R_HOST; on error NULL is stored. If we know the port + used by the selected host, a string representation is written to + R_PORTSTR, otherwise it is left untouched. If R_HTTPFLAGS is not + NULL it will receive flags which are to be passed to http_open. If + R_POOLNAME is not NULL a malloced name of the pool is stored or + NULL if it is not a pool. */ +static gpg_error_t +map_host (ctrl_t ctrl, const char *name, int force_reselect, + char **r_host, char *r_portstr, + unsigned int *r_httpflags, char **r_poolname) +{ + gpg_error_t err = 0; + hostinfo_t hi; + int idx; + + *r_host = NULL; + if (r_httpflags) + *r_httpflags = 0; + if (r_poolname) + *r_poolname = NULL; + + /* No hostname means localhost. */ + if (!name || !*name) + { + *r_host = xtrystrdup ("localhost"); + return *r_host? 0 : gpg_error_from_syserror (); + } + + /* See whether the host is in our table. */ + idx = find_hostinfo (name); + if (idx == -1 && is_onion_address (name)) + { + idx = create_new_hostinfo (name); + if (idx == -1) + return gpg_error_from_syserror (); + hi = hosttable[idx]; + hi->onion = 1; + } + else if (idx == -1) + { + /* We never saw this host. Allocate a new entry. */ + dns_addrinfo_t aibuf, ai; + int *reftbl; + size_t reftblsize; + int refidx; + int is_pool = 0; + char *cname; + char *srvrecord; + struct srventry *srvs; + unsigned int srvscount; + + reftblsize = 100; + reftbl = xtrymalloc (reftblsize * sizeof *reftbl); + if (!reftbl) + return gpg_error_from_syserror (); + refidx = 0; + + idx = create_new_hostinfo (name); + if (idx == -1) + { + err = gpg_error_from_syserror (); + xfree (reftbl); + return err; + } + hi = hosttable[idx]; + + if (!is_ip_address (name)) + { + /* Check for SRV records. */ + srvrecord = xtryasprintf ("_hkp._tcp.%s", name); + if (srvrecord == NULL) + { + err = gpg_error_from_syserror (); + xfree (reftbl); + return err; + } + + err = get_dns_srv (srvrecord, &srvs, &srvscount); + xfree (srvrecord); + if (err) + { + xfree (reftbl); + return err; + } + + if (srvscount > 0) + { + int i; + is_pool = srvscount > 1; + + for (i = 0; i < srvscount; i++) + { + err = resolve_dns_name (srvs[i].target, 0, + AF_UNSPEC, SOCK_STREAM, + &ai, &cname); + if (err) + continue; + dirmngr_tick (ctrl); + add_host (name, is_pool, ai, srvs[i].port, + reftbl, reftblsize, &refidx); + } + + xfree (srvs); + } + } + + /* Find all A records for this entry and put them into the pool + list - if any. */ + err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname); + if (err) + { + log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err)); + err = 0; + } + else + { + /* First figure out whether this is a pool. For a pool we + use a different strategy than for a plain server: We use + the canonical name of the pool as the virtual host along + with the IP addresses. If it is not a pool, we use the + specified name. */ + if (! is_pool) + is_pool = arecords_is_pool (aibuf); + if (is_pool && cname) + { + hi->cname = cname; + cname = NULL; + } + + for (ai = aibuf; ai; ai = ai->next) + { + if (ai->family != AF_INET && ai->family != AF_INET6) + continue; + dirmngr_tick (ctrl); + + add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx); + } + } + reftbl[refidx] = -1; + xfree (cname); + free_dns_addrinfo (aibuf); + + if (refidx && is_pool) + { + assert (!hi->pool); + hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl); + if (!hi->pool) + { + err = gpg_error_from_syserror (); + log_error ("shrinking index table in map_host failed: %s\n", + gpg_strerror (err)); + xfree (reftbl); + return err; + } + qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool); + } + else + xfree (reftbl); + } + + hi = hosttable[idx]; + if (hi->pool) + { + /* Deal with the pool name before selecting a host. */ + if (r_poolname) + { + *r_poolname = xtrystrdup (hi->cname? hi->cname : hi->name); + if (!*r_poolname) + return gpg_error_from_syserror (); + } + + /* If the currently selected host is now marked dead, force a + re-selection . */ + if (force_reselect) + hi->poolidx = -1; + else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size + && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead) + hi->poolidx = -1; + + /* Select a host if needed. */ + if (hi->poolidx == -1) + { + hi->poolidx = select_random_host (hi->pool); + if (hi->poolidx == -1) + { + log_error ("no alive host found in pool '%s'\n", name); + if (r_poolname) + { + xfree (*r_poolname); + *r_poolname = NULL; + } + return gpg_error (GPG_ERR_NO_KEYSERVER); + } + } + + assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size); + hi = hosttable[hi->poolidx]; + assert (hi); + } + + if (hi->dead) + { + log_error ("host '%s' marked as dead\n", hi->name); + if (r_poolname) + { + xfree (*r_poolname); + *r_poolname = NULL; + } + return gpg_error (GPG_ERR_NO_KEYSERVER); + } + + if (r_httpflags) + { + /* If the hosttable does not indicate that a certain host + supports IPv, we explicit set the corresponding http + flags. The reason for this is that a host might be listed in + a pool as not v6 only but actually support v6 when later + the name is resolved by our http layer. */ + if (!hi->v4) + *r_httpflags |= HTTP_FLAG_IGNORE_IPv4; + if (!hi->v6) + *r_httpflags |= HTTP_FLAG_IGNORE_IPv6; + + /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion + addresses because the http module detects this itself. This + also allows us to use an onion address without Tor mode being + enabled. */ + } + + *r_host = xtrystrdup (hi->name); + if (!*r_host) + { + err = gpg_error_from_syserror (); + if (r_poolname) + { + xfree (*r_poolname); + *r_poolname = NULL; + } + return err; + } + if (hi->port) + snprintf (r_portstr, 6 /* five digits and the sentinel */, + "%hu", hi->port); + return 0; +} + + +/* Mark the host NAME as dead. NAME may be given as an URL. Returns + true if a host was really marked as dead or was already marked dead + (e.g. by a concurrent session). */ +static int +mark_host_dead (const char *name) +{ + const char *host; + char *host_buffer = NULL; + parsed_uri_t parsed_uri = NULL; + int done = 0; + + if (name && *name && !http_parse_uri (&parsed_uri, name, 1)) + { + if (parsed_uri->v6lit) + { + host_buffer = strconcat ("[", parsed_uri->host, "]", NULL); + if (!host_buffer) + log_error ("out of core in mark_host_dead"); + host = host_buffer; + } + else + host = parsed_uri->host; + } + else + host = name; + + if (host && *host && strcmp (host, "localhost")) + { + hostinfo_t hi; + int idx; + + idx = find_hostinfo (host); + if (idx != -1) + { + hi = hosttable[idx]; + log_info ("marking host '%s' as dead%s\n", + hi->name, hi->dead? " (again)":""); + hi->dead = 1; + hi->died_at = gnupg_get_time (); + if (!hi->died_at) + hi->died_at = 1; + done = 1; + } + } + + http_release_parsed_uri (parsed_uri); + xfree (host_buffer); + return done; +} + + +/* Mark a host in the hosttable as dead or - if ALIVE is true - as + alive. */ +gpg_error_t +ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive) +{ + gpg_error_t err = 0; + hostinfo_t hi, hi2; + int idx, idx2, idx3, n; + + if (!name || !*name || !strcmp (name, "localhost")) + return 0; + + idx = find_hostinfo (name); + if (idx == -1) + return gpg_error (GPG_ERR_NOT_FOUND); + + hi = hosttable[idx]; + if (alive && hi->dead) + { + hi->dead = 0; + err = ks_printf_help (ctrl, "marking '%s' as alive", name); + } + else if (!alive && !hi->dead) + { + hi->dead = 1; + hi->died_at = 0; /* Manually set dead. */ + err = ks_printf_help (ctrl, "marking '%s' as dead", name); + } + + /* If the host is a pool mark all member hosts. */ + if (!err && hi->pool) + { + for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++) + { + assert (n >= 0 && n < hosttable_size); + + if (!alive) + { + /* Do not mark a host from a pool dead if it is also a + member in another pool. */ + for (idx3=0; idx3 < hosttable_size; idx3++) + { + if (hosttable[idx3] + && hosttable[idx3]->pool + && idx3 != idx + && host_in_pool_p (hosttable[idx3]->pool, n)) + break; + } + if (idx3 < hosttable_size) + continue; /* Host is also a member of another pool. */ + } + + hi2 = hosttable[n]; + if (!hi2) + ; + else if (alive && hi2->dead) + { + hi2->dead = 0; + err = ks_printf_help (ctrl, "marking '%s' as alive", + hi2->name); + } + else if (!alive && !hi2->dead) + { + hi2->dead = 1; + hi2->died_at = 0; /* Manually set dead. */ + err = ks_printf_help (ctrl, "marking '%s' as dead", + hi2->name); + } + } + } + + return err; +} + + +/* Debug function to print the entire hosttable. */ +gpg_error_t +ks_hkp_print_hosttable (ctrl_t ctrl) +{ + gpg_error_t err; + int idx, idx2; + hostinfo_t hi; + membuf_t mb; + time_t curtime; + char *p, *died; + const char *diedstr; + + err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):"); + if (err) + return err; + + curtime = gnupg_get_time (); + for (idx=0; idx < hosttable_size; idx++) + if ((hi=hosttable[idx])) + { + if (hi->dead && hi->died_at) + { + died = elapsed_time_string (hi->died_at, curtime); + diedstr = died? died : "error"; + } + else + diedstr = died = NULL; + err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n", + idx, + hi->onion? "O" : hi->v6? "6":" ", + hi->v4? "4":" ", + hi->dead? "d":" ", + hi->name, + hi->v6addr? " v6=":"", + hi->v6addr? hi->v6addr:"", + hi->v4addr? " v4=":"", + hi->v4addr? hi->v4addr:"", + diedstr? " (":"", + diedstr? diedstr:"", + diedstr? ")":"" ); + xfree (died); + if (err) + return err; + + if (hi->cname) + err = ks_printf_help (ctrl, " . %s", hi->cname); + if (err) + return err; + + if (hi->pool) + { + init_membuf (&mb, 256); + put_membuf_printf (&mb, " . -->"); + for (idx2=0; hi->pool[idx2] != -1; idx2++) + { + put_membuf_printf (&mb, " %d", hi->pool[idx2]); + if (hi->poolidx == hi->pool[idx2]) + put_membuf_printf (&mb, "*"); + } + put_membuf( &mb, "", 1); + p = get_membuf (&mb, NULL); + if (!p) + return gpg_error_from_syserror (); + err = ks_print_help (ctrl, p); + xfree (p); + if (err) + return err; + } + } + return 0; +} + + + +/* Print a help output for the schemata supported by this module. */ +gpg_error_t +ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri) +{ + const char const data[] = + "Handler for HKP URLs:\n" + " hkp://\n" +#if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS + " hkps://\n" +#endif + "Supported methods: search, get, put\n"; + gpg_error_t err; + +#if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS + const char data2[] = " hkp\n hkps"; +#else + const char data2[] = " hkp"; +#endif + + if (!uri) + err = ks_print_help (ctrl, data2); + else if (uri->is_http && (!strcmp (uri->scheme, "hkp") + || !strcmp (uri->scheme, "hkps"))) + err = ks_print_help (ctrl, data); + else + err = 0; + + return err; +} + + +/* Build the remote part of the URL from SCHEME, HOST and an optional + PORT. Returns an allocated string at R_HOSTPORT or NULL on failure + If R_POOLNAME is not NULL it receives a malloced string with the + poolname. */ +static gpg_error_t +make_host_part (ctrl_t ctrl, + const char *scheme, const char *host, unsigned short port, + int force_reselect, + char **r_hostport, unsigned int *r_httpflags, char **r_poolname) +{ + gpg_error_t err; + char portstr[10]; + char *hostname; + + *r_hostport = NULL; + + portstr[0] = 0; + err = map_host (ctrl, host, force_reselect, + &hostname, portstr, r_httpflags, r_poolname); + if (err) + return err; + + /* Map scheme and port. */ + if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https")) + { + scheme = "https"; + if (! *portstr) + strcpy (portstr, "443"); + } + else /* HKP or HTTP. */ + { + scheme = "http"; + if (! *portstr) + strcpy (portstr, "11371"); + } + if (port) + snprintf (portstr, sizeof portstr, "%hu", port); + else + { + /*fixme_do_srv_lookup ()*/ + } + + *r_hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL); + xfree (hostname); + if (!*r_hostport) + { + if (r_poolname) + { + xfree (*r_poolname); + *r_poolname = NULL; + } + return gpg_error_from_syserror (); + } + return 0; +} + + +/* Resolve all known keyserver names and update the hosttable. This + is mainly useful for debugging because the resolving is anyway done + on demand. */ +gpg_error_t +ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri) +{ + gpg_error_t err; + char *hostport = NULL; + + err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, 1, + &hostport, NULL, NULL); + if (err) + { + err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s", + uri->scheme, uri->host, uri->port, + gpg_strerror (err)); + } + else + { + err = ks_printf_help (ctrl, "%s", hostport); + xfree (hostport); + } + return err; +} + + +/* Housekeeping function called from the housekeeping thread. It is + used to mark dead hosts alive so that they may be tried again after + some time. */ +void +ks_hkp_housekeeping (time_t curtime) +{ + int idx; + hostinfo_t hi; + + for (idx=0; idx < hosttable_size; idx++) + { + hi = hosttable[idx]; + if (!hi) + continue; + if (!hi->dead) + continue; + if (!hi->died_at) + continue; /* Do not resurrect manually shot hosts. */ + if (hi->died_at + RESURRECT_INTERVAL <= curtime + || hi->died_at > curtime) + { + hi->dead = 0; + log_info ("resurrected host '%s'", hi->name); + } + } +} + + +/* Send an HTTP request. On success returns an estream object at + R_FP. HOSTPORTSTR is only used for diagnostics. If HTTPHOST is + not NULL it will be used as HTTP "Host" header. If POST_CB is not + NULL a post request is used and that callback is called to allow + writing the post data. If R_HTTP_STATUS is not NULL, the http + status code will be stored there. */ +static gpg_error_t +send_request (ctrl_t ctrl, const char *request, const char *hostportstr, + const char *httphost, unsigned int httpflags, + gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value, + estream_t *r_fp, unsigned int *r_http_status) +{ + gpg_error_t err; + http_session_t session = NULL; + http_t http = NULL; + int redirects_left = MAX_REDIRECTS; + estream_t fp = NULL; + char *request_buffer = NULL; + + *r_fp = NULL; + + err = http_session_new (&session, NULL, httphost, HTTP_FLAG_TRUST_DEF); + if (err) + goto leave; + http_session_set_log_cb (session, cert_log_cb); + + once_more: + err = http_open (&http, + post_cb? HTTP_REQ_POST : HTTP_REQ_GET, + request, + httphost, + /* fixme: AUTH */ NULL, + (httpflags + |(opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) + |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + ctrl->http_proxy, + session, + NULL, + /*FIXME curl->srvtag*/NULL); + if (!err) + { + fp = http_get_write_ptr (http); + /* Avoid caches to get the most recent copy of the key. We set + both the Pragma and Cache-Control versions of the header, so + we're good with both HTTP 1.0 and 1.1. */ + es_fputs ("Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n", fp); + if (post_cb) + err = post_cb (post_cb_value, http); + if (!err) + { + http_start_data (http); + if (es_ferror (fp)) + err = gpg_error_from_syserror (); + } + } + if (err) + { + /* Fixme: After a redirection we show the old host name. */ + log_error (_("error connecting to '%s': %s\n"), + hostportstr, gpg_strerror (err)); + goto leave; + } + + /* Wait for the response. */ + dirmngr_tick (ctrl); + err = http_wait_response (http); + if (err) + { + log_error (_("error reading HTTP response for '%s': %s\n"), + hostportstr, gpg_strerror (err)); + goto leave; + } + + if (http_get_tls_info (http, NULL)) + { + /* Update the httpflags so that a redirect won't fallback to an + unencrypted connection. */ + httpflags |= HTTP_FLAG_FORCE_TLS; + } + + if (r_http_status) + *r_http_status = http_get_status_code (http); + + switch (http_get_status_code (http)) + { + case 200: + err = 0; + break; /* Success. */ + + case 301: + case 302: + case 307: + { + const char *s = http_get_header (http, "Location"); + + log_info (_("URL '%s' redirected to '%s' (%u)\n"), + request, s?s:"[none]", http_get_status_code (http)); + if (s && *s && redirects_left-- ) + { + xfree (request_buffer); + request_buffer = xtrystrdup (s); + if (request_buffer) + { + request = request_buffer; + http_close (http, 0); + http = NULL; + goto once_more; + } + err = gpg_error_from_syserror (); + } + else + err = gpg_error (GPG_ERR_NO_DATA); + log_error (_("too many redirections\n")); + } + goto leave; + + case 501: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + goto leave; + + default: + log_error (_("error accessing '%s': http status %u\n"), + request, http_get_status_code (http)); + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + + /* FIXME: We should register a permanent redirection and whether a + host has ever used TLS so that future calls will always use + TLS. */ + + fp = http_get_read_ptr (http); + if (!fp) + { + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + + /* Return the read stream and close the HTTP context. */ + *r_fp = fp; + http_close (http, 1); + http = NULL; + + leave: + http_close (http, 0); + http_session_release (session); + xfree (request_buffer); + return err; +} + + +/* Helper to evaluate the error code ERR form a send_request() call + with REQUEST. The function returns true if the caller shall try + again. TRIES_LEFT points to a variable to track the number of + retries; this function decrements it and won't return true if it is + down to zero. */ +static int +handle_send_request_error (gpg_error_t err, const char *request, + unsigned int *tries_left) +{ + int retry = 0; + + switch (gpg_err_code (err)) + { + case GPG_ERR_ECONNREFUSED: + case GPG_ERR_ENETUNREACH: + case GPG_ERR_UNKNOWN_HOST: + case GPG_ERR_NETWORK: + if (mark_host_dead (request) && *tries_left) + retry = 1; + break; + + case GPG_ERR_ETIMEDOUT: + if (*tries_left) + { + log_info ("selecting a different host due to a timeout\n"); + retry = 1; + } + + default: + break; + } + + if (*tries_left) + --*tries_left; + + return retry; +} + + +/* Search the keyserver identified by URI for keys matching PATTERN. + On success R_FP has an open stream to read the data. If + R_HTTP_STATUS is not NULL, the http status code will be stored + there. */ +gpg_error_t +ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, + estream_t *r_fp, unsigned int *r_http_status) +{ + gpg_error_t err; + KEYDB_SEARCH_DESC desc; + char fprbuf[2+40+1]; + char *hostport = NULL; + char *request = NULL; + estream_t fp = NULL; + int reselect; + unsigned int httpflags; + char *httphost = NULL; + unsigned int tries = SEND_REQUEST_RETRIES; + + *r_fp = NULL; + + /* Remove search type indicator and adjust PATTERN accordingly. + Note that HKP keyservers like the 0x to be present when searching + by keyid. We need to re-format the fingerprint and keyids so to + remove the gpg specific force-use-of-this-key flag ("!"). */ + err = classify_user_id (pattern, &desc, 1); + if (err) + return err; + switch (desc.mode) + { + case KEYDB_SEARCH_MODE_EXACT: + case KEYDB_SEARCH_MODE_SUBSTR: + case KEYDB_SEARCH_MODE_MAIL: + case KEYDB_SEARCH_MODE_MAILSUB: + pattern = desc.u.name; + break; + case KEYDB_SEARCH_MODE_SHORT_KID: + snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]); + pattern = fprbuf; + break; + case KEYDB_SEARCH_MODE_LONG_KID: + snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX", + (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]); + pattern = fprbuf; + break; + case KEYDB_SEARCH_MODE_FPR16: + fprbuf[0] = '0'; + fprbuf[1] = 'x'; + bin2hex (desc.u.fpr, 16, fprbuf+2); + pattern = fprbuf; + break; + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + fprbuf[0] = '0'; + fprbuf[1] = 'x'; + bin2hex (desc.u.fpr, 20, fprbuf+2); + pattern = fprbuf; + break; + default: + return gpg_error (GPG_ERR_INV_USER_ID); + } + + /* Build the request string. */ + reselect = 0; + again: + { + char *searchkey; + + xfree (hostport); hostport = NULL; + xfree (httphost); httphost = NULL; + err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect, + &hostport, &httpflags, &httphost); + if (err) + goto leave; + + searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS); + if (!searchkey) + { + err = gpg_error_from_syserror (); + goto leave; + } + + xfree (request); + request = strconcat (hostport, + "/pks/lookup?op=index&options=mr&search=", + searchkey, + NULL); + xfree (searchkey); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + /* Send the request. */ + err = send_request (ctrl, request, hostport, httphost, httpflags, + NULL, NULL, &fp, r_http_status); + if (handle_send_request_error (err, request, &tries)) + { + reselect = 1; + goto again; + } + if (err) + goto leave; + + err = dirmngr_status (ctrl, "SOURCE", hostport, NULL); + if (err) + goto leave; + + /* Peek at the response. */ + { + int c = es_getc (fp); + if (c == -1) + { + err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF); + log_error ("error reading response: %s\n", gpg_strerror (err)); + goto leave; + } + if (c == '<') + { + /* The document begins with a '<': Assume a HTML response, + which we don't support. */ + err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + goto leave; + } + es_ungetc (c, fp); + } + + /* Return the read stream. */ + *r_fp = fp; + fp = NULL; + + leave: + es_fclose (fp); + xfree (request); + xfree (hostport); + xfree (httphost); + return err; +} + + +/* Get the key described key the KEYSPEC string from the keyserver + identified by URI. On success R_FP has an open stream to read the + data. The data will be provided in a format GnuPG can import + (either a binary OpenPGP message or an armored one). */ +gpg_error_t +ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) +{ + gpg_error_t err; + KEYDB_SEARCH_DESC desc; + char kidbuf[2+40+1]; + const char *exactname = NULL; + char *searchkey = NULL; + char *hostport = NULL; + char *request = NULL; + estream_t fp = NULL; + int reselect; + char *httphost = NULL; + unsigned int httpflags; + unsigned int tries = SEND_REQUEST_RETRIES; + + *r_fp = NULL; + + /* Remove search type indicator and adjust PATTERN accordingly. + Note that HKP keyservers like the 0x to be present when searching + by keyid. We need to re-format the fingerprint and keyids so to + remove the gpg specific force-use-of-this-key flag ("!"). */ + err = classify_user_id (keyspec, &desc, 1); + if (err) + return err; + switch (desc.mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]); + break; + case KEYDB_SEARCH_MODE_LONG_KID: + snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX", + (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]); + break; + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + /* This is a v4 fingerprint. */ + kidbuf[0] = '0'; + kidbuf[1] = 'x'; + bin2hex (desc.u.fpr, 20, kidbuf+2); + break; + + case KEYDB_SEARCH_MODE_EXACT: + exactname = desc.u.name; + break; + + case KEYDB_SEARCH_MODE_FPR16: + log_error ("HKP keyservers do not support v3 fingerprints\n"); + default: + return gpg_error (GPG_ERR_INV_USER_ID); + } + + searchkey = http_escape_string (exactname? exactname : kidbuf, + EXTRA_ESCAPE_CHARS); + if (!searchkey) + { + err = gpg_error_from_syserror (); + goto leave; + } + + reselect = 0; + again: + /* Build the request string. */ + xfree (hostport); hostport = NULL; + xfree (httphost); httphost = NULL; + err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect, + &hostport, &httpflags, &httphost); + if (err) + goto leave; + + xfree (request); + request = strconcat (hostport, + "/pks/lookup?op=get&options=mr&search=", + searchkey, + exactname? "&exact=on":"", + NULL); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Send the request. */ + err = send_request (ctrl, request, hostport, httphost, httpflags, + NULL, NULL, &fp, NULL); + if (handle_send_request_error (err, request, &tries)) + { + reselect = 1; + goto again; + } + if (err) + goto leave; + + err = dirmngr_status (ctrl, "SOURCE", hostport, NULL); + if (err) + goto leave; + + /* Return the read stream and close the HTTP context. */ + *r_fp = fp; + fp = NULL; + + leave: + es_fclose (fp); + xfree (request); + xfree (hostport); + xfree (httphost); + xfree (searchkey); + return err; +} + + + + +/* Callback parameters for put_post_cb. */ +struct put_post_parm_s +{ + char *datastring; +}; + + +/* Helper for ks_hkp_put. */ +static gpg_error_t +put_post_cb (void *opaque, http_t http) +{ + struct put_post_parm_s *parm = opaque; + gpg_error_t err = 0; + estream_t fp; + size_t len; + + fp = http_get_write_ptr (http); + len = strlen (parm->datastring); + + es_fprintf (fp, + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */); + http_start_data (http); + if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL)) + err = gpg_error_from_syserror (); + return err; +} + + +/* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */ +gpg_error_t +ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) +{ + gpg_error_t err; + char *hostport = NULL; + char *request = NULL; + estream_t fp = NULL; + struct put_post_parm_s parm; + char *armored = NULL; + int reselect; + char *httphost = NULL; + unsigned int httpflags; + unsigned int tries = SEND_REQUEST_RETRIES; + + parm.datastring = NULL; + + err = armor_data (&armored, data, datalen); + if (err) + goto leave; + + parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS); + if (!parm.datastring) + { + err = gpg_error_from_syserror (); + goto leave; + } + xfree (armored); + armored = NULL; + + /* Build the request string. */ + reselect = 0; + again: + xfree (hostport); hostport = NULL; + xfree (httphost); httphost = NULL; + err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect, + &hostport, &httpflags, &httphost); + if (err) + goto leave; + + xfree (request); + request = strconcat (hostport, "/pks/add", NULL); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Send the request. */ + err = send_request (ctrl, request, hostport, httphost, 0, + put_post_cb, &parm, &fp, NULL); + if (handle_send_request_error (err, request, &tries)) + { + reselect = 1; + goto again; + } + if (err) + goto leave; + + leave: + es_fclose (fp); + xfree (parm.datastring); + xfree (armored); + xfree (request); + xfree (hostport); + xfree (httphost); + return err; +} diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c new file mode 100644 index 0000000..4c4ab1e --- /dev/null +++ b/dirmngr/ks-engine-http.c @@ -0,0 +1,184 @@ +/* ks-engine-http.c - HTTP OpenPGP key access + * Copyright (C) 2011 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 3 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, see . + */ + +#include + +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "ks-engine.h" + +/* How many redirections do we allow. */ +#define MAX_REDIRECTS 2 + +/* Print a help output for the schemata supported by this module. */ +gpg_error_t +ks_http_help (ctrl_t ctrl, parsed_uri_t uri) +{ + const char const data[] = + "Handler for HTTP URLs:\n" + " http://\n" +#if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS + " https://\n" +#endif + "Supported methods: fetch\n"; + gpg_error_t err; + +#if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS + const char data2[] = " http\n https"; +#else + const char data2[] = " http"; +#endif + + if (!uri) + err = ks_print_help (ctrl, data2); + else if (uri->is_http && strcmp (uri->scheme, "hkp")) + err = ks_print_help (ctrl, data); + else + err = 0; + + return err; +} + + +/* Get the key from URL which is expected to specify a http style + scheme. On success R_FP has an open stream to read the data. */ +gpg_error_t +ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) +{ + gpg_error_t err; + http_session_t session = NULL; + http_t http = NULL; + int redirects_left = MAX_REDIRECTS; + estream_t fp = NULL; + char *request_buffer = NULL; + + once_more: + /* Note that we only use the system provided certificates with the + * fetch command. */ + err = http_session_new (&session, NULL, NULL, HTTP_FLAG_TRUST_SYS); + if (err) + goto leave; + http_session_set_log_cb (session, cert_log_cb); + + *r_fp = NULL; + err = http_open (&http, + HTTP_REQ_GET, + url, + /* httphost */ NULL, + /* fixme: AUTH */ NULL, + ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) + | (opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + ctrl->http_proxy, + session, + NULL, + /*FIXME curl->srvtag*/NULL); + if (!err) + { + fp = http_get_write_ptr (http); + /* Avoid caches to get the most recent copy of the key. We set + both the Pragma and Cache-Control versions of the header, so + we're good with both HTTP 1.0 and 1.1. */ + es_fputs ("Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n", fp); + http_start_data (http); + if (es_ferror (fp)) + err = gpg_error_from_syserror (); + } + if (err) + { + /* Fixme: After a redirection we show the old host name. */ + log_error (_("error connecting to '%s': %s\n"), + url, gpg_strerror (err)); + goto leave; + } + + /* Wait for the response. */ + dirmngr_tick (ctrl); + err = http_wait_response (http); + if (err) + { + log_error (_("error reading HTTP response for '%s': %s\n"), + url, gpg_strerror (err)); + goto leave; + } + + switch (http_get_status_code (http)) + { + case 200: + err = 0; + break; /* Success. */ + + case 301: + case 302: + case 307: + { + const char *s = http_get_header (http, "Location"); + + log_info (_("URL '%s' redirected to '%s' (%u)\n"), + url, s?s:"[none]", http_get_status_code (http)); + if (s && *s && redirects_left-- ) + { + xfree (request_buffer); + request_buffer = xtrystrdup (s); + if (request_buffer) + { + url = request_buffer; + http_close (http, 0); + http = NULL; + http_session_release (session); + goto once_more; + } + err = gpg_error_from_syserror (); + } + else + err = gpg_error (GPG_ERR_NO_DATA); + log_error (_("too many redirections\n")); + } + goto leave; + + default: + log_error (_("error accessing '%s': http status %u\n"), + url, http_get_status_code (http)); + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + + fp = http_get_read_ptr (http); + if (!fp) + { + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + + /* Return the read stream and close the HTTP context. */ + *r_fp = fp; + http_close (http, 1); + http = NULL; + + leave: + http_close (http, 0); + http_session_release (session); + xfree (request_buffer); + return err; +} diff --git a/dirmngr/ks-engine-kdns.c b/dirmngr/ks-engine-kdns.c new file mode 100644 index 0000000..d49d046 --- /dev/null +++ b/dirmngr/ks-engine-kdns.c @@ -0,0 +1,79 @@ +/* ks-engine-kdns.c - KDNS OpenPGP key access + * Copyright (C) 2011 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 3 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, see . + */ + +#include + +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "userids.h" +#include "ks-engine.h" + +/* Print a help output for the schemata supported by this module. */ +gpg_error_t +ks_kdns_help (ctrl_t ctrl, parsed_uri_t uri) +{ + const char const data[] = + "This keyserver engine accepts URLs of the form:\n" + " kdns://[NAMESERVER]/[ROOT][?at=STRING]\n" + "with\n" + " NAMESERVER used for queries (default: system standard)\n" + " ROOT a DNS name appended to the query (default: none)\n" + " STRING a string to replace the '@' (default: \".\")\n" + "If a long answer is expected add the parameter \"usevc=1\".\n" + "Supported methods: fetch\n" + "Example:\n" + "A query for \"hacker@gnupg.org\" with\n" + " kdns://10.0.0.1/example.net?at=_key_&usevc=1\n" + "setup as --auto-key-lookup in gpg does a CERT record query\n" + "with type PGP on the nameserver 10.0.0.1 for\n" + " hacker._key_.gnupg.org.example.net"; + gpg_error_t err; + + if (!uri) + err = ks_print_help (ctrl, " kdns"); + else if (!strcmp (uri->scheme, "kdns")) + err = ks_print_help (ctrl, data); + else + err = 0; + + return err; +} + + +/* Get the key from URI which is expected to specify a kdns scheme. + On success R_FP has an open stream to read the data. */ +gpg_error_t +ks_kdns_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp) +{ + gpg_error_t err; + + (void)ctrl; + *r_fp = NULL; + + if (strcmp (uri->scheme, "kdns")) + return gpg_error (GPG_ERR_INV_ARG); + + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + return err; +} diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c new file mode 100644 index 0000000..ee55bf2 --- /dev/null +++ b/dirmngr/ks-engine-ldap.c @@ -0,0 +1,2101 @@ +/* ks-engine-ldap.c - talk to a LDAP keyserver + * Copyright (C) 2001, 2002, 2004, 2005, 2006 + * 2007 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +# include +#endif +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# ifdef NEED_LBER_H +# include +# endif +/* For OpenLDAP, to enable the API that we're using. */ +# define LDAP_DEPRECATED 1 +# include +#endif + +#include "dirmngr.h" +#include "misc.h" +#include "userids.h" +#include "ks-engine.h" +#include "ldap-parse-uri.h" + +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *tm); +#endif + +/* Convert an LDAP error to a GPG error. */ +static int +ldap_err_to_gpg_err (int code) +{ + gpg_err_code_t ec; + + switch (code) + { +#ifdef LDAP_X_CONNECTING + case LDAP_X_CONNECTING: ec = GPG_ERR_LDAP_X_CONNECTING; break; +#endif + + case LDAP_REFERRAL_LIMIT_EXCEEDED: ec = GPG_ERR_LDAP_REFERRAL_LIMIT; break; + case LDAP_CLIENT_LOOP: ec = GPG_ERR_LDAP_CLIENT_LOOP; break; + case LDAP_NO_RESULTS_RETURNED: ec = GPG_ERR_LDAP_NO_RESULTS; break; + case LDAP_CONTROL_NOT_FOUND: ec = GPG_ERR_LDAP_CONTROL_NOT_FOUND; break; + case LDAP_NOT_SUPPORTED: ec = GPG_ERR_LDAP_NOT_SUPPORTED; break; + case LDAP_CONNECT_ERROR: ec = GPG_ERR_LDAP_CONNECT; break; + case LDAP_NO_MEMORY: ec = GPG_ERR_LDAP_NO_MEMORY; break; + case LDAP_PARAM_ERROR: ec = GPG_ERR_LDAP_PARAM; break; + case LDAP_USER_CANCELLED: ec = GPG_ERR_LDAP_USER_CANCELLED; break; + case LDAP_FILTER_ERROR: ec = GPG_ERR_LDAP_FILTER; break; + case LDAP_AUTH_UNKNOWN: ec = GPG_ERR_LDAP_AUTH_UNKNOWN; break; + case LDAP_TIMEOUT: ec = GPG_ERR_LDAP_TIMEOUT; break; + case LDAP_DECODING_ERROR: ec = GPG_ERR_LDAP_DECODING; break; + case LDAP_ENCODING_ERROR: ec = GPG_ERR_LDAP_ENCODING; break; + case LDAP_LOCAL_ERROR: ec = GPG_ERR_LDAP_LOCAL; break; + case LDAP_SERVER_DOWN: ec = GPG_ERR_LDAP_SERVER_DOWN; break; + + case LDAP_SUCCESS: ec = GPG_ERR_LDAP_SUCCESS; break; + + case LDAP_OPERATIONS_ERROR: ec = GPG_ERR_LDAP_OPERATIONS; break; + case LDAP_PROTOCOL_ERROR: ec = GPG_ERR_LDAP_PROTOCOL; break; + case LDAP_TIMELIMIT_EXCEEDED: ec = GPG_ERR_LDAP_TIMELIMIT; break; + case LDAP_SIZELIMIT_EXCEEDED: ec = GPG_ERR_LDAP_SIZELIMIT; break; + case LDAP_COMPARE_FALSE: ec = GPG_ERR_LDAP_COMPARE_FALSE; break; + case LDAP_COMPARE_TRUE: ec = GPG_ERR_LDAP_COMPARE_TRUE; break; + case LDAP_AUTH_METHOD_NOT_SUPPORTED: ec=GPG_ERR_LDAP_UNSUPPORTED_AUTH;break; + case LDAP_STRONG_AUTH_REQUIRED: ec = GPG_ERR_LDAP_STRONG_AUTH_RQRD; break; + case LDAP_PARTIAL_RESULTS: ec = GPG_ERR_LDAP_PARTIAL_RESULTS; break; + case LDAP_REFERRAL: ec = GPG_ERR_LDAP_REFERRAL; break; + +#ifdef LDAP_ADMINLIMIT_EXCEEDED + case LDAP_ADMINLIMIT_EXCEEDED: ec = GPG_ERR_LDAP_ADMINLIMIT; break; +#endif + +#ifdef LDAP_UNAVAILABLE_CRITICAL_EXTENSION + case LDAP_UNAVAILABLE_CRITICAL_EXTENSION: + ec = GPG_ERR_LDAP_UNAVAIL_CRIT_EXTN; break; +#endif + + case LDAP_CONFIDENTIALITY_REQUIRED: ec = GPG_ERR_LDAP_CONFIDENT_RQRD; break; + case LDAP_SASL_BIND_IN_PROGRESS: ec = GPG_ERR_LDAP_SASL_BIND_INPROG; break; + case LDAP_NO_SUCH_ATTRIBUTE: ec = GPG_ERR_LDAP_NO_SUCH_ATTRIBUTE; break; + case LDAP_UNDEFINED_TYPE: ec = GPG_ERR_LDAP_UNDEFINED_TYPE; break; + case LDAP_INAPPROPRIATE_MATCHING: ec = GPG_ERR_LDAP_BAD_MATCHING; break; + case LDAP_CONSTRAINT_VIOLATION: ec = GPG_ERR_LDAP_CONST_VIOLATION; break; + +#ifdef LDAP_TYPE_OR_VALUE_EXISTS + case LDAP_TYPE_OR_VALUE_EXISTS: ec = GPG_ERR_LDAP_TYPE_VALUE_EXISTS; break; +#endif + + case LDAP_INVALID_SYNTAX: ec = GPG_ERR_LDAP_INV_SYNTAX; break; + case LDAP_NO_SUCH_OBJECT: ec = GPG_ERR_LDAP_NO_SUCH_OBJ; break; + case LDAP_ALIAS_PROBLEM: ec = GPG_ERR_LDAP_ALIAS_PROBLEM; break; + case LDAP_INVALID_DN_SYNTAX: ec = GPG_ERR_LDAP_INV_DN_SYNTAX; break; + case LDAP_IS_LEAF: ec = GPG_ERR_LDAP_IS_LEAF; break; + case LDAP_ALIAS_DEREF_PROBLEM: ec = GPG_ERR_LDAP_ALIAS_DEREF; break; + +#ifdef LDAP_X_PROXY_AUTHZ_FAILURE + case LDAP_X_PROXY_AUTHZ_FAILURE: ec = GPG_ERR_LDAP_X_PROXY_AUTH_FAIL; break; +#endif + + case LDAP_INAPPROPRIATE_AUTH: ec = GPG_ERR_LDAP_BAD_AUTH; break; + case LDAP_INVALID_CREDENTIALS: ec = GPG_ERR_LDAP_INV_CREDENTIALS; break; + +#ifdef LDAP_INSUFFICIENT_ACCESS + case LDAP_INSUFFICIENT_ACCESS: ec = GPG_ERR_LDAP_INSUFFICIENT_ACC; break; +#endif + + case LDAP_BUSY: ec = GPG_ERR_LDAP_BUSY; break; + case LDAP_UNAVAILABLE: ec = GPG_ERR_LDAP_UNAVAILABLE; break; + case LDAP_UNWILLING_TO_PERFORM: ec = GPG_ERR_LDAP_UNWILL_TO_PERFORM; break; + case LDAP_LOOP_DETECT: ec = GPG_ERR_LDAP_LOOP_DETECT; break; + case LDAP_NAMING_VIOLATION: ec = GPG_ERR_LDAP_NAMING_VIOLATION; break; + case LDAP_OBJECT_CLASS_VIOLATION: ec = GPG_ERR_LDAP_OBJ_CLS_VIOLATION; break; + case LDAP_NOT_ALLOWED_ON_NONLEAF: ec=GPG_ERR_LDAP_NOT_ALLOW_NONLEAF;break; + case LDAP_NOT_ALLOWED_ON_RDN: ec = GPG_ERR_LDAP_NOT_ALLOW_ON_RDN; break; + case LDAP_ALREADY_EXISTS: ec = GPG_ERR_LDAP_ALREADY_EXISTS; break; + case LDAP_NO_OBJECT_CLASS_MODS: ec = GPG_ERR_LDAP_NO_OBJ_CLASS_MODS; break; + case LDAP_RESULTS_TOO_LARGE: ec = GPG_ERR_LDAP_RESULTS_TOO_LARGE; break; + case LDAP_AFFECTS_MULTIPLE_DSAS: ec = GPG_ERR_LDAP_AFFECTS_MULT_DSAS; break; + +#ifdef LDAP_VLV_ERROR + case LDAP_VLV_ERROR: ec = GPG_ERR_LDAP_VLV; break; +#endif + + case LDAP_OTHER: ec = GPG_ERR_LDAP_OTHER; break; + +#ifdef LDAP_CUP_RESOURCES_EXHAUSTED + case LDAP_CUP_RESOURCES_EXHAUSTED: ec=GPG_ERR_LDAP_CUP_RESOURCE_LIMIT;break; + case LDAP_CUP_SECURITY_VIOLATION: ec=GPG_ERR_LDAP_CUP_SEC_VIOLATION; break; + case LDAP_CUP_INVALID_DATA: ec = GPG_ERR_LDAP_CUP_INV_DATA; break; + case LDAP_CUP_UNSUPPORTED_SCHEME: ec = GPG_ERR_LDAP_CUP_UNSUP_SCHEME; break; + case LDAP_CUP_RELOAD_REQUIRED: ec = GPG_ERR_LDAP_CUP_RELOAD; break; +#endif + +#ifdef LDAP_CANCELLED + case LDAP_CANCELLED: ec = GPG_ERR_LDAP_CANCELLED; break; +#endif + +#ifdef LDAP_NO_SUCH_OPERATION + case LDAP_NO_SUCH_OPERATION: ec = GPG_ERR_LDAP_NO_SUCH_OPERATION; break; +#endif + +#ifdef LDAP_TOO_LATE + case LDAP_TOO_LATE: ec = GPG_ERR_LDAP_TOO_LATE; break; +#endif + +#ifdef LDAP_CANNOT_CANCEL + case LDAP_CANNOT_CANCEL: ec = GPG_ERR_LDAP_CANNOT_CANCEL; break; +#endif + +#ifdef LDAP_ASSERTION_FAILED + case LDAP_ASSERTION_FAILED: ec = GPG_ERR_LDAP_ASSERTION_FAILED; break; +#endif + +#ifdef LDAP_PROXIED_AUTHORIZATION_DENIED + case LDAP_PROXIED_AUTHORIZATION_DENIED: + ec = GPG_ERR_LDAP_PROX_AUTH_DENIED; break; +#endif + + default: +#if defined(LDAP_E_ERROR) && defined(LDAP_X_ERROR) + if (LDAP_E_ERROR (code)) + ec = GPG_ERR_LDAP_E_GENERAL; + else if (LDAP_X_ERROR (code)) + ec = GPG_ERR_LDAP_X_GENERAL; + else +#endif + ec = GPG_ERR_LDAP_GENERAL; + break; + } + + return ec; +} + +/* Retrieve an LDAP error and return it's GPG equivalent. */ +static int +ldap_to_gpg_err (LDAP *ld) +{ +#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) + int err; + + if (ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &err) == 0) + return ldap_err_to_gpg_err (err); + else + return GPG_ERR_GENERAL; +#elif defined(HAVE_LDAP_LD_ERRNO) + return ldap_err_to_gpg_err (ld->ld_errno); +#else + /* We should never get here since the LDAP library should always + have either ldap_get_option or ld_errno, but just in case... */ + return GPG_ERR_INTERNAL; +#endif +} + +static time_t +ldap2epochtime (const char *timestr) +{ + struct tm pgptime; + time_t answer; + + memset (&pgptime, 0, sizeof(pgptime)); + + /* YYYYMMDDHHmmssZ */ + + sscanf (timestr, "%4d%2d%2d%2d%2d%2d", + &pgptime.tm_year, + &pgptime.tm_mon, + &pgptime.tm_mday, + &pgptime.tm_hour, + &pgptime.tm_min, + &pgptime.tm_sec); + + pgptime.tm_year -= 1900; + pgptime.tm_isdst = -1; + pgptime.tm_mon--; + + /* mktime() takes the timezone into account, so we use timegm() */ + + answer = timegm (&pgptime); + + return answer; +} + +/* Caller must free the result. */ +static char * +tm2ldaptime (struct tm *tm) +{ + struct tm tmp = *tm; + char buf[16]; + + /* YYYYMMDDHHmmssZ */ + + tmp.tm_year += 1900; + tmp.tm_mon ++; + + snprintf (buf, sizeof buf, "%04d%02d%02d%02d%02d%02dZ", + tmp.tm_year, + tmp.tm_mon, + tmp.tm_mday, + tmp.tm_hour, + tmp.tm_min, + tmp.tm_sec); + + return xstrdup (buf); +} + +#if 0 +/* Caller must free */ +static char * +epoch2ldaptime (time_t stamp) +{ + struct tm tm; + if (gmtime_r (&stamp, &tm)) + return tm2ldaptime (&tm); + else + return xstrdup ("INVALID TIME"); +} +#endif + +/* Print a help output for the schemata supported by this module. */ +gpg_error_t +ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri) +{ + const char const data[] = + "Handler for LDAP URLs:\n" + " ldap://host:port/[BASEDN]???[bindname=BINDNAME,password=PASSWORD]\n" + "\n" + "Note: basedn, bindname and password need to be percent escaped. In\n" + "particular, spaces need to be replaced with %20 and commas with %2c.\n" + "bindname will typically be of the form:\n" + "\n" + " uid=user%2cou=PGP%20Users%2cdc=EXAMPLE%2cdc=ORG\n" + "\n" + "The ldaps:// and ldapi:// schemes are also supported. If ldaps is used\n" + "then the server's certificate will be checked. If it is not valid, any\n" + "operation will be aborted.\n" + "\n" + "Supported methods: search, get, put\n"; + gpg_error_t err; + + if(!uri) + err = ks_print_help (ctrl, " ldap"); + else if (strcmp (uri->scheme, "ldap") == 0 + || strcmp (uri->scheme, "ldaps") == 0 + || strcmp (uri->scheme, "ldapi") == 0) + err = ks_print_help (ctrl, data); + else + err = 0; + + return err; +} + +/* Convert a keyspec to a filter. Return an error if the keyspec is + bad or is not supported. The filter is escaped and returned in + *filter. It is the caller's responsibility to free *filter. + *filter is only set if this function returns success (i.e., 0). */ +static gpg_error_t +keyspec_to_ldap_filter (const char *keyspec, char **filter, int only_exact) +{ + /* Remove search type indicator and adjust PATTERN accordingly. + Note: don't include a preceding 0x when searching by keyid. */ + + /* XXX: Should we include disabled / revoke options? */ + KEYDB_SEARCH_DESC desc; + char *f = NULL; + char *freeme = NULL; + + gpg_error_t err = classify_user_id (keyspec, &desc, 1); + if (err) + return err; + + switch (desc.mode) + { + case KEYDB_SEARCH_MODE_EXACT: + f = xasprintf ("(pgpUserID=%s)", + (freeme = ldap_escape_filter (desc.u.name))); + break; + + case KEYDB_SEARCH_MODE_SUBSTR: + if (! only_exact) + f = xasprintf ("(pgpUserID=*%s*)", + (freeme = ldap_escape_filter (desc.u.name))); + break; + + case KEYDB_SEARCH_MODE_MAIL: + if (! only_exact) + f = xasprintf ("(pgpUserID=*<%s>*)", + (freeme = ldap_escape_filter (desc.u.name))); + break; + + case KEYDB_SEARCH_MODE_MAILSUB: + if (! only_exact) + f = xasprintf ("(pgpUserID=*<*%s*>*)", + (freeme = ldap_escape_filter (desc.u.name))); + break; + + case KEYDB_SEARCH_MODE_MAILEND: + if (! only_exact) + f = xasprintf ("(pgpUserID=*<*%s>*)", + (freeme = ldap_escape_filter (desc.u.name))); + break; + + case KEYDB_SEARCH_MODE_SHORT_KID: + f = xasprintf ("(pgpKeyID=%08lX)", (ulong) desc.u.kid[1]); + break; + case KEYDB_SEARCH_MODE_LONG_KID: + f = xasprintf ("(pgpCertID=%08lX%08lX)", + (ulong) desc.u.kid[0], (ulong) desc.u.kid[1]); + break; + + case KEYDB_SEARCH_MODE_FPR16: + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + case KEYDB_SEARCH_MODE_ISSUER: + case KEYDB_SEARCH_MODE_ISSUER_SN: + case KEYDB_SEARCH_MODE_SN: + case KEYDB_SEARCH_MODE_SUBJECT: + case KEYDB_SEARCH_MODE_KEYGRIP: + case KEYDB_SEARCH_MODE_WORDS: + case KEYDB_SEARCH_MODE_FIRST: + case KEYDB_SEARCH_MODE_NEXT: + default: + break; + } + + xfree (freeme); + + if (! f) + { + log_error ("Unsupported search mode.\n"); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + *filter = f; + + return 0; +} + + + +/* Connect to an LDAP server and interrogate it. + + - uri describes the server to connect to and various options + including whether to use TLS and the username and password (see + ldap_parse_uri for a description of the various fields). + + This function returns: + + - The ldap connection handle in *LDAP_CONNP. + + - The base DN for the PGP key space by querying the + pgpBaseKeySpaceDN attribute (This is normally + 'ou=PGP Keys,dc=EXAMPLE,dc=ORG'). + + - The attribute to lookup to find the pgp key. This is either + 'pgpKey' or 'pgpKeyV2'. + + - Whether this is a real ldap server. (It's unclear what this + exactly means.) + + The values are returned in the passed variables. If you pass NULL, + then the value won't be returned. It is the caller's + responsibility to release *LDAP_CONNP with ldap_unbind and xfree + *BASEDNP and *PGPKEYATTRP. + + If this function successfully interrogated the server, it returns + 0. If there was an LDAP error, it returns the LDAP error code. If + an error occurred, *basednp, etc., are undefined (and don't need to + be freed.) + + If no LDAP error occurred, you still need to check that *basednp is + valid. If it is NULL, then the server does not appear to be an + OpenPGP Keyserver. In this case, you also do not need to xfree + *pgpkeyattrp. */ +static int +my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, + char **basednp, char **pgpkeyattrp, int *real_ldapp) +{ + int err = 0; + + LDAP *ldap_conn = NULL; + + char *user = uri->auth; + struct uri_tuple_s *password_param = uri_query_lookup (uri, "password"); + char *password = password_param ? password_param->value : NULL; + + char *basedn = NULL; + /* Whether to look for the pgpKey or pgpKeyv2 attribute. */ + char *pgpkeyattr = "pgpKey"; + int real_ldap = 0; + + log_debug ("my_ldap_connect(%s:%d/%s????%s%s%s%s%s)\n", + uri->host, uri->port, + uri->path ?: "", + uri->auth ? "bindname=" : "", uri->auth ?: "", + uri->auth && password ? "," : "", + password ? "password=" : "", password ?: ""); + + /* If the uri specifies a secure connection and we don't support + TLS, then fail; don't silently revert to an insecure + connection. */ + if (uri->use_tls) + { +#ifndef HAVE_LDAP_START_TLS_S + log_error ("Can't use LDAP to connect to the server: no TLS support."); + err = GPG_ERR_LDAP_NOT_SUPPORTED; + goto out; +#endif + } + + ldap_conn = ldap_init (uri->host, uri->port); + if (! ldap_conn) + { + err = gpg_err_code_from_syserror (); + log_error ("Failed to open connection to LDAP server (%s://%s:%d)\n", + uri->scheme, uri->host, uri->port); + goto out; + } + +#ifdef HAVE_LDAP_SET_OPTION + { + int ver = LDAP_VERSION3; + + err = ldap_set_option (ldap_conn, LDAP_OPT_PROTOCOL_VERSION, &ver); + if (err != LDAP_SUCCESS) + { + log_error ("gpgkeys: unable to go to LDAP 3: %s\n", + ldap_err2string (err)); + goto out; + } + } +#endif + + /* XXX: It would be nice to have an option to provide the server's + certificate. */ +#if 0 +#if defined(LDAP_OPT_X_TLS_CACERTFILE) && defined(HAVE_LDAP_SET_OPTION) + err = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, ca_cert_file); + if (err) + { + log_error ("unable to set ca-cert-file to '%s': %s\n", + ca_cert_file, ldap_err2string (err)); + goto out; + } +#endif /* LDAP_OPT_X_TLS_CACERTFILE && HAVE_LDAP_SET_OPTION */ +#endif + +#ifdef HAVE_LDAP_START_TLS_S + if (uri->use_tls) + { + /* XXX: We need an option to determine whether to abort if the + certificate is bad or not. Right now we conservatively + default to checking the certificate and aborting. */ +#ifndef HAVE_W32_SYSTEM + int check_cert = LDAP_OPT_X_TLS_HARD; /* LDAP_OPT_X_TLS_NEVER */ + + err = ldap_set_option (ldap_conn, + LDAP_OPT_X_TLS_REQUIRE_CERT, &check_cert); + if (err) + { + log_error ("Failed to set TLS option on LDAP connection.\n"); + goto out; + } +#else + /* On Windows, the certificates are checked by default. If the + option to disable checking mentioned above is ever + implemented, the way to do that on Windows is to install a + callback routine using ldap_set_option (.., + LDAP_OPT_SERVER_CERTIFICATE, ..); */ +#endif + + err = ldap_start_tls_s (ldap_conn, +#ifdef HAVE_W32_SYSTEM + /* ServerReturnValue, result */ + NULL, NULL, +#endif + /* ServerControls, ClientControls */ + NULL, NULL); + if (err) + { + log_error ("Failed to connect to LDAP server with TLS.\n"); + goto out; + } + } +#endif + + /* By default we don't bind as there is usually no need to. */ + if (uri->auth) + { + log_debug ("LDAP bind to %s, password %s\n", + user, password ? ">not shown<" : ">none<"); + + err = ldap_simple_bind_s (ldap_conn, user, password); + if (err != LDAP_SUCCESS) + { + log_error ("Internal LDAP bind error: %s\n", + ldap_err2string (err)); + goto out; + } + } + + if (uri->path && *uri->path) + /* User specified base DN. */ + { + basedn = xstrdup (uri->path); + + /* If the user specifies a base DN, then we know the server is a + real LDAP server. */ + real_ldap = 1; + } + else + { + LDAPMessage *res = NULL; + /* Look for namingContexts. */ + char *attr[] = { "namingContexts", NULL }; + + err = ldap_search_s (ldap_conn, "", LDAP_SCOPE_BASE, + "(objectClass=*)", attr, 0, &res); + if (err == LDAP_SUCCESS) + { + char **context = ldap_get_values (ldap_conn, res, "namingContexts"); + if (context) + /* We found some, so try each namingContext as the search + base and look for pgpBaseKeySpaceDN. Because we found + this, we know we're talking to a regular-ish LDAP + server and not an LDAP keyserver. */ + { + int i; + char *attr2[] = + { "pgpBaseKeySpaceDN", "pgpVersion", "pgpSoftware", NULL }; + + real_ldap = 1; + + for (i = 0; context[i] && ! basedn; i++) + { + char **vals; + LDAPMessage *si_res; + + { + char *object = xasprintf ("cn=pgpServerInfo,%s", + context[i]); + err = ldap_search_s (ldap_conn, object, LDAP_SCOPE_BASE, + "(objectClass=*)", attr2, 0, &si_res); + xfree (object); + } + + if (err == LDAP_SUCCESS) + { + vals = ldap_get_values (ldap_conn, si_res, + "pgpBaseKeySpaceDN"); + if (vals) + { + basedn = xtrystrdup (vals[0]); + ldap_value_free (vals); + } + + vals = ldap_get_values (ldap_conn, si_res, + "pgpSoftware"); + if (vals) + { + log_debug ("Server: \t%s\n", vals[0]); + ldap_value_free (vals); + } + + vals = ldap_get_values (ldap_conn, si_res, + "pgpVersion"); + if (vals) + { + log_debug ("Version:\t%s\n", vals[0]); + ldap_value_free (vals); + } + } + + /* From man ldap_search_s: "res parameter of + ldap_search_ext_s() and ldap_search_s() should be + freed with ldap_msgfree() regardless of return + value of these functions. */ + ldap_msgfree (si_res); + } + + ldap_value_free (context); + } + } + else + { + /* We don't have an answer yet, which means the server might + be an LDAP keyserver. */ + char **vals; + LDAPMessage *si_res = NULL; + + char *attr2[] = { "pgpBaseKeySpaceDN", "version", "software", NULL }; + + err = ldap_search_s (ldap_conn, "cn=pgpServerInfo", LDAP_SCOPE_BASE, + "(objectClass=*)", attr2, 0, &si_res); + if (err == LDAP_SUCCESS) + { + /* For the LDAP keyserver, this is always + "OU=ACTIVE,O=PGP KEYSPACE,C=US", but it might not be + in the future. */ + + vals = ldap_get_values (ldap_conn, si_res, "baseKeySpaceDN"); + if (vals) + { + basedn = xtrystrdup (vals[0]); + ldap_value_free (vals); + } + + vals = ldap_get_values (ldap_conn, si_res, "software"); + if (vals) + { + log_debug ("ldap: Server: \t%s\n", vals[0]); + ldap_value_free (vals); + } + + vals = ldap_get_values (ldap_conn, si_res, "version"); + if (vals) + { + log_debug ("ldap: Version:\t%s\n", vals[0]); + + /* If the version is high enough, use the new + pgpKeyV2 attribute. This design is iffy at best, + but it matches how PGP does it. I figure the NAI + folks assumed that there would never be an LDAP + keyserver vendor with a different numbering + scheme. */ + if (atoi (vals[0]) > 1) + pgpkeyattr = "pgpKeyV2"; + + ldap_value_free (vals); + } + } + + ldap_msgfree (si_res); + } + + /* From man ldap_search_s: "res parameter of ldap_search_ext_s() + and ldap_search_s() should be freed with ldap_msgfree() + regardless of return value of these functions. */ + ldap_msgfree (res); + } + + out: + if (! err) + { + log_debug ("ldap_conn: %p\n", ldap_conn); + log_debug ("real_ldap: %d\n", real_ldap); + log_debug ("basedn: %s\n", basedn); + log_debug ("pgpkeyattr: %s\n", pgpkeyattr); + } + + if (! err && real_ldapp) + *real_ldapp = real_ldap; + + if (err) + xfree (basedn); + else + { + if (pgpkeyattrp) + { + if (basedn) + *pgpkeyattrp = xstrdup (pgpkeyattr); + else + *pgpkeyattrp = NULL; + } + + if (basednp) + *basednp = basedn; + else + xfree (basedn); + } + + if (err) + { + if (ldap_conn) + ldap_unbind (ldap_conn); + } + else + *ldap_connp = ldap_conn; + + return err; +} + +/* Extract keys from an LDAP reply and write them out to the output + stream OUTPUT in a format GnuPG can import (either the OpenPGP + binary format or armored format). */ +static void +extract_keys (estream_t output, + LDAP *ldap_conn, const char *certid, LDAPMessage *message) +{ + char **vals; + + es_fprintf (output, "INFO %s BEGIN\n", certid); + es_fprintf (output, "pub:%s:", certid); + + /* Note: ldap_get_values returns a NULL terminates array of + strings. */ + vals = ldap_get_values (ldap_conn, message, "pgpkeytype"); + if (vals && vals[0]) + { + if (strcmp (vals[0], "RSA") == 0) + es_fprintf (output, "1"); + else if (strcmp (vals[0],"DSS/DH") == 0) + es_fprintf (output, "17"); + ldap_value_free (vals); + } + + es_fprintf (output, ":"); + + vals = ldap_get_values (ldap_conn, message, "pgpkeysize"); + if (vals && vals[0]) + { + int v = atoi (vals[0]); + if (v > 0) + es_fprintf (output, "%d", v); + ldap_value_free (vals); + } + + es_fprintf (output, ":"); + + vals = ldap_get_values (ldap_conn, message, "pgpkeycreatetime"); + if (vals && vals[0]) + { + if (strlen (vals[0]) == 15) + es_fprintf (output, "%u", (unsigned int) ldap2epochtime (vals[0])); + ldap_value_free (vals); + } + + es_fprintf (output, ":"); + + vals = ldap_get_values (ldap_conn, message, "pgpkeyexpiretime"); + if (vals && vals[0]) + { + if (strlen (vals[0]) == 15) + es_fprintf (output, "%u", (unsigned int) ldap2epochtime (vals[0])); + ldap_value_free (vals); + } + + es_fprintf (output, ":"); + + vals = ldap_get_values (ldap_conn, message, "pgprevoked"); + if (vals && vals[0]) + { + if (atoi (vals[0]) == 1) + es_fprintf (output, "r"); + ldap_value_free (vals); + } + + es_fprintf (output, "\n"); + + vals = ldap_get_values (ldap_conn, message, "pgpuserid"); + if (vals && vals[0]) + { + int i; + for (i = 0; vals[i]; i++) + es_fprintf (output, "uid:%s\n", vals[i]); + ldap_value_free (vals); + } + + es_fprintf (output, "INFO %s END\n", certid); +} + +/* Get the key described key the KEYSPEC string from the keyserver + identified by URI. On success R_FP has an open stream to read the + data. */ +gpg_error_t +ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, + estream_t *r_fp) +{ + gpg_error_t err = 0; + int ldap_err; + + char *filter = NULL; + + LDAP *ldap_conn = NULL; + + char *basedn = NULL; + char *pgpkeyattr = NULL; + + estream_t fp = NULL; + + LDAPMessage *message = NULL; + + (void) ctrl; + + if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("LDAP access not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + /* Before connecting to the server, make sure we have a sane + keyspec. If not, there is no need to establish a network + connection. */ + err = keyspec_to_ldap_filter (keyspec, &filter, 1); + if (err) + return (err); + + /* Make sure we are talking to an OpenPGP LDAP server. */ + ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, &pgpkeyattr, NULL); + if (ldap_err || !basedn) + { + if (ldap_err) + err = ldap_err_to_gpg_err (ldap_err); + else + err = GPG_ERR_GENERAL; + goto out; + } + + { + /* The ordering is significant. Specifically, "pgpcertid" needs + to be the second item in the list, since everything after it + may be discarded we aren't in verbose mode. */ + char *attrs[] = + { + pgpkeyattr, + "pgpcertid", "pgpuserid", "pgpkeyid", "pgprevoked", "pgpdisabled", + "pgpkeycreatetime", "modifytimestamp", "pgpkeysize", "pgpkeytype", + NULL + }; + /* 1 if we want just attribute types; 0 if we want both attribute + types and values. */ + int attrsonly = 0; + + int count; + + ldap_err = ldap_search_s (ldap_conn, basedn, LDAP_SCOPE_SUBTREE, + filter, attrs, attrsonly, &message); + if (ldap_err) + { + err = ldap_err_to_gpg_err (ldap_err); + + log_error ("gpgkeys: LDAP search error: %s\n", + ldap_err2string (ldap_err)); + goto out; + } + + count = ldap_count_entries (ldap_conn, message); + if (count < 1) + { + log_error ("gpgkeys: key %s not found on keyserver\n", keyspec); + + if (count == -1) + err = ldap_to_gpg_err (ldap_conn); + else + err = gpg_error (GPG_ERR_NO_DATA); + + goto out; + } + + { + /* There may be more than one unique result for a given keyID, + so we should fetch them all (test this by fetching short key + id 0xDEADBEEF). */ + + /* The set of entries that we've seen. */ + strlist_t seen = NULL; + LDAPMessage *each; + + for (each = ldap_first_entry (ldap_conn, message); + each; + each = ldap_next_entry (ldap_conn, each)) + { + char **vals; + char **certid; + + /* Use the long keyid to remove duplicates. The LDAP + server returns the same keyid more than once if there + are multiple user IDs on the key. Note that this does + NOT mean that a keyid that exists multiple times on the + keyserver will not be fetched. It means that each KEY, + no matter how many user IDs share its keyid, will be + fetched only once. If a keyid that belongs to more + than one key is fetched, the server quite properly + responds with all matching keys. -ds */ + + certid = ldap_get_values (ldap_conn, each, "pgpcertid"); + if (certid && certid[0]) + { + if (! strlist_find (seen, certid[0])) + { + /* It's not a duplicate, add it */ + + add_to_strlist (&seen, certid[0]); + + if (! fp) + fp = es_fopenmem(0, "rw"); + + extract_keys (fp, ldap_conn, certid[0], each); + + vals = ldap_get_values (ldap_conn, each, pgpkeyattr); + if (! vals) + { + err = ldap_to_gpg_err (ldap_conn); + log_error("gpgkeys: unable to retrieve key %s " + "from keyserver\n", certid[0]); + goto out; + } + else + { + /* We should strip the new lines. */ + es_fprintf (fp, "KEY 0x%s BEGIN\n", certid[0]); + es_fputs (vals[0], fp); + es_fprintf (fp, "\nKEY 0x%s END\n", certid[0]); + + ldap_value_free (vals); + } + } + } + + ldap_value_free (certid); + } + + free_strlist (seen); + + if (! fp) + err = gpg_error (GPG_ERR_NO_DATA); + } + } + + out: + if (message) + ldap_msgfree (message); + + if (err) + { + if (fp) + es_fclose (fp); + } + else + { + if (fp) + es_fseek (fp, 0, SEEK_SET); + + *r_fp = fp; + } + + xfree (pgpkeyattr); + xfree (basedn); + + if (ldap_conn) + ldap_unbind (ldap_conn); + + xfree (filter); + + return err; +} + +/* Search the keyserver identified by URI for keys matching PATTERN. + On success R_FP has an open stream to read the data. */ +gpg_error_t +ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, + estream_t *r_fp) +{ + gpg_error_t err; + int ldap_err; + + char *filter = NULL; + + LDAP *ldap_conn = NULL; + + char *basedn = NULL; + + estream_t fp = NULL; + + (void) ctrl; + + if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("LDAP access not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + /* Before connecting to the server, make sure we have a sane + keyspec. If not, there is no need to establish a network + connection. */ + err = keyspec_to_ldap_filter (pattern, &filter, 0); + if (err) + { + log_error ("Bad search pattern: '%s'\n", pattern); + return (err); + } + + /* Make sure we are talking to an OpenPGP LDAP server. */ + ldap_err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL); + if (ldap_err || !basedn) + { + if (ldap_err) + err = ldap_err_to_gpg_err (ldap_err); + else + err = GPG_ERR_GENERAL; + goto out; + } + + /* Even if we have no results, we want to return a stream. */ + fp = es_fopenmem(0, "rw"); + if (!fp) + { + err = gpg_error_from_syserror (); + goto out; + } + + { + char **vals; + LDAPMessage *res, *each; + int count = 0; + strlist_t dupelist = NULL; + + /* The maximum size of the search, including the optional stuff + and the trailing \0 */ + char *attrs[] = + { + "pgpcertid", "pgpuserid", "pgprevoked", "pgpdisabled", + "pgpkeycreatetime", "pgpkeyexpiretime", "modifytimestamp", + "pgpkeysize", "pgpkeytype", NULL + }; + + log_debug ("SEARCH '%s' => '%s' BEGIN\n", pattern, filter); + + ldap_err = ldap_search_s (ldap_conn, basedn, + LDAP_SCOPE_SUBTREE, filter, attrs, 0, &res); + + xfree (filter); + filter = NULL; + + if (ldap_err != LDAP_SUCCESS && ldap_err != LDAP_SIZELIMIT_EXCEEDED) + { + err = ldap_err_to_gpg_err (ldap_err); + + log_error ("SEARCH %s FAILED %d\n", pattern, err); + log_error ("gpgkeys: LDAP search error: %s\n", + ldap_err2string (err)); + goto out; + } + + /* The LDAP server doesn't return a real count of unique keys, so we + can't use ldap_count_entries here. */ + for (each = ldap_first_entry (ldap_conn, res); + each; + each = ldap_next_entry (ldap_conn, each)) + { + char **certid = ldap_get_values (ldap_conn, each, "pgpcertid"); + if (certid && certid[0] && ! strlist_find (dupelist, certid[0])) + { + add_to_strlist (&dupelist, certid[0]); + count++; + } + } + + if (ldap_err == LDAP_SIZELIMIT_EXCEEDED) + { + if (count == 1) + log_error ("gpgkeys: search results exceeded server limit." + " First 1 result shown.\n"); + else + log_error ("gpgkeys: search results exceeded server limit." + " First %d results shown.\n", count); + } + + free_strlist (dupelist); + dupelist = NULL; + + if (count < 1) + es_fputs ("info:1:0\n", fp); + else + { + es_fprintf (fp, "info:1:%d\n", count); + + for (each = ldap_first_entry (ldap_conn, res); + each; + each = ldap_next_entry (ldap_conn, each)) + { + char **certid; + LDAPMessage *uids; + + certid = ldap_get_values (ldap_conn, each, "pgpcertid"); + if (! certid || ! certid[0]) + continue; + + /* Have we seen this certid before? */ + if (! strlist_find (dupelist, certid[0])) + { + add_to_strlist (&dupelist, certid[0]); + + es_fprintf (fp, "pub:%s:",certid[0]); + + vals = ldap_get_values (ldap_conn, each, "pgpkeytype"); + if (vals) + { + /* The LDAP server doesn't exactly handle this + well. */ + if (strcasecmp (vals[0], "RSA") == 0) + es_fputs ("1", fp); + else if (strcasecmp (vals[0], "DSS/DH") == 0) + es_fputs ("17", fp); + ldap_value_free (vals); + } + + es_fputc (':', fp); + + vals = ldap_get_values (ldap_conn, each, "pgpkeysize"); + if (vals) + { + /* Not sure why, but some keys are listed with a + key size of 0. Treat that like an unknown. */ + if (atoi (vals[0]) > 0) + es_fprintf (fp, "%d", atoi (vals[0])); + ldap_value_free (vals); + } + + es_fputc (':', fp); + + /* YYYYMMDDHHmmssZ */ + + vals = ldap_get_values (ldap_conn, each, "pgpkeycreatetime"); + if(vals && strlen (vals[0]) == 15) + { + es_fprintf (fp, "%u", + (unsigned int) ldap2epochtime(vals[0])); + ldap_value_free (vals); + } + + es_fputc (':', fp); + + vals = ldap_get_values (ldap_conn, each, "pgpkeyexpiretime"); + if (vals && strlen (vals[0]) == 15) + { + es_fprintf (fp, "%u", + (unsigned int) ldap2epochtime (vals[0])); + ldap_value_free (vals); + } + + es_fputc (':', fp); + + vals = ldap_get_values (ldap_conn, each, "pgprevoked"); + if (vals) + { + if (atoi (vals[0]) == 1) + es_fprintf (fp, "r"); + ldap_value_free (vals); + } + + vals = ldap_get_values (ldap_conn, each, "pgpdisabled"); + if (vals) + { + if (atoi (vals[0]) ==1) + es_fprintf (fp, "d"); + ldap_value_free (vals); + } + +#if 0 + /* This is not yet specified in the keyserver + protocol, but may be someday. */ + es_fputc (':', fp); + + vals = ldap_get_values (ldap_conn, each, "modifytimestamp"); + if(vals && strlen (vals[0]) == 15) + { + es_fprintf (fp, "%u", + (unsigned int) ldap2epochtime (vals[0])); + ldap_value_free (vals); + } +#endif + + es_fprintf (fp, "\n"); + + /* Now print all the uids that have this certid */ + for (uids = ldap_first_entry (ldap_conn, res); + uids; + uids = ldap_next_entry (ldap_conn, uids)) + { + vals = ldap_get_values (ldap_conn, uids, "pgpcertid"); + if (! vals) + continue; + + if (strcasecmp (certid[0], vals[0]) == 0) + { + char **uidvals; + + es_fprintf (fp, "uid:"); + + uidvals = ldap_get_values (ldap_conn, + uids, "pgpuserid"); + if (uidvals) + { + /* Need to escape any colons */ + char *quoted = percent_escape (uidvals[0], NULL); + es_fputs (quoted, fp); + xfree (quoted); + ldap_value_free (uidvals); + } + + es_fprintf (fp, "\n"); + } + + ldap_value_free(vals); + } + } + + ldap_value_free (certid); + } + } + + ldap_msgfree (res); + free_strlist (dupelist); + } + + log_debug ("SEARCH %s END\n", pattern); + + out: + if (err) + { + if (fp) + es_fclose (fp); + } + else + { + /* Return the read stream. */ + if (fp) + es_fseek (fp, 0, SEEK_SET); + + *r_fp = fp; + } + + xfree (basedn); + + if (ldap_conn) + ldap_unbind (ldap_conn); + + xfree (filter); + + return err; +} + + + +/* A modlist describes a set of changes to an LDAP entry. (An entry + consists of 1 or more attributes. Attributes are + pairs. Note: an attribute may be multi-valued in which case + multiple values are associated with a single name.) + + A modlist is a NULL terminated array of struct LDAPMod's. + + Thus, if we have: + + LDAPMod **modlist; + + Then: + + modlist[i] + + Is the ith modification. + + Each LDAPMod describes a change to a single attribute. Further, + there is one modification for each attribute that we want to + change. The attribute's new value is stored in LDAPMod.mod_values. + If the attribute is multi-valued, we still only use a single + LDAPMod structure: mod_values is a NULL-terminated array of + strings. To delete an attribute from an entry, we set mod_values + to NULL. + + Thus, if: + + modlist[i]->mod_values == NULL + + then we remove the attribute. + + (Using LDAP_MOD_DELETE doesn't work here as we don't know if the + attribute in question exists or not.) + + Note: this function does NOT copy or free ATTR. It does copy + VALUE. */ +static void +modlist_add (LDAPMod ***modlistp, char *attr, const char *value) +{ + LDAPMod **modlist = *modlistp; + + LDAPMod **m; + int nummods = 0; + + /* Search modlist for the attribute we're playing with. If modlist + is NULL, then the list is empty. Recall: modlist is a NULL + terminated array. */ + for (m = modlist; m && *m; m++, nummods ++) + { + /* The attribute is already on the list. */ + char **ptr; + int numvalues = 0; + + if (strcasecmp ((*m)->mod_type, attr) != 0) + continue; + + /* We have this attribute already, so when the REPLACE happens, + the server attributes will be replaced anyway. */ + if (! value) + return; + + /* Attributes can be multi-valued. See if the value is already + present. mod_values is a NULL terminated array of pointers. + Note: mod_values can be NULL. */ + for (ptr = (*m)->mod_values; ptr && *ptr; ptr++) + { + if (strcmp (*ptr, value) == 0) + /* Duplicate value, we're done. */ + return; + numvalues ++; + } + + /* Append the value. */ + ptr = xrealloc ((*m)->mod_values, sizeof (char *) * (numvalues + 2)); + + (*m)->mod_values = ptr; + ptr[numvalues] = xstrdup (value); + + ptr[numvalues + 1] = NULL; + + return; + } + + /* We didn't find the attr, so make one and add it to the end */ + + /* Like attribute values, the list of attributes is NULL terminated + array of pointers. */ + modlist = xrealloc (modlist, sizeof (LDAPMod *) * (nummods + 2)); + + *modlistp = modlist; + modlist[nummods] = xmalloc (sizeof (LDAPMod)); + + modlist[nummods]->mod_op = LDAP_MOD_REPLACE; + modlist[nummods]->mod_type = attr; + if (value) + { + modlist[nummods]->mod_values = xmalloc (sizeof(char *) * 2); + + modlist[nummods]->mod_values[0] = xstrdup (value); + modlist[nummods]->mod_values[1] = NULL; + } + else + modlist[nummods]->mod_values = NULL; + + modlist[nummods + 1] = NULL; + + return; +} + +/* Look up the value of an attribute in the specified modlist. If the + attribute is not on the mod list, returns NULL. The result is a + NULL-terminated array of strings. Don't change it. */ +static char ** +modlist_lookup (LDAPMod **modlist, const char *attr) +{ + LDAPMod **m; + for (m = modlist; m && *m; m++) + { + if (strcasecmp ((*m)->mod_type, attr) != 0) + continue; + + return (*m)->mod_values; + } + + return NULL; +} + +/* Dump a modlist to a file. This is useful for debugging. */ +static estream_t modlist_dump (LDAPMod **modlist, estream_t output) + GPGRT_ATTR_USED; + +static estream_t +modlist_dump (LDAPMod **modlist, estream_t output) +{ + LDAPMod **m; + + int opened = 0; + + if (! output) + { + output = es_fopenmem (0, "rw"); + if (!output) + return NULL; + opened = 1; + } + + for (m = modlist; m && *m; m++) + { + es_fprintf (output, " %s:", (*m)->mod_type); + + if (! (*m)->mod_values) + es_fprintf(output, " delete.\n"); + else + { + char **ptr; + int i; + + int multi = 0; + if ((*m)->mod_values[0] && (*m)->mod_values[1]) + /* Have at least 2. */ + multi = 1; + + if (multi) + es_fprintf (output, "\n"); + + for ((ptr = (*m)->mod_values), (i = 1); ptr && *ptr; ptr++, i ++) + { + /* Assuming terminals are about 80 characters wide, + display at most most about 10 lines of debugging + output. If we do trim the buffer, append '...' to + the end. */ + const int max_len = 10 * 70; + size_t value_len = strlen (*ptr); + int elide = value_len > max_len; + + if (multi) + es_fprintf (output, " %d. ", i); + es_fprintf (output, "`%.*s", max_len, *ptr); + if (elide) + es_fprintf (output, "...' (%zd bytes elided)", + value_len - max_len); + else + es_fprintf (output, "'"); + es_fprintf (output, "\n"); + } + } + } + + if (opened) + es_fseek (output, 0, SEEK_SET); + + return output; +} + +/* Free all of the memory allocated by the mod list. This assumes + that the attribute names don't have to be freed, but the attributes + values do. (Which is what modlist_add does.) */ +static void +modlist_free (LDAPMod **modlist) +{ + LDAPMod **ml; + + if (! modlist) + return; + + /* Unwind and free the whole modlist structure */ + + /* The modlist is a NULL terminated array of pointers. */ + for (ml = modlist; *ml; ml++) + { + LDAPMod *mod = *ml; + char **ptr; + + /* The list of values is a NULL termianted array of pointers. + If the list is NULL, there are no values. */ + + if (mod->mod_values) + { + for (ptr = mod->mod_values; *ptr; ptr++) + xfree (*ptr); + + xfree (mod->mod_values); + } + + xfree (mod); + } + xfree (modlist); +} + +/* Append two onto the end of one. Two is not freed, but its pointers + are now part of one. Make sure you don't free them both! + + As long as you don't add anything to ONE, TWO is still valid. + After that all bets are off. */ +static void +modlists_join (LDAPMod ***one, LDAPMod **two) +{ + int i, one_count = 0, two_count = 0; + LDAPMod **grow; + + if (!*two) + /* two is empty. Nothing to do. */ + return; + + if (!*one) + /* one is empty. Just set it equal to *two. */ + { + *one = two; + return; + } + + for (grow = *one; *grow; grow++) + one_count ++; + + for (grow = two; *grow; grow++) + two_count ++; + + grow = xrealloc (*one, sizeof(LDAPMod *) * (one_count + two_count + 1)); + + for (i = 0; i < two_count; i++) + grow[one_count + i] = two[i]; + + grow[one_count + i] = NULL; + + *one = grow; +} + +/* Given a string, unescape C escapes. In particular, \xXX. This + modifies the string in place. */ +static void +uncescape (char *str) +{ + size_t r = 0; + size_t w = 0; + + char *first = strchr (str, '\\'); + if (! first) + /* No backslashes => no escaping. We're done. */ + return; + + /* Start at the first '\\'. */ + r = w = (uintptr_t) first - (uintptr_t) str; + + while (str[r]) + { + /* XXX: What to do about bad escapes? + XXX: hextobyte already checks the string thus the hexdigitp + could be removed. */ + if (str[r] == '\\' && str[r + 1] == 'x' + && str[r+2] && str[r+3] + && hexdigitp (str + r + 2) + && hexdigitp (str + r + 3)) + { + int x = hextobyte (&str[r + 2]); + assert (0 <= x && x <= 0xff); + + str[w] = x; + + /* We consumed 4 characters and wrote 1. */ + r += 4; + w ++; + } + else + str[w ++] = str[r ++]; + } + + str[w] = '\0'; +} + +/* Given one line from an info block (`gpg --list-{keys,sigs} + --with-colons KEYID'), pull it apart and fill in the modlist with + the relevant (for the LDAP schema) attributes. */ +static void +extract_attributes (LDAPMod ***modlist, char *line) +{ + int field_count; + char **fields; + + char *keyid; + + int is_pub, is_sub, is_uid, is_sig; + + /* Remove trailing whitespace */ + trim_trailing_spaces (line); + + fields = strsplit (line, ':', '\0', &field_count); + if (field_count == 1) + /* We only have a single field. There is definitely nothing to + do. */ + goto out; + + if (field_count < 7) + goto out; + + is_pub = strcasecmp ("pub", fields[0]) == 0; + is_sub = strcasecmp ("sub", fields[0]) == 0; + is_uid = strcasecmp ("uid", fields[0]) == 0; + is_sig = strcasecmp ("sig", fields[0]) == 0; + + if (!is_pub && !is_sub && !is_uid && !is_sig) + /* Not a relevant line. */ + goto out; + + keyid = fields[4]; + + if (is_uid && strlen (keyid) == 0) + /* The uid record type can have an empty keyid. */ + ; + else if (strlen (keyid) == 16 + && strspn (keyid, "0123456789aAbBcCdDeEfF") == 16) + /* Otherwise, we expect exactly 16 hex characters. */ + ; + else + { + log_error ("malformed record!\n"); + goto out; + } + + if (is_pub) + { + int disabled = 0; + int revoked = 0; + char *flags; + for (flags = fields[1]; *flags; flags ++) + switch (*flags) + { + case 'r': + case 'R': + revoked = 1; + break; + + case 'd': + case 'D': + disabled = 1; + break; + } + + /* Note: we always create the pgpDisabled and pgpRevoked + attributes, regardless of whether the key is disabled/revoked + or not. This is because a very common search is like + "(&(pgpUserID=*isabella*)(pgpDisabled=0))" */ + + if (is_pub) + { + modlist_add (modlist,"pgpDisabled", disabled ? "1" : "0"); + modlist_add (modlist,"pgpRevoked", revoked ? "1" : "0"); + } + } + + if (is_pub || is_sub) + { + char *size = fields[2]; + int val = atoi (size); + size = NULL; + + if (val > 0) + { + /* We zero pad this on the left to make PGP happy. */ + char padded[6]; + if (val < 99999 && val > 0) + { + snprintf (padded, sizeof padded, "%05u", val); + size = padded; + } + } + + if (size) + { + if (is_pub || is_sub) + modlist_add (modlist, "pgpKeySize", size); + } + } + + if (is_pub) + { + char *algo = fields[3]; + int val = atoi (algo); + switch (val) + { + case 1: + algo = "RSA"; + break; + + case 17: + algo = "DSS/DH"; + break; + + default: + algo = NULL; + break; + } + + if (algo) + { + if (is_pub) + modlist_add (modlist, "pgpKeyType", algo); + } + } + + if (is_pub || is_sub || is_sig) + { + if (is_pub) + { + modlist_add (modlist, "pgpCertID", keyid); + modlist_add (modlist, "pgpKeyID", &keyid[8]); + } + + if (is_sub) + modlist_add (modlist, "pgpSubKeyID", keyid); + + if (is_sig) + modlist_add (modlist, "pgpSignerID", keyid); + } + + if (is_pub) + { + char *create_time = fields[5]; + + if (strlen (create_time) == 0) + create_time = NULL; + else + { + char *create_time_orig = create_time; + struct tm tm; + time_t t; + char *end; + + memset (&tm, 0, sizeof (tm)); + + /* parse_timestamp handles both seconds fromt he epoch and + ISO 8601 format. We also need to handle YYYY-MM-DD + format (as generated by gpg1 --with-colons --list-key). + Check that first and then if it fails, then try + parse_timestamp. */ + + if (!isodate_human_to_tm (create_time, &tm)) + create_time = tm2ldaptime (&tm); + else if ((t = parse_timestamp (create_time, &end)) != (time_t) -1 + && *end == '\0') + { + + if (!gnupg_gmtime (&t, &tm)) + create_time = NULL; + else + create_time = tm2ldaptime (&tm); + } + else + create_time = NULL; + + if (! create_time) + /* Failed to parse string. */ + log_error ("Failed to parse creation time ('%s')", + create_time_orig); + } + + if (create_time) + { + modlist_add (modlist, "pgpKeyCreateTime", create_time); + xfree (create_time); + } + } + + if (is_pub) + { + char *expire_time = fields[6]; + + if (strlen (expire_time) == 0) + expire_time = NULL; + else + { + char *expire_time_orig = expire_time; + struct tm tm; + time_t t; + char *end; + + memset (&tm, 0, sizeof (tm)); + + /* parse_timestamp handles both seconds fromt he epoch and + ISO 8601 format. We also need to handle YYYY-MM-DD + format (as generated by gpg1 --with-colons --list-key). + Check that first and then if it fails, then try + parse_timestamp. */ + + if (!isodate_human_to_tm (expire_time, &tm)) + expire_time = tm2ldaptime (&tm); + else if ((t = parse_timestamp (expire_time, &end)) != (time_t) -1 + && *end == '\0') + { + if (!gnupg_gmtime (&t, &tm)) + expire_time = NULL; + else + expire_time = tm2ldaptime (&tm); + } + else + expire_time = NULL; + + if (! expire_time) + /* Failed to parse string. */ + log_error ("Failed to parse creation time ('%s')", + expire_time_orig); + } + + if (expire_time) + { + modlist_add (modlist, "pgpKeyExpireTime", expire_time); + xfree (expire_time); + } + } + + if ((is_uid || is_pub) && field_count >= 10) + { + char *uid = fields[9]; + + if (is_pub && strlen (uid) == 0) + /* When using gpg --list-keys, the uid is included. When + passed via gpg, it is not. It is important to process it + when it is present, because gpg 1 won't print a UID record + if there is only one key. */ + ; + else + { + uncescape (uid); + modlist_add (modlist, "pgpUserID", uid); + } + } + + out: + free (fields); +} + +/* Send the key in {KEY,KEYLEN} with the metadata {INFO,INFOLEN} to + the keyserver identified by URI. See server.c:cmd_ks_put for the + format of the data and metadata. */ +gpg_error_t +ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, + void *data, size_t datalen, + void *info, size_t infolen) +{ + gpg_error_t err = 0; + int ldap_err; + + LDAP *ldap_conn = NULL; + char *basedn = NULL; + char *pgpkeyattr = NULL; + int real_ldap; + + LDAPMod **modlist = NULL; + LDAPMod **addlist = NULL; + + char *data_armored = NULL; + + /* The last byte of the info block. */ + const char *infoend = (const char *) info + infolen - 1; + + /* Enable this code to dump the modlist to /tmp/modlist.txt. */ +#if 0 +# warning Disable debug code before checking in. + const int dump_modlist = 1; +#else + const int dump_modlist = 0; +#endif + estream_t dump = NULL; + + /* Elide a warning. */ + (void) ctrl; + + if (opt.use_tor) + { + /* For now we do not support LDAP over Tor. */ + log_error (_("LDAP access not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + ldap_err = my_ldap_connect (uri, + &ldap_conn, &basedn, &pgpkeyattr, &real_ldap); + if (ldap_err || !basedn) + { + if (ldap_err) + err = ldap_err_to_gpg_err (ldap_err); + else + err = GPG_ERR_GENERAL; + goto out; + } + + if (! real_ldap) + /* We appear to have an OpenPGP Keyserver, which can unpack the key + on its own (not just a dumb LDAP server). */ + { + LDAPMod mod, *attrs[2]; + char *key[] = { data, NULL }; + char *dn; + + memset (&mod, 0, sizeof (mod)); + mod.mod_op = LDAP_MOD_ADD; + mod.mod_type = pgpkeyattr; + mod.mod_values = key; + attrs[0] = &mod; + attrs[1] = NULL; + + dn = xasprintf ("pgpCertid=virtual,%s", basedn); + ldap_err = ldap_add_s (ldap_conn, dn, attrs); + xfree (dn); + + if (ldap_err != LDAP_SUCCESS) + { + err = ldap_err_to_gpg_err (err); + goto out; + } + + goto out; + } + + modlist = xmalloc (sizeof (LDAPMod *)); + *modlist = NULL; + + if (dump_modlist) + { + dump = es_fopen("/tmp/modlist.txt", "w"); + if (! dump) + log_error ("Failed to open /tmp/modlist.txt: %s\n", + strerror (errno)); + + if (dump) + { + es_fprintf(dump, "data (%zd bytes)\n", datalen); + es_fprintf(dump, "info (%zd bytes): '\n", infolen); + es_fwrite(info, infolen, 1, dump); + es_fprintf(dump, "'\n"); + } + } + + /* Start by nulling out all attributes. We try and do a modify + operation first, so this ensures that we don't leave old + attributes lying around. */ + modlist_add (&modlist, "pgpDisabled", NULL); + modlist_add (&modlist, "pgpKeyID", NULL); + modlist_add (&modlist, "pgpKeyType", NULL); + modlist_add (&modlist, "pgpUserID", NULL); + modlist_add (&modlist, "pgpKeyCreateTime", NULL); + modlist_add (&modlist, "pgpSignerID", NULL); + modlist_add (&modlist, "pgpRevoked", NULL); + modlist_add (&modlist, "pgpSubKeyID", NULL); + modlist_add (&modlist, "pgpKeySize", NULL); + modlist_add (&modlist, "pgpKeyExpireTime", NULL); + modlist_add (&modlist, "pgpCertID", NULL); + + /* Assemble the INFO stuff into LDAP attributes */ + + while (infolen > 0) + { + char *temp = NULL; + + char *newline = memchr (info, '\n', infolen); + if (! newline) + /* The last line is not \n terminated! Make a copy so we can + add a NUL terminator. */ + { + temp = xmalloc (infolen + 1); + memcpy (temp, info, infolen); + info = temp; + newline = (char *) info + infolen; + } + + *newline = '\0'; + + extract_attributes (&modlist, info); + + infolen = infolen - ((uintptr_t) newline - (uintptr_t) info + 1); + info = newline + 1; + + /* Sanity check. */ + if (! temp) + assert ((char *) info + infolen - 1 == infoend); + else + { + assert (infolen == -1); + xfree (temp); + } + } + + modlist_add (&addlist, "objectClass", "pgpKeyInfo"); + + err = armor_data (&data_armored, data, datalen); + if (err) + goto out; + + modlist_add (&addlist, pgpkeyattr, data_armored); + + /* Now append addlist onto modlist. */ + modlists_join (&modlist, addlist); + + if (dump) + { + estream_t input = modlist_dump (modlist, NULL); + if (input) + { + copy_stream (input, dump); + es_fclose (input); + } + } + + /* Going on the assumption that modify operations are more frequent + than adds, we try a modify first. If it's not there, we just + turn around and send an add command for the same key. Otherwise, + the modify brings the server copy into compliance with our copy. + Note that unlike the LDAP keyserver (and really, any other + keyserver) this does NOT merge signatures, but replaces the whole + key. This should make some people very happy. */ + { + char **certid; + char *dn; + + certid = modlist_lookup (modlist, "pgpCertID"); + if (/* We should have a value. */ + ! certid + /* Exactly one. */ + || !(certid[0] && !certid[1])) + { + log_error ("Bad certid.\n"); + err = GPG_ERR_GENERAL; + goto out; + } + + dn = xasprintf ("pgpCertID=%s,%s", certid[0], basedn); + + err = ldap_modify_s (ldap_conn, dn, modlist); + if (err == LDAP_NO_SUCH_OBJECT) + err = ldap_add_s (ldap_conn, dn, addlist); + + xfree (dn); + + if (err != LDAP_SUCCESS) + { + log_error ("gpgkeys: error adding key to keyserver: %s\n", + ldap_err2string (err)); + err = ldap_err_to_gpg_err (err); + } + } + + out: + if (dump) + es_fclose (dump); + + if (ldap_conn) + ldap_unbind (ldap_conn); + + xfree (basedn); + xfree (pgpkeyattr); + + modlist_free (modlist); + xfree (addlist); + + xfree (data_armored); + + return err; +} diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h new file mode 100644 index 0000000..b5b4dd0 --- /dev/null +++ b/dirmngr/ks-engine.h @@ -0,0 +1,67 @@ +/* ks-engine.h - Keyserver engines definitions + * Copyright (C) 2011 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#ifndef DIRMNGR_KS_ENGINE_H +#define DIRMNGR_KS_ENGINE_H 1 + +#include "http.h" + +/*-- ks-action.c --*/ +gpg_error_t ks_print_help (ctrl_t ctrl, const char *text); +gpg_error_t ks_printf_help (ctrl_t ctrl, const char *format, + ...) GPGRT_ATTR_PRINTF(2,3); + +/*-- ks-engine-hkp.c --*/ +gpg_error_t ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri); +gpg_error_t ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive); +gpg_error_t ks_hkp_print_hosttable (ctrl_t ctrl); +gpg_error_t ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri); +gpg_error_t ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, + estream_t *r_fp, unsigned int *r_http_status); +gpg_error_t ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, + const char *keyspec, estream_t *r_fp); +gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, + const void *data, size_t datalen); + +/*-- ks-engine-http.c --*/ +gpg_error_t ks_http_help (ctrl_t ctrl, parsed_uri_t uri); +gpg_error_t ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp); + + +/*-- ks-engine-finger.c --*/ +gpg_error_t ks_finger_help (ctrl_t ctrl, parsed_uri_t uri); +gpg_error_t ks_finger_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp); + +/*-- ks-engine-kdns.c --*/ +gpg_error_t ks_kdns_help (ctrl_t ctrl, parsed_uri_t uri); +gpg_error_t ks_kdns_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp); + +/*-- ks-engine-ldap.c --*/ +gpg_error_t ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri); +gpg_error_t ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, + estream_t *r_fp); +gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, + const char *keyspec, estream_t *r_fp); +gpg_error_t ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, + void *data, size_t datalen, + void *info, size_t infolen); + + +#endif /*DIRMNGR_KS_ENGINE_H*/ diff --git a/dirmngr/ldap-parse-uri.c b/dirmngr/ldap-parse-uri.c new file mode 100644 index 0000000..9671496 --- /dev/null +++ b/dirmngr/ldap-parse-uri.c @@ -0,0 +1,246 @@ +/* ldap-parse-uri.c - Parse an LDAP URI. + * Copyright (C) 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include + +#include + +#ifdef HAVE_W32_SYSTEM +# include "ldap-url.h" +#else +# include +#endif + +#include "util.h" +#include "http.h" + +/* Returns 1 if the string is an LDAP URL (begins with ldap:, ldaps: + or ldapi:). */ +int +ldap_uri_p (const char *url) +{ + char *colon = strchr (url, ':'); + if (! colon) + return 0; + else + { + int offset = (uintptr_t) colon - (uintptr_t) url; + + if (/* All lower case. */ + (offset == 4 && memcmp (url, "ldap", 4) == 0) + || (offset == 5 + && (memcmp (url, "ldaps", 5) == 0 + && memcmp (url, "ldapi", 5) == 0)) + /* Mixed case. */ + || ((url[0] == 'l' || url[0] == 'L') + && (url[1] == 'd' || url[1] == 'D') + && (url[2] == 'a' || url[2] == 'A') + && (url[3] == 'p' || url[3] == 'P') + && (url[4] == ':' + || ((url[4] == 's' || url[4] == 'S' + || url[4] == 'i' || url[4] == 'i') + && url[5] == ':')))) + return 1; + return 0; + } +} + +/* Parse a URI and put the result into *purip. On success the + caller must use http_release_parsed_uri() to releases the resources. + + uri->path is the base DN (or NULL for the default). + uri->auth is the bindname (or NULL for none). + The uri->query variable "password" is the password. + + Note: any specified scope, any attributes, any filter and any + unknown extensions are simply ignored. */ +gpg_error_t +ldap_parse_uri (parsed_uri_t *purip, const char *uri) +{ + gpg_err_code_t err = 0; + parsed_uri_t puri = NULL; + + int result; + LDAPURLDesc *lud = NULL; + + char *scheme = NULL; + char *host = NULL; + char *dn = NULL; + char *bindname = NULL; + char *password = NULL; + + char **s; + + char *buffer; + int len; + + result = ldap_url_parse (uri, &lud); + if (result != 0) + { + log_error ("Unable to parse LDAP uri '%s'\n", uri); + err = GPG_ERR_GENERAL; + goto out; + } + + scheme = lud->lud_scheme; + host = lud->lud_host; + dn = lud->lud_dn; + + for (s = lud->lud_exts; s && *s; s ++) + { + if (strncmp (*s, "bindname=", 9) == 0) + { + if (bindname) + log_error ("bindname given multiple times in URL '%s', ignoring.\n", + uri); + else + bindname = *s + 9; + } + else if (strncmp (*s, "password=", 9) == 0) + { + if (password) + log_error ("password given multiple times in URL '%s', ignoring.\n", + uri); + else + password = *s + 9; + } + else + log_error ("Unhandled extension (%s) in URL '%s', ignoring.", + *s, uri); + } + + len = 0; + +#define add(s) do { if (s) len += strlen (s) + 1; } while (0) + + add (scheme); + add (host); + add (dn); + add (bindname); + add (password); + + puri = xtrycalloc (1, sizeof *puri + len); + if (! puri) + { + err = gpg_err_code_from_syserror (); + goto out; + } + + buffer = puri->buffer; + +#define copy(to, s) \ + do \ + { \ + if (s) \ + { \ + to = buffer; \ + buffer = stpcpy (buffer, s) + 1; \ + } \ + } \ + while (0) + + copy (puri->scheme, scheme); + /* Make sure the scheme is lower case. */ + ascii_strlwr (puri->scheme); + + copy (puri->host, host); + copy (puri->path, dn); + copy (puri->auth, bindname); + + if (password) + { + puri->query = calloc (sizeof (*puri->query), 1); + if (!puri->query) + { + err = gpg_err_code_from_syserror (); + goto out; + } + puri->query->name = "password"; + copy (puri->query->value, password); + puri->query->valuelen = strlen (password) + 1; + } + + puri->use_tls = strcmp (puri->scheme, "ldaps") == 0; + puri->port = lud->lud_port; + + out: + if (lud) + ldap_free_urldesc (lud); + + if (err) + { + if (puri) + http_release_parsed_uri (puri); + } + else + *purip = puri; + + return gpg_err_make (default_errsource, err); +} + +/* The following characters need to be escaped to be part of an LDAP + filter: *, (, ), \, NUL and /. Note: we don't handle NUL, since a + NUL can't be part of a C string. + + This function always allocates a new string on success. It is the + caller's responsibility to free it. +*/ +char * +ldap_escape_filter (const char *filter) +{ + int l = strcspn (filter, "*()\\/"); + if (l == strlen (filter)) + /* Nothing to escape. */ + return xstrdup (filter); + + { + /* In the worst case we need to escape every letter. */ + char *escaped = xmalloc (1 + 3 * strlen (filter)); + + /* Indices into filter and escaped. */ + int filter_i = 0; + int escaped_i = 0; + + for (filter_i = 0; filter_i < strlen (filter); filter_i ++) + { + switch (filter[filter_i]) + { + case '*': + case '(': + case ')': + case '\\': + case '/': + snprintf (&escaped[escaped_i], 4, "%%%02x", + ((const unsigned char *)filter)[filter_i]); + escaped_i += 3; + break; + + default: + escaped[escaped_i ++] = filter[filter_i]; + break; + } + } + /* NUL terminate it. */ + escaped[escaped_i] = 0; + + /* We could shrink escaped to be just escaped_i bytes, but the + result will probably be freed very quickly anyways. */ + return escaped; + } +} diff --git a/dirmngr/ldap-parse-uri.h b/dirmngr/ldap-parse-uri.h new file mode 100644 index 0000000..bdbb6c3 --- /dev/null +++ b/dirmngr/ldap-parse-uri.h @@ -0,0 +1,33 @@ +/* ldap-parse-uri.h - Parse an LDAP URI. + * Copyright (C) 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#ifndef DIRMNGR_LDAP_PARSE_URI_H +#define DIRMNGR_LDAP_PARSE_URI_H + +#include "util.h" +#include "http.h" + +extern int ldap_uri_p (const char *url); + +extern gpg_error_t ldap_parse_uri (parsed_uri_t *ret_uri, const char *uri); + +extern char *ldap_escape_filter (const char *filter); + + +#endif diff --git a/dirmngr/ldap-url.c b/dirmngr/ldap-url.c new file mode 100644 index 0000000..8308514 --- /dev/null +++ b/dirmngr/ldap-url.c @@ -0,0 +1,935 @@ +/* The following code comes from the OpenLDAP project. The references + to the COPYRIGHT file below refer to the corresponding file in the + OpenLDAP distribution, which is reproduced here in full: + +Copyright 1998-2004 The OpenLDAP Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Individual files and/or contributed packages may be copyright by +other parties and subject to additional restrictions. + +This work is derived from the University of Michigan LDAP v3.3 +distribution. Information concerning this software is available +at . + +This work also contains materials derived from public sources. + +Additional information about OpenLDAP can be obtained at +. + +--- + +Portions Copyright 1998-2004 Kurt D. Zeilenga. +Portions Copyright 1998-2004 Net Boolean Incorporated. +Portions Copyright 2001-2004 IBM Corporation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +--- + +Portions Copyright 1999-2003 Howard Y.H. Chu. +Portions Copyright 1999-2003 Symas Corporation. +Portions Copyright 1998-2003 Hallvard B. Furuseth. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that this notice is preserved. +The names of the copyright holders may not be used to endorse or +promote products derived from this software without their specific +prior written permission. This software is provided `'as is'' +without express or implied warranty. + +--- + +Portions Copyright (c) 1992-1996 Regents of the University of Michigan. +All rights reserved. + +Redistribution and use in source and binary forms are permitted +provided that this notice is preserved and that due credit is given +to the University of Michigan at Ann Arbor. The name of the +University may not be used to endorse or promote products derived +from this software without specific prior written permission. This +software is provided `'as is'' without express or implied warranty. */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "ldap-url.h" +#define LDAP_P(protos) protos +#define LDAP_URL_URLCOLON "URL:" +#define LDAP_URL_URLCOLON_LEN (sizeof(LDAP_URL_URLCOLON)-1) +#define LDAP_URL_PREFIX "ldap://" +#define LDAP_URL_PREFIX_LEN (sizeof(LDAP_URL_PREFIX)-1) +#define LDAPS_URL_PREFIX "ldaps://" +#define LDAPS_URL_PREFIX_LEN (sizeof(LDAPS_URL_PREFIX)-1) +#define LDAPI_URL_PREFIX "ldapi://" +#define LDAPI_URL_PREFIX_LEN (sizeof(LDAPI_URL_PREFIX)-1) +#define LDAP_VFREE(v) { int _i; for (_i = 0; (v)[_i]; _i++) free((v)[_i]); } +#define LDAP_FREE free +#define LDAP_STRDUP strdup +#define LDAP_CALLOC calloc +#define LDAP_MALLOC malloc +#define LDAP_REALLOC realloc +#define ldap_utf8_strchr strchr +#define ldap_utf8_strtok(n,d) strtok (n,d) +#define Debug(a,b,c,d,e) +void ldap_pvt_hex_unescape( char *s ); + + +#ifndef LDAP_SCOPE_DEFAULT +# define LDAP_SCOPE_DEFAULT -1 +#endif + + + +/* $OpenLDAP: pkg/ldap/libraries/libldap/charray.c,v 1.9.2.2 2003/03/03 17:10:04 kurt Exp $ */ +/* + * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + */ +/* charray.c - routines for dealing with char * arrays */ + +int +ldap_charray_add( + char ***a, + char *s +) +{ + int n; + + if ( *a == NULL ) { + *a = (char **) LDAP_MALLOC( 2 * sizeof(char *) ); + n = 0; + + if( *a == NULL ) { + return -1; + } + + } else { + char **new; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + + new = (char **) LDAP_REALLOC( (char *) *a, + (n + 2) * sizeof(char *) ); + + if( new == NULL ) { + /* caller is required to call ldap_charray_free(*a) */ + return -1; + } + + *a = new; + } + + (*a)[n] = LDAP_STRDUP(s); + + if( (*a)[n] == NULL ) { + return 1; + } + + (*a)[++n] = NULL; + + return 0; +} + +int +ldap_charray_merge( + char ***a, + char **s +) +{ + int i, n, nn; + char **aa; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + for ( nn = 0; s[nn] != NULL; nn++ ) { + ; /* NULL */ + } + + aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) ); + + if( aa == NULL ) { + return -1; + } + + *a = aa; + + for ( i = 0; i < nn; i++ ) { + (*a)[n + i] = LDAP_STRDUP(s[i]); + + if( (*a)[n + i] == NULL ) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( (*a)[n + i] ); + (*a)[n + i] = NULL; + } + return -1; + } + } + + (*a)[n + nn] = NULL; + return 0; +} + +void +ldap_charray_free( char **a ) +{ + char **p; + + if ( a == NULL ) { + return; + } + + for ( p = a; *p != NULL; p++ ) { + if ( *p != NULL ) { + LDAP_FREE( *p ); + } + } + + LDAP_FREE( (char *) a ); +} + +int +ldap_charray_inlist( + char **a, + char *s +) +{ + int i; + + if( a == NULL ) return 0; + + for ( i=0; a[i] != NULL; i++ ) { + if ( strcasecmp( s, a[i] ) == 0 ) { + return 1; + } + } + + return 0; +} + +char ** +ldap_charray_dup( char **a ) +{ + int i; + char **new; + + for ( i = 0; a[i] != NULL; i++ ) + ; /* NULL */ + + new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) ); + + if( new == NULL ) { + return NULL; + } + + for ( i = 0; a[i] != NULL; i++ ) { + new[i] = LDAP_STRDUP( a[i] ); + + if( new[i] == NULL ) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( new[i] ); + } + LDAP_FREE( new ); + return NULL; + } + } + new[i] = NULL; + + return( new ); +} + +char ** +ldap_str2charray( const char *str_in, const char *brkstr ) +{ + char **res; + char *str, *s; + int i; + + /* protect the input string from strtok */ + str = LDAP_STRDUP( str_in ); + if( str == NULL ) { + return NULL; + } + + i = 1; + for ( s = str; *s; s++ ) { + if ( ldap_utf8_strchr( brkstr, *s ) != NULL ) { + i++; + } + } + + res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) ); + + if( res == NULL ) { + LDAP_FREE( str ); + return NULL; + } + + i = 0; + + for ( s = ldap_utf8_strtok( str, brkstr); + s != NULL; + s = ldap_utf8_strtok( NULL, brkstr) ) + { + res[i] = LDAP_STRDUP( s ); + + if(res[i] == NULL) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( res[i] ); + } + LDAP_FREE( res ); + LDAP_FREE( str ); + return NULL; + } + + i++; + } + + res[i] = NULL; + + LDAP_FREE( str ); + return( res ); +} + +char * ldap_charray2str( char **a, const char *sep ) +{ + char *s, **v, *p; + int len; + int slen; + + if( sep == NULL ) sep = " "; + + slen = strlen( sep ); + len = 0; + + for ( v = a; *v != NULL; v++ ) { + len += strlen( *v ) + slen; + } + + if ( len == 0 ) { + return NULL; + } + + /* trim extra sep len */ + len -= slen; + + s = LDAP_MALLOC ( len + 1 ); + + if ( s == NULL ) { + return NULL; + } + + p = s; + for ( v = a; *v != NULL; v++ ) { + if ( v != a ) { + strncpy( p, sep, slen ); + p += slen; + } + + len = strlen( *v ); + strncpy( p, *v, len ); + p += len; + } + + *p = '\0'; + return s; +} + + + +/* $OpenLDAP: pkg/ldap/libraries/libldap/url.c,v 1.64.2.5 2003/03/03 17:10:05 kurt Exp $ */ +/* + * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + */ +/* Portions + * Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + * + * LIBLDAP url.c -- LDAP URL (RFC 2255) related routines + * + * LDAP URLs look like this: + * ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]] + * + * where: + * attributes is a comma separated list + * scope is one of these three strings: base one sub (default=base) + * filter is an string-represented filter as in RFC 2254 + * + * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension + * + * We also tolerate URLs that look like: and + */ + +/* local functions */ +static const char* skip_url_prefix LDAP_P(( + const char *url, + int *enclosedp, + const char **scheme )); + +int +ldap_is_ldap_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return 1; +} + + +static const char* +skip_url_prefix( + const char *url, + int *enclosedp, + const char **scheme ) +{ + /* + * return non-zero if this looks like a LDAP URL; zero if not + * if non-zero returned, *urlp will be moved past "ldap://" part of URL + */ + const char *p; + + if ( url == NULL ) { + return( NULL ); + } + + p = url; + + /* skip leading '<' (if any) */ + if ( *p == '<' ) { + *enclosedp = 1; + ++p; + } else { + *enclosedp = 0; + } + + /* skip leading "URL:" (if any) */ + if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) { + p += LDAP_URL_URLCOLON_LEN; + } + + /* check for "ldap://" prefix */ + if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldap://" prefix and return success */ + p += LDAP_URL_PREFIX_LEN; + *scheme = "ldap"; + return( p ); + } + + /* check for "ldaps://" prefix */ + if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldaps://" prefix and return success */ + p += LDAPS_URL_PREFIX_LEN; + *scheme = "ldaps"; + return( p ); + } + + /* check for "ldapi://" prefix */ + if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldapi://" prefix and return success */ + p += LDAPI_URL_PREFIX_LEN; + *scheme = "ldapi"; + return( p ); + } + +#ifdef LDAP_CONNECTIONLESS + /* check for "cldap://" prefix */ + if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) { + /* skip over "cldap://" prefix and return success */ + p += LDAPC_URL_PREFIX_LEN; + *scheme = "cldap"; + return( p ); + } +#endif + + return( NULL ); +} + + +static int str2scope( const char *p ) +{ + if ( strcasecmp( p, "one" ) == 0 ) { + return LDAP_SCOPE_ONELEVEL; + + } else if ( strcasecmp( p, "onetree" ) == 0 ) { + return LDAP_SCOPE_ONELEVEL; + + } else if ( strcasecmp( p, "base" ) == 0 ) { + return LDAP_SCOPE_BASE; + + } else if ( strcasecmp( p, "sub" ) == 0 ) { + return LDAP_SCOPE_SUBTREE; + + } else if ( strcasecmp( p, "subtree" ) == 0 ) { + return LDAP_SCOPE_SUBTREE; + } + + return( -1 ); +} + + +int +ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) +{ +/* + * Pick apart the pieces of an LDAP URL. + */ + + LDAPURLDesc *ludp; + char *p, *q, *r; + int i, enclosed; + const char *scheme = NULL; + const char *url_tmp; + char *url; + + if( url_in == NULL || ludpp == NULL ) { + return LDAP_URL_ERR_PARAM; + } + +#ifndef LDAP_INT_IN_KERNEL + /* Global options may not be created yet + * We can't test if the global options are initialized + * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate + * the options and cause infinite recursion + */ +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 ); +#endif +#endif + + *ludpp = NULL; /* pessimistic */ + + url_tmp = skip_url_prefix( url_in, &enclosed, &scheme ); + + if ( url_tmp == NULL ) { + return LDAP_URL_ERR_BADSCHEME; + } + + assert( scheme ); + + /* make working copy of the remainder of the URL */ + url = LDAP_STRDUP( url_tmp ); + if ( url == NULL ) { + return LDAP_URL_ERR_MEM; + } + + if ( enclosed ) { + p = &url[strlen(url)-1]; + + if( *p != '>' ) { + LDAP_FREE( url ); + return LDAP_URL_ERR_BADENCLOSURE; + } + + *p = '\0'; + } + + /* allocate return struct */ + ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc )); + + if ( ludp == NULL ) { + LDAP_FREE( url ); + return LDAP_URL_ERR_MEM; + } + + ludp->lud_next = NULL; + ludp->lud_host = NULL; + ludp->lud_port = 0; + ludp->lud_dn = NULL; + ludp->lud_attrs = NULL; + ludp->lud_filter = NULL; + ludp->lud_scope = LDAP_SCOPE_DEFAULT; + ludp->lud_filter = NULL; + ludp->lud_exts = NULL; + + ludp->lud_scheme = LDAP_STRDUP( scheme ); + + if ( ludp->lud_scheme == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + /* scan forward for '/' that marks end of hostport and begin. of dn */ + p = strchr( url, '/' ); + + if( p != NULL ) { + /* terminate hostport; point to start of dn */ + *p++ = '\0'; + } + + /* IPv6 syntax with [ip address]:port */ + if ( *url == '[' ) { + r = strchr( url, ']' ); + if ( r == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + *r++ = '\0'; + q = strchr( r, ':' ); + } else { + q = strchr( url, ':' ); + } + + if ( q != NULL ) { + *q++ = '\0'; + ldap_pvt_hex_unescape( q ); + + if( *q == '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + ludp->lud_port = atoi( q ); + } + + ldap_pvt_hex_unescape( url ); + + /* If [ip address]:port syntax, url is [ip and we skip the [ */ + ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) ); + + if( ludp->lud_host == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + /* + * Kludge. ldap://111.222.333.444:389??cn=abc,o=company + * + * On early Novell releases, search references/referrals were returned + * in this format, i.e., the dn was kind of in the scope position, + * but the required slash is missing. The whole thing is illegal syntax, + * but we need to account for it. Fortunately it can't be confused with + * anything real. + */ + if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) { + q++; + /* ? immediately followed by question */ + if( *q == '?') { + q++; + if( *q != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( q ); + ludp->lud_dn = LDAP_STRDUP( q ); + } else { + ludp->lud_dn = LDAP_STRDUP( "" ); + } + + if( ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + } + + if( p == NULL ) { + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of dn */ + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate dn part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( p ); + ludp->lud_dn = LDAP_STRDUP( p ); + } else { + ludp->lud_dn = LDAP_STRDUP( "" ); + } + + if( ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + if( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of attributes */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate attributes part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse attributes */ + ldap_pvt_hex_unescape( p ); + ludp->lud_attrs = ldap_str2charray( p, "," ); + + if( ludp->lud_attrs == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADATTRS; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of scope */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate the scope part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse the scope */ + ldap_pvt_hex_unescape( p ); + ludp->lud_scope = str2scope( p ); + + if( ludp->lud_scope == -1 ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADSCOPE; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of filter */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate the filter part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse the filter */ + ldap_pvt_hex_unescape( p ); + + if( ! *p ) { + /* missing filter */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADFILTER; + } + + LDAP_FREE( ludp->lud_filter ); + ludp->lud_filter = LDAP_STRDUP( p ); + + if( ludp->lud_filter == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of extensions */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* extra '?' */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + /* parse the extensions */ + ludp->lud_exts = ldap_str2charray( p, "," ); + + if( ludp->lud_exts == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADEXTS; + } + + for( i=0; ludp->lud_exts[i] != NULL; i++ ) { + ldap_pvt_hex_unescape( ludp->lud_exts[i] ); + + if( *ludp->lud_exts[i] == '!' ) { + /* count the number of critical extensions */ + ludp->lud_crit_exts++; + } + } + + if( i == 0 ) { + /* must have 1 or more */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADEXTS; + } + + /* no more */ + *ludpp = ludp; + LDAP_FREE( url ); + return LDAP_URL_SUCCESS; +} + +int +ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) +{ + int rc = ldap_url_parse_ext( url_in, ludpp ); + + if( rc != LDAP_URL_SUCCESS ) { + return rc; + } + + if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) { + (*ludpp)->lud_scope = LDAP_SCOPE_BASE; + } + + if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') { + LDAP_FREE( (*ludpp)->lud_host ); + (*ludpp)->lud_host = NULL; + } + + if ((*ludpp)->lud_port == 0) { + if( strcmp((*ludpp)->lud_scheme, "ldap") == 0 ) { + (*ludpp)->lud_port = LDAP_PORT; +#ifdef LDAP_CONNECTIONLESS + } else if( strcmp((*ludpp)->lud_scheme, "cldap") == 0 ) { + (*ludpp)->lud_port = LDAP_PORT; +#endif + } else if( strcmp((*ludpp)->lud_scheme, "ldaps") == 0 ) { + (*ludpp)->lud_port = LDAPS_PORT; + } + } + + return rc; +} + + +void +ldap_free_urldesc( LDAPURLDesc *ludp ) +{ + if ( ludp == NULL ) { + return; + } + + if ( ludp->lud_scheme != NULL ) { + LDAP_FREE( ludp->lud_scheme ); + } + + if ( ludp->lud_host != NULL ) { + LDAP_FREE( ludp->lud_host ); + } + + if ( ludp->lud_dn != NULL ) { + LDAP_FREE( ludp->lud_dn ); + } + + if ( ludp->lud_filter != NULL ) { + LDAP_FREE( ludp->lud_filter); + } + + if ( ludp->lud_attrs != NULL ) { + LDAP_VFREE( ludp->lud_attrs ); + } + + if ( ludp->lud_exts != NULL ) { + LDAP_VFREE( ludp->lud_exts ); + } + + LDAP_FREE( ludp ); +} + + +static int +ldap_int_unhex( int c ) +{ + return( c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F' ? c - 'A' + 10 + : c - 'a' + 10 ); +} + +void +ldap_pvt_hex_unescape( char *s ) +{ + /* + * Remove URL hex escapes from s... done in place. The basic concept for + * this routine is borrowed from the WWW library HTUnEscape() routine. + */ + char *p; + + for ( p = s; *s != '\0'; ++s ) { + if ( *s == '%' ) { + if ( *++s == '\0' ) { + break; + } + *p = ldap_int_unhex( *s ) << 4; + if ( *++s == '\0' ) { + break; + } + *p++ += ldap_int_unhex( *s ); + } else { + *p++ = *s; + } + } + + *p = '\0'; +} diff --git a/dirmngr/ldap-url.h b/dirmngr/ldap-url.h new file mode 100644 index 0000000..f3104d8 --- /dev/null +++ b/dirmngr/ldap-url.h @@ -0,0 +1,50 @@ +/* Copyright 2007 g10 Code GmbH + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. */ + +#ifndef LDAP_URL_H +#define LDAP_URL_H 1 + +#define LDAP_CONST const + +typedef struct ldap_url_desc +{ + struct ldap_url_desc *lud_next; + char *lud_scheme; + char *lud_host; + int lud_port; + char *lud_dn; + char **lud_attrs; + int lud_scope; + char *lud_filter; + char **lud_exts; + int lud_crit_exts; +} LDAPURLDesc; + +#define LDAP_URL_SUCCESS 0x00 +#define LDAP_URL_ERR_MEM 0x01 +#define LDAP_URL_ERR_PARAM 0x02 + +#define LDAP_URL_ERR_BADSCHEME 0x03 +#define LDAP_URL_ERR_BADENCLOSURE 0x04 +#define LDAP_URL_ERR_BADURL 0x05 +#define LDAP_URL_ERR_BADHOST 0x06 +#define LDAP_URL_ERR_BADATTRS 0x07 +#define LDAP_URL_ERR_BADSCOPE 0x08 +#define LDAP_URL_ERR_BADFILTER 0x09 +#define LDAP_URL_ERR_BADEXTS 0x0a + +#define LDAPS_PORT 636 + +int ldap_is_ldap_url (LDAP_CONST char *url); +int ldap_url_parse (LDAP_CONST char *url_in, LDAPURLDesc **ludpp); +void ldap_free_urldesc (LDAPURLDesc *ludp); + +#endif /* !LDAP_URL_H */ diff --git a/dirmngr/ldap-wrapper-ce.c b/dirmngr/ldap-wrapper-ce.c new file mode 100644 index 0000000..478e694 --- /dev/null +++ b/dirmngr/ldap-wrapper-ce.c @@ -0,0 +1,571 @@ +/* ldap-wrapper-ce.c - LDAP access via W32 threads + * Copyright (C) 2010 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 3 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, see . + */ + +/* + Alternative wrapper for use with WindowsCE. Under WindowsCE the + number of processes is strongly limited (32 processes including the + kernel processes) and thus we don't use the process approach but + implement a wrapper based on native threads. + + See ldap-wrapper.c for the standard wrapper interface. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "ldap-wrapper.h" + +#ifdef USE_LDAPWRAPPER +# error This module is not expected to be build. +#endif + + + +/* Read a fixed amount of data from READER into BUFFER. */ +static gpg_error_t +read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) +{ + gpg_error_t err; + size_t nread; + + while (count) + { + err = ksba_reader_read (reader, buffer, count, &nread); + if (err) + return err; + buffer += nread; + count -= nread; + } + return 0; +} + + + + +/* Start the reaper thread for this wrapper. */ +void +ldap_wrapper_launch_thread (void) +{ + /* Not required. */ +} + + + + + +/* Wait until all ldap wrappers have terminated. We assume that the + kill has already been sent to all of them. */ +void +ldap_wrapper_wait_connections () +{ + /* Not required. */ +} + + +/* Cleanup all resources held by the connection associated with + CTRL. This is used after a cancel to kill running wrappers. */ +void +ldap_wrapper_connection_cleanup (ctrl_t ctrl) +{ + (void)ctrl; + + /* Not required. */ +} + + + +/* The cookie we use to implement the outstream of the wrapper thread. */ +struct outstream_cookie_s +{ + int refcount; /* Reference counter - possible values are 1 and 2. */ + + /* We don't need a mutex for the conditions, as npth provides a + simpler condition interface that relies on the global lock. This + can be used if we never yield between testing the condition and + waiting on it. */ + npth_cond_t wait_data; /* Condition that data is available. */ + npth_cond_t wait_space; /* Condition that space is available. */ + + int eof_seen; /* EOF indicator. */ + char buffer[4000]; /* Data ring buffer. */ + size_t buffer_len; /* The amount of data in the BUFFER. */ + size_t buffer_pos; /* The next read position of the BUFFER. */ +}; + +#define BUFFER_EMPTY(c) ((c)->buffer_len == 0) +#define BUFFER_FULL(c) ((c)->buffer_len == DIM((c)->buffer)) +#define BUFFER_DATA_AVAILABLE(c) ((c)->buffer_len) +#define BUFFER_SPACE_AVAILABLE(c) (DIM((c)->buffer) - (c)->buffer_len) +#define BUFFER_INC_POS(c,n) (c)->buffer_pos = ((c)->buffer_pos + (n)) % DIM((c)->buffer) +#define BUFFER_CUR_POS(c) (&(c)->buffer[(c)->buffer_pos]) + +static int +buffer_get_data (struct outstream_cookie_s *cookie, char *dst, int cnt) +{ + int amount; + int left; + int chunk; + + amount = cnt; + if (BUFFER_DATA_AVAILABLE (cookie) < amount) + amount = BUFFER_DATA_AVAILABLE (cookie); + left = amount; + + /* How large is the part up to the end of the buffer array? */ + chunk = DIM(cookie->buffer) - cookie->buffer_pos; + if (chunk > left) + chunk = left; + + memcpy (dst, BUFFER_CUR_POS (cookie), chunk); + BUFFER_INC_POS (cookie, chunk); + left -= chunk; + dst += chunk; + + if (left) + { + memcpy (dst, BUFFER_CUR_POS (cookie), left); + BUFFER_INC_POS (cookie, left); + } + + return amount; +} + + +static int +buffer_put_data (struct outstream_cookie_s *cookie, const char *src, int cnt) +{ + int amount; + int remain; + int left; + int chunk; + + remain = DIM(cookie->buffer) - cookie->buffer_len; + + amount = cnt; + if (remain < amount) + amount = remain; + left = amount; + + /* How large is the part up to the end of the buffer array? */ + chunk = DIM(cookie->buffer) - cookie->buffer_pos; + if (chunk > left) + chunk = left; + + memcpy (BUFFER_CUR_POS (cookie), src, chunk); + BUFFER_INC_POS (cookie, chunk); + left -= chunk; + src += chunk; + + if (left) + { + memcpy (BUFFER_CUR_POS (cookie), src, left); + BUFFER_INC_POS (cookie, left); + } + + cookie->buffer_len -= amount; + return amount; +} + + +/* The writer function for the outstream. This is used to transfer + the output of the ldap wrapper thread to the ksba reader object. */ +static ssize_t +outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size) +{ + struct outstream_cookie_s *cookie = cookie_arg; + const char *src; + ssize_t nwritten = 0; + int res; + ssize_t amount = 0; + + src = buffer; + do + { + int was_empty = 0; + + /* Wait for free space. */ + while (BUFFER_FULL(cookie)) + { + /* Buffer is full: Wait for space. */ + res = npth_cond_wait (&cookie->wait_space, NULL); + if (res) + { + gpg_err_set_errno (res); + return -1; + } + } + + if (BUFFER_EMPTY(cookie)) + was_empty = 1; + + /* Copy data. */ + nwritten = buffer_put_data (cookie, buffer, size); + size -= nwritten; + src += nwritten; + amount += nwritten; + + if (was_empty) + npth_cond_signal (&cookie->wait_data); + } + while (size); /* Until done. */ + + return amount; +} + + +static void +outstream_release_cookie (struct outstream_cookie_s *cookie) +{ + cookie->refcount--; + if (!cookie->refcount) + { + npth_cond_destroy (&cookie->wait_data); + npth_cond_destroy (&cookie->wait_space); + xfree (cookie); + } +} + + +/* Closer function for the outstream. This deallocates the cookie if + it won't be used anymore. */ +static int +outstream_cookie_closer (void *cookie_arg) +{ + struct outstream_cookie_s *cookie = cookie_arg; + + if (!cookie) + return 0; /* Nothing to do. */ + + cookie->eof_seen = 1; /* (only useful if refcount > 1) */ + + assert (cookie->refcount > 0); + outstream_release_cookie (cookie); + return 0; +} + + +/* The KSBA reader callback which takes the output of the ldap thread + form the outstream_cookie_writer and make it available to the ksba + reader. */ +static int +outstream_reader_cb (void *cb_value, char *buffer, size_t count, + size_t *r_nread) +{ + struct outstream_cookie_s *cookie = cb_value; + size_t nread = 0; + int was_full = 0; + + if (!buffer && !count && !r_nread) + return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported. */ + + *r_nread = 0; + + while (BUFFER_EMPTY(cookie)) + { + if (cookie->eof_seen) + return gpg_error (GPG_ERR_EOF); + + /* Wait for data to become available. */ + npth_cond_wait (&cookie->wait_data, NULL); + } + + if (BUFFER_FULL(cookie)) + was_full = 1; + + nread = buffer_get_data (cookie, buffer, count); + + if (was_full) + { + npth_cond_signal (&cookie->wait_space); + } + + *r_nread = nread; + return 0; /* Success. */ +} + + +/* This function is called by ksba_reader_release. */ +static void +outstream_reader_released (void *cb_value, ksba_reader_t r) +{ + struct outstream_cookie_s *cookie = cb_value; + + (void)r; + + assert (cookie->refcount > 0); + outstream_release_cookie (cookie); +} + + + +/* This function is to be used to release a context associated with the + given reader object. This does not release the reader object, though. */ +void +ldap_wrapper_release_context (ksba_reader_t reader) +{ + (void)reader; + /* Nothing to do. */ +} + + + +/* Free a NULL terminated array of malloced strings and the array + itself. */ +static void +free_arg_list (char **arg_list) +{ + int i; + + if (arg_list) + { + for (i=0; arg_list[i]; i++) + xfree (arg_list[i]); + xfree (arg_list); + } +} + + +/* Copy ARGV into a new array and prepend one element as name of the + program (which is more or less a stub). We need to allocate all + the strings to get ownership of them. */ +static gpg_error_t +create_arg_list (const char *argv[], char ***r_arg_list) +{ + gpg_error_t err; + char **arg_list; + int i, j; + + for (i = 0; argv[i]; i++) + ; + arg_list = xtrycalloc (i + 2, sizeof *arg_list); + if (!arg_list) + goto outofcore; + + i = 0; + arg_list[i] = xtrystrdup (""); + if (!arg_list[i]) + goto outofcore; + i++; + for (j=0; argv[j]; j++) + { + arg_list[i] = xtrystrdup (argv[j]); + if (!arg_list[i]) + goto outofcore; + i++; + } + arg_list[i] = NULL; + *r_arg_list = arg_list; + return 0; + + outofcore: + err = gpg_error_from_syserror (); + log_error (_("error allocating memory: %s\n"), strerror (errno)); + free_arg_list (arg_list); + *r_arg_list = NULL; + return err; + +} + + +/* Parameters passed to the wrapper thread. */ +struct ldap_wrapper_thread_parms +{ + char **arg_list; + estream_t outstream; +}; + +/* The thread which runs the LDAP wrapper. */ +static void * +ldap_wrapper_thread (void *opaque) +{ + struct ldap_wrapper_thread_parms *parms = opaque; + + /*err =*/ ldap_wrapper_main (parms->arg_list, parms->outstream); + + /* FIXME: Do we need to return ERR? */ + + free_arg_list (parms->arg_list); + es_fclose (parms->outstream); + xfree (parms); + return NULL; +} + + + +/* Start a new LDAP thread and returns a new libksba reader + object at READER. ARGV is a NULL terminated list of arguments for + the wrapper. The function returns 0 on success or an error code. */ +gpg_error_t +ldap_wrapper (ctrl_t ctrl, ksba_reader_t *r_reader, const char *argv[]) +{ + gpg_error_t err; + struct ldap_wrapper_thread_parms *parms; + npth_attr_t tattr; + es_cookie_io_functions_t outstream_func = { NULL }; + struct outstream_cookie_s *outstream_cookie; + ksba_reader_t reader; + int res; + npth_t thread; + + (void)ctrl; + + *r_reader = NULL; + + parms = xtrycalloc (1, sizeof *parms); + if (!parms) + return gpg_error_from_syserror (); + + err = create_arg_list (argv, &parms->arg_list); + if (err) + { + xfree (parms); + return err; + } + + outstream_cookie = xtrycalloc (1, sizeof *outstream_cookie); + if (!outstream_cookie) + { + err = gpg_error_from_syserror (); + free_arg_list (parms->arg_list); + xfree (parms); + return err; + } + outstream_cookie->refcount++; + + res = npth_cond_init (&outstream_cookie->wait_data, NULL); + if (res) + { + free_arg_list (parms->arg_list); + xfree (parms); + return gpg_error_from_errno (res); + } + res = npth_cond_init (&outstream_cookie->wait_space, NULL); + if (res) + { + npth_cond_destroy (&outstream_cookie->wait_data); + free_arg_list (parms->arg_list); + xfree (parms); + return gpg_error_from_errno (res); + } + + err = ksba_reader_new (&reader); + if (!err) + err = ksba_reader_set_release_notify (reader, + outstream_reader_released, + outstream_cookie); + if (!err) + err = ksba_reader_set_cb (reader, + outstream_reader_cb, outstream_cookie); + if (err) + { + log_error (_("error initializing reader object: %s\n"), + gpg_strerror (err)); + ksba_reader_release (reader); + outstream_release_cookie (outstream_cookie); + free_arg_list (parms->arg_list); + xfree (parms); + return err; + } + + + outstream_func.func_write = outstream_cookie_writer; + outstream_func.func_close = outstream_cookie_closer; + parms->outstream = es_fopencookie (outstream_cookie, "wb", outstream_func); + if (!parms->outstream) + { + err = gpg_error_from_syserror (); + ksba_reader_release (reader); + outstream_release_cookie (outstream_cookie); + free_arg_list (parms->arg_list); + xfree (parms); + return err; + } + outstream_cookie->refcount++; + + res = npth_attr_init(&tattr); + if (res) + { + err = gpg_error_from_errno (res); + ksba_reader_release (reader); + free_arg_list (parms->arg_list); + es_fclose (parms->outstream); + xfree (parms); + return err; + } + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + + res = npth_create (&thread, &tattr, ldap_wrapper_thread, parms); + npth_attr_destroy (&tattr); + if (res) + { + err = gpg_error_from_errno (res); + log_error ("error spawning ldap wrapper thread: %s\n", + strerror (res) ); + } + else + parms = NULL; /* Now owned by the thread. */ + + if (parms) + { + free_arg_list (parms->arg_list); + es_fclose (parms->outstream); + xfree (parms); + } + if (err) + { + ksba_reader_release (reader); + return err; + } + + /* Need to wait for the first byte so we are able to detect an empty + output and not let the consumer see an EOF without further error + indications. The CRL loading logic assumes that after return + from this function, a failed search (e.g. host not found ) is + indicated right away. */ + { + unsigned char c; + + err = read_buffer (reader, &c, 1); + if (err) + { + ksba_reader_release (reader); + reader = NULL; + if (gpg_err_code (err) == GPG_ERR_EOF) + return gpg_error (GPG_ERR_NO_DATA); + else + return err; + } + ksba_reader_unread (reader, &c, 1); + } + + *r_reader = reader; + + return 0; +} diff --git a/dirmngr/ldap-wrapper.c b/dirmngr/ldap-wrapper.c new file mode 100644 index 0000000..b313848 --- /dev/null +++ b/dirmngr/ldap-wrapper.c @@ -0,0 +1,782 @@ +/* ldap-wrapper.c - LDAP access via a wrapper process + * Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH + * Copyright (C) 2010 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 3 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, see . + */ + +/* + We can't use LDAP directly for these reasons: + + 1. On some systems the LDAP library uses (indirectly) pthreads and + that is not compatible with PTh. + + 2. It is huge library in particular if TLS comes into play. So + problems with unfreed memory might turn up and we don't want + this in a long running daemon. + + 3. There is no easy way for timeouts. In particular the timeout + value does not work for DNS lookups (well, this is usual) and it + seems not to work while loading a large attribute like a + CRL. Having a separate process allows us to either tell the + process to commit suicide or have our own housekepping function + kill it after some time. The latter also allows proper + cancellation of a query at any point of time. + + 4. Given that we are going out to the network and usually get back + a long response, the fork/exec overhead is acceptable. + + Note that under WindowsCE the number of processes is strongly + limited (32 processes including the kernel processes) and thus we + don't use the process approach but implement a different wrapper in + ldap-wrapper-ce.c. +*/ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "exechelp.h" +#include "misc.h" +#include "ldap-wrapper.h" + + +#ifdef HAVE_W32_SYSTEM +#define setenv(a,b,c) SetEnvironmentVariable ((a),(b)) +#else +#define pth_close(fd) close(fd) +#endif + +#ifndef USE_LDAPWRAPPER +# error This module is not expected to be build. +#endif + +/* In case sysconf does not return a value we need to have a limit. */ +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + +#define INACTIVITY_TIMEOUT (opt.ldaptimeout + 60*5) /* seconds */ + +#define TIMERTICK_INTERVAL 2 + +/* To keep track of the LDAP wrapper state we use this structure. */ +struct wrapper_context_s +{ + struct wrapper_context_s *next; + + pid_t pid; /* The pid of the wrapper process. */ + int printable_pid; /* Helper to print diagnostics after the process has + been cleaned up. */ + int fd; /* Connected with stdout of the ldap wrapper. */ + gpg_error_t fd_error; /* Set to the gpg_error of the last read error + if any. */ + int log_fd; /* Connected with stderr of the ldap wrapper. */ + ctrl_t ctrl; /* Connection data. */ + int ready; /* Internally used to mark to be removed contexts. */ + ksba_reader_t reader; /* The ksba reader object or NULL. */ + char *line; /* Used to print the log lines (malloced). */ + size_t linesize;/* Allocated size of LINE. */ + size_t linelen; /* Use size of LINE. */ + time_t stamp; /* The last time we noticed ativity. */ +}; + + + +/* We keep a global list of spawed wrapper process. A separate thread + makes use of this list to log error messages and to watch out for + finished processes. */ +static struct wrapper_context_s *wrapper_list; + +/* We need to know whether we are shutting down the process. */ +static int shutting_down; + +/* Close the pth file descriptor FD and set it to -1. */ +#define SAFE_CLOSE(fd) \ + do { int _fd = fd; if (_fd != -1) { close (_fd); fd = -1;} } while (0) + + + + +/* Read a fixed amount of data from READER into BUFFER. */ +static gpg_error_t +read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) +{ + gpg_error_t err; + size_t nread; + + while (count) + { + err = ksba_reader_read (reader, buffer, count, &nread); + if (err) + return err; + buffer += nread; + count -= nread; + } + return 0; +} + + +/* Release the wrapper context and kill a running wrapper process. */ +static void +destroy_wrapper (struct wrapper_context_s *ctx) +{ + if (ctx->pid != (pid_t)(-1)) + { + gnupg_kill_process (ctx->pid); + gnupg_release_process (ctx->pid); + } + ksba_reader_release (ctx->reader); + SAFE_CLOSE (ctx->fd); + SAFE_CLOSE (ctx->log_fd); + xfree (ctx->line); + xfree (ctx); +} + + +/* Print the content of LINE to thye log stream but make sure to only + print complete lines. Using NULL for LINE will flush any pending + output. LINE may be modified by this function. */ +static void +print_log_line (struct wrapper_context_s *ctx, char *line) +{ + char *s; + size_t n; + + if (!line) + { + if (ctx->line && ctx->linelen) + { + + log_info ("%s\n", ctx->line); + ctx->linelen = 0; + } + return; + } + + while ((s = strchr (line, '\n'))) + { + *s = 0; + if (ctx->line && ctx->linelen) + { + log_info ("%s", ctx->line); + ctx->linelen = 0; + log_printf ("%s\n", line); + } + else + log_info ("%s\n", line); + line = s + 1; + } + n = strlen (line); + if (n) + { + if (ctx->linelen + n + 1 >= ctx->linesize) + { + char *tmp; + size_t newsize; + + newsize = ctx->linesize + ((n + 255) & ~255) + 1; + tmp = (ctx->line ? xtryrealloc (ctx->line, newsize) + : xtrymalloc (newsize)); + if (!tmp) + { + log_error (_("error printing log line: %s\n"), strerror (errno)); + return; + } + ctx->line = tmp; + ctx->linesize = newsize; + } + memcpy (ctx->line + ctx->linelen, line, n); + ctx->linelen += n; + ctx->line[ctx->linelen] = 0; + } +} + + +/* Read data from the log stream. Returns true if the log stream + indicated EOF or error. */ +static int +read_log_data (struct wrapper_context_s *ctx) +{ + int n; + char line[256]; + + /* We must use the npth_read function for pipes, always. */ + do + n = npth_read (ctx->log_fd, line, sizeof line - 1); + while (n < 0 && errno == EINTR); + + if (n <= 0) /* EOF or error. */ + { + if (n < 0) + log_error (_("error reading log from ldap wrapper %d: %s\n"), + (int)ctx->pid, strerror (errno)); + print_log_line (ctx, NULL); + SAFE_CLOSE (ctx->log_fd); + return 1; + } + + line[n] = 0; + print_log_line (ctx, line); + if (ctx->stamp != (time_t)(-1)) + ctx->stamp = time (NULL); + return 0; +} + + +/* This function is run by a separate thread to maintain the list of + wrappers and to log error messages from these wrappers. */ +void * +ldap_wrapper_thread (void *dummy) +{ + int nfds; + struct wrapper_context_s *ctx; + struct wrapper_context_s *ctx_prev; + struct timespec abstime; + struct timespec curtime; + struct timespec timeout; + fd_set fdset; + int ret; + time_t exptime; + + (void)dummy; + + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + + for (;;) + { + int any_action = 0; + + npth_clock_gettime (&curtime); + if (!(npth_timercmp (&curtime, &abstime, <))) + { + /* Inactivity is checked below. Nothing else to do. */ + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + } + npth_timersub (&abstime, &curtime, &timeout); + + FD_ZERO (&fdset); + nfds = -1; + for (ctx = wrapper_list; ctx; ctx = ctx->next) + { + if (ctx->log_fd != -1) + { + FD_SET (ctx->log_fd, &fdset); + if (ctx->log_fd > nfds) + nfds = ctx->log_fd; + } + } + nfds++; + + /* FIXME: For Windows, we have to use a reader thread on the + pipe that signals an event (and a npth_select_ev variant). */ + ret = npth_pselect (nfds + 1, &fdset, NULL, NULL, &timeout, NULL); + if (ret == -1) + { + if (errno != EINTR) + { + log_error (_("npth_select failed: %s - waiting 1s\n"), + strerror (errno)); + npth_sleep (1); + } + continue; + } + + /* All timestamps before exptime should be considered expired. */ + exptime = time (NULL); + if (exptime > INACTIVITY_TIMEOUT) + exptime -= INACTIVITY_TIMEOUT; + + /* Note that there is no need to lock the list because we always + add entries at the head (with a pending event status) and + thus traversing the list will even work if we have a context + switch in waitpid (which should anyway only happen with Pth's + hard system call mapping). */ + for (ctx = wrapper_list; ctx; ctx = ctx->next) + { + /* Check whether there is any logging to be done. */ + if (nfds && ctx->log_fd != -1 && FD_ISSET (ctx->log_fd, &fdset)) + { + if (read_log_data (ctx)) + { + SAFE_CLOSE (ctx->log_fd); + any_action = 1; + } + } + + /* Check whether the process is still running. */ + if (ctx->pid != (pid_t)(-1)) + { + gpg_error_t err; + int status; + + err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0, + &status); + if (!err) + { + log_info (_("ldap wrapper %d ready"), (int)ctx->pid); + ctx->ready = 1; + gnupg_release_process (ctx->pid); + ctx->pid = (pid_t)(-1); + any_action = 1; + } + else if (gpg_err_code (err) == GPG_ERR_GENERAL) + { + if (status == 10) + log_info (_("ldap wrapper %d ready: timeout\n"), + (int)ctx->pid); + else + log_info (_("ldap wrapper %d ready: exitcode=%d\n"), + (int)ctx->pid, status); + ctx->ready = 1; + gnupg_release_process (ctx->pid); + ctx->pid = (pid_t)(-1); + any_action = 1; + } + else if (gpg_err_code (err) != GPG_ERR_TIMEOUT) + { + log_error (_("waiting for ldap wrapper %d failed: %s\n"), + (int)ctx->pid, gpg_strerror (err)); + any_action = 1; + } + } + + /* Check whether we should terminate the process. */ + if (ctx->pid != (pid_t)(-1) + && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) + { + gnupg_kill_process (ctx->pid); + ctx->stamp = (time_t)(-1); + log_info (_("ldap wrapper %d stalled - killing\n"), + (int)ctx->pid); + /* We need to close the log fd because the cleanup loop + waits for it. */ + SAFE_CLOSE (ctx->log_fd); + any_action = 1; + } + } + + /* If something has been printed to the log file or we got an + EOF from a wrapper, we now print the list of active + wrappers. */ + if (any_action && DBG_LOOKUP) + { + log_info ("ldap worker stati:\n"); + for (ctx = wrapper_list; ctx; ctx = ctx->next) + log_info (" c=%p pid=%d/%d rdr=%p ctrl=%p/%d la=%lu rdy=%d\n", + ctx, + (int)ctx->pid, (int)ctx->printable_pid, + ctx->reader, + ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, + (unsigned long)ctx->stamp, ctx->ready); + } + + + /* Use a separate loop to check whether ready marked wrappers + may be removed. We may only do so if the ksba reader object + is not anymore in use or we are in shutdown state. */ + again: + for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next) + if (ctx->ready + && ((ctx->log_fd == -1 && !ctx->reader) || shutting_down)) + { + if (ctx_prev) + ctx_prev->next = ctx->next; + else + wrapper_list = ctx->next; + destroy_wrapper (ctx); + /* We need to restart because destroy_wrapper might have + done a context switch. */ + goto again; + } + } + /*NOTREACHED*/ + return NULL; /* Make the compiler happy. */ +} + + + +/* Start the reaper thread for the ldap wrapper. */ +void +ldap_wrapper_launch_thread (void) +{ + static int done; + npth_attr_t tattr; + npth_t thread; + int err; + + if (done) + return; + done = 1; + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + + err = npth_create (&thread, &tattr, ldap_wrapper_thread, NULL); + if (err) + { + log_error (_("error spawning ldap wrapper reaper thread: %s\n"), + strerror (err) ); + dirmngr_exit (1); + } + npth_setname_np (thread, "ldap-reaper"); + npth_attr_destroy (&tattr); +} + + + + + +/* Wait until all ldap wrappers have terminated. We assume that the + kill has already been sent to all of them. */ +void +ldap_wrapper_wait_connections () +{ + shutting_down = 1; + /* FIXME: This is a busy wait. */ + while (wrapper_list) + npth_usleep (200); +} + + +/* This function is to be used to release a context associated with the + given reader object. */ +void +ldap_wrapper_release_context (ksba_reader_t reader) +{ + struct wrapper_context_s *ctx; + + if (!reader ) + return; + + for (ctx=wrapper_list; ctx; ctx=ctx->next) + if (ctx->reader == reader) + { + if (DBG_LOOKUP) + log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n", + ctx, + (int)ctx->pid, (int)ctx->printable_pid, + ctx->reader, + ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0); + + ctx->reader = NULL; + SAFE_CLOSE (ctx->fd); + if (ctx->ctrl) + { + ctx->ctrl->refcount--; + ctx->ctrl = NULL; + } + if (ctx->fd_error) + log_info (_("reading from ldap wrapper %d failed: %s\n"), + ctx->printable_pid, gpg_strerror (ctx->fd_error)); + break; + } +} + +/* Cleanup all resources held by the connection associated with + CTRL. This is used after a cancel to kill running wrappers. */ +void +ldap_wrapper_connection_cleanup (ctrl_t ctrl) +{ + struct wrapper_context_s *ctx; + + for (ctx=wrapper_list; ctx; ctx=ctx->next) + if (ctx->ctrl && ctx->ctrl == ctrl) + { + ctx->ctrl->refcount--; + ctx->ctrl = NULL; + if (ctx->pid != (pid_t)(-1)) + gnupg_kill_process (ctx->pid); + if (ctx->fd_error) + log_info (_("reading from ldap wrapper %d failed: %s\n"), + ctx->printable_pid, gpg_strerror (ctx->fd_error)); + } +} + + +/* This is the callback used by the ldap wrapper to feed the ksba + reader with the wrappers stdout. See the description of + ksba_reader_set_cb for details. */ +static int +reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread) +{ + struct wrapper_context_s *ctx = cb_value; + size_t nleft = count; + int nfds; + struct timespec abstime; + struct timespec curtime; + struct timespec timeout; + int saved_errno; + fd_set fdset, read_fdset; + int ret; + + /* FIXME: We might want to add some internal buffering because the + ksba code does not do any buffering for itself (because a ksba + reader may be detached from another stream to read other data and + the it would be cumbersome to get back already buffered + stuff). */ + + if (!buffer && !count && !nread) + return -1; /* Rewind is not supported. */ + + /* If we ever encountered a read error, don't continue (we don't want to + possibly overwrite the last error cause). Bail out also if the + file descriptor has been closed. */ + if (ctx->fd_error || ctx->fd == -1) + { + *nread = 0; + return -1; + } + + FD_ZERO (&fdset); + FD_SET (ctx->fd, &fdset); + nfds = ctx->fd + 1; + + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + + while (nleft > 0) + { + int n; + gpg_error_t err; + + npth_clock_gettime (&curtime); + if (!(npth_timercmp (&curtime, &abstime, <))) + { + err = dirmngr_tick (ctx->ctrl); + if (err) + { + ctx->fd_error = err; + SAFE_CLOSE (ctx->fd); + return -1; + } + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL; + } + npth_timersub (&abstime, &curtime, &timeout); + + read_fdset = fdset; + ret = npth_pselect (nfds, &read_fdset, NULL, NULL, &timeout, NULL); + saved_errno = errno; + + if (ret == -1 && saved_errno != EINTR) + { + ctx->fd_error = gpg_error_from_errno (errno); + SAFE_CLOSE (ctx->fd); + return -1; + } + if (ret <= 0) + /* Timeout. Will be handled when calculating the next timeout. */ + continue; + + /* This should not block now that select returned with a file + descriptor. So it shouldn't be necessary to use npth_read + (and it is slightly dangerous in the sense that a concurrent + thread might (accidentially?) change the status of ctx->fd + before we read. FIXME: Set ctx->fd to nonblocking? */ + n = read (ctx->fd, buffer, nleft); + if (n < 0) + { + ctx->fd_error = gpg_error_from_errno (errno); + SAFE_CLOSE (ctx->fd); + return -1; + } + else if (!n) + { + if (nleft == count) + return -1; /* EOF. */ + break; + } + nleft -= n; + buffer += n; + if (n > 0 && ctx->stamp != (time_t)(-1)) + ctx->stamp = time (NULL); + } + *nread = count - nleft; + + return 0; +} + +/* Fork and exec the LDAP wrapper and return a new libksba reader + object at READER. ARGV is a NULL terminated list of arguments for + the wrapper. The function returns 0 on success or an error code. + + Special hack to avoid passing a password through the command line + which is globally visible: If the first element of ARGV is "--pass" + it will be removed and instead the environment variable + DIRMNGR_LDAP_PASS will be set to the next value of ARGV. On modern + OSes the environment is not visible to other users. For those old + systems where it can't be avoided, we don't want to go into the + hassle of passing the password via stdin; it's just too complicated + and an LDAP password used for public directory lookups should not + be that confidential. */ +gpg_error_t +ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) +{ + gpg_error_t err; + pid_t pid; + struct wrapper_context_s *ctx; + int i; + int j; + const char **arg_list; + const char *pgmname; + int outpipe[2], errpipe[2]; + + /* It would be too simple to connect stderr just to our logging + stream. The problem is that if we are running multi-threaded + everything gets intermixed. Clearly we don't want this. So the + only viable solutions are either to have another thread + responsible for logging the messages or to add an option to the + wrapper module to do the logging on its own. Given that we anyway + need a way to reap the child process and this is best done using a + general reaping thread, that thread can do the logging too. */ + ldap_wrapper_launch_thread (); + + *reader = NULL; + + /* Files: We need to prepare stdin and stdout. We get stderr from + the function. */ + if (!opt.ldap_wrapper_program || !*opt.ldap_wrapper_program) + pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR_LDAP); + else + pgmname = opt.ldap_wrapper_program; + + /* Create command line argument array. */ + for (i = 0; argv[i]; i++) + ; + arg_list = xtrycalloc (i + 2, sizeof *arg_list); + if (!arg_list) + { + err = gpg_error_from_syserror (); + log_error (_("error allocating memory: %s\n"), strerror (errno)); + return err; + } + for (i = j = 0; argv[i]; i++, j++) + if (!i && argv[i + 1] && !strcmp (*argv, "--pass")) + { + arg_list[j] = "--env-pass"; + setenv ("DIRMNGR_LDAP_PASS", argv[1], 1); + i++; + } + else + arg_list[j] = (char*) argv[i]; + + ctx = xtrycalloc (1, sizeof *ctx); + if (!ctx) + { + err = gpg_error_from_syserror (); + log_error (_("error allocating memory: %s\n"), strerror (errno)); + xfree (arg_list); + return err; + } + + err = gnupg_create_inbound_pipe (outpipe, NULL, 0); + if (!err) + { + err = gnupg_create_inbound_pipe (errpipe, NULL, 0); + if (err) + { + close (outpipe[0]); + close (outpipe[1]); + } + } + if (err) + { + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + xfree (arg_list); + xfree (ctx); + return err; + } + + err = gnupg_spawn_process_fd (pgmname, arg_list, + -1, outpipe[1], errpipe[1], &pid); + xfree (arg_list); + close (outpipe[1]); + close (errpipe[1]); + if (err) + { + close (outpipe[0]); + close (errpipe[0]); + xfree (ctx); + return err; + } + + ctx->pid = pid; + ctx->printable_pid = (int) pid; + ctx->fd = outpipe[0]; + ctx->log_fd = errpipe[0]; + ctx->ctrl = ctrl; + ctrl->refcount++; + ctx->stamp = time (NULL); + + err = ksba_reader_new (reader); + if (!err) + err = ksba_reader_set_cb (*reader, reader_callback, ctx); + if (err) + { + log_error (_("error initializing reader object: %s\n"), + gpg_strerror (err)); + destroy_wrapper (ctx); + ksba_reader_release (*reader); + *reader = NULL; + return err; + } + + /* Hook the context into our list of running wrappers. */ + ctx->reader = *reader; + ctx->next = wrapper_list; + wrapper_list = ctx; + if (opt.verbose) + log_info ("ldap wrapper %d started (reader %p)\n", + (int)ctx->pid, ctx->reader); + + /* Need to wait for the first byte so we are able to detect an empty + output and not let the consumer see an EOF without further error + indications. The CRL loading logic assumes that after return + from this function, a failed search (e.g. host not found ) is + indicated right away. */ + { + unsigned char c; + + err = read_buffer (*reader, &c, 1); + if (err) + { + ldap_wrapper_release_context (*reader); + ksba_reader_release (*reader); + *reader = NULL; + if (gpg_err_code (err) == GPG_ERR_EOF) + return gpg_error (GPG_ERR_NO_DATA); + else + return err; + } + ksba_reader_unread (*reader, &c, 1); + } + + return 0; +} diff --git a/dirmngr/ldap-wrapper.h b/dirmngr/ldap-wrapper.h new file mode 100644 index 0000000..a015efa --- /dev/null +++ b/dirmngr/ldap-wrapper.h @@ -0,0 +1,40 @@ +/* ldap-wrapper.h - Interface to an LDAP access wrapper. + * Copyright (C) 2010 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 3 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, see . + */ + +#ifndef LDAP_WRAPPER_H +#define LDAP_WRAPPER_H + +#include + +/* ldap-wrapper.c or ldap-wrapper-ce.c */ +void ldap_wrapper_launch_thread (void); +void ldap_wrapper_wait_connections (void); +void ldap_wrapper_release_context (ksba_reader_t reader); +void ldap_wrapper_connection_cleanup (ctrl_t); +gpg_error_t ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, + const char *argv[]); + + +/* dirmngr_ldap.c */ +#ifndef USE_LDAPWRAPPER +int ldap_wrapper_main (char **argv, estream_t outstream); +#endif + + +#endif /*LDAP_WRAPPER_H*/ diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c new file mode 100644 index 0000000..20cbbd8 --- /dev/null +++ b/dirmngr/ldap.c @@ -0,0 +1,875 @@ +/* ldap.c - LDAP access + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "exechelp.h" +#include "crlfetch.h" +#include "ldapserver.h" +#include "misc.h" +#include "ldap-wrapper.h" +#include "host2net.h" + + +#define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "01234567890" \ + "$-_.+!*'()," +#define USERCERTIFICATE "userCertificate" +#define CACERTIFICATE "caCertificate" +#define X509CACERT "x509caCert" +#define USERSMIMECERTIFICATE "userSMIMECertificate" + + +/* Definition for the context of the cert fetch functions. */ +struct cert_fetch_context_s +{ + ksba_reader_t reader; /* The reader used (shallow copy). */ + unsigned char *tmpbuf; /* Helper buffer. */ + size_t tmpbufsize; /* Allocated size of tmpbuf. */ + int truncated; /* Flag to indicate a truncated output. */ +}; + + + + +/* Add HOST and PORT to our list of LDAP servers. Fixme: We should + better use an extra list of servers. */ +static void +add_server_to_servers (const char *host, int port) +{ + ldap_server_t server; + ldap_server_t last = NULL; + const char *s; + + if (!port) + port = 389; + + for (server=opt.ldapservers; server; server = server->next) + { + if (!strcmp (server->host, host) && server->port == port) + return; /* already in list... */ + last = server; + } + + /* We assume that the host names are all supplied by our + configuration files and thus are sane. To keep this assumption + we must reject all invalid host names. */ + for (s=host; *s; s++) + if (!strchr ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "01234567890.-", *s)) + { + log_error (_("invalid char 0x%02x in host name - not added\n"), *s); + return; + } + + log_info (_("adding '%s:%d' to the ldap server list\n"), host, port); + server = xtrycalloc (1, sizeof *s); + if (!server) + log_error (_("malloc failed: %s\n"), strerror (errno)); + else + { + server->host = xstrdup (host); + server->port = port; + if (last) + last->next = server; + else + opt.ldapservers = server; + } +} + + + + +/* Perform an LDAP query. Returns an gpg error code or 0 on success. + The function returns a new reader object at READER. */ +static gpg_error_t +run_ldap_wrapper (ctrl_t ctrl, + int ignore_timeout, + int multi_mode, + const char *proxy, + const char *host, int port, + const char *user, const char *pass, + const char *dn, const char *filter, const char *attr, + const char *url, + ksba_reader_t *reader) +{ + const char *argv[40]; + int argc; + char portbuf[30], timeoutbuf[30]; + + + *reader = NULL; + + argc = 0; + if (pass) /* Note, that the password must be the first item. */ + { + argv[argc++] = "--pass"; + argv[argc++] = pass; + } + if (opt.verbose) + argv[argc++] = "-vv"; + argv[argc++] = "--log-with-pid"; + if (multi_mode) + argv[argc++] = "--multi"; + if (opt.ldaptimeout) + { + sprintf (timeoutbuf, "%u", opt.ldaptimeout); + argv[argc++] = "--timeout"; + argv[argc++] = timeoutbuf; + if (ignore_timeout) + argv[argc++] = "--only-search-timeout"; + } + if (proxy) + { + argv[argc++] = "--proxy"; + argv[argc++] = proxy; + } + if (host) + { + argv[argc++] = "--host"; + argv[argc++] = host; + } + if (port) + { + sprintf (portbuf, "%d", port); + argv[argc++] = "--port"; + argv[argc++] = portbuf; + } + if (user) + { + argv[argc++] = "--user"; + argv[argc++] = user; + } + if (dn) + { + argv[argc++] = "--dn"; + argv[argc++] = dn; + } + if (filter) + { + argv[argc++] = "--filter"; + argv[argc++] = filter; + } + if (attr) + { + argv[argc++] = "--attr"; + argv[argc++] = attr; + } + argv[argc++] = url? url : "ldap://"; + argv[argc] = NULL; + + return ldap_wrapper (ctrl, reader, argv); +} + + + + +/* Perform a LDAP query using a given URL. On success a new ksba + reader is returned. If HOST or PORT are not 0, they are used to + override the values from the URL. */ +gpg_error_t +url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port, + ksba_reader_t *reader) +{ + gpg_error_t err; + + err = run_ldap_wrapper (ctrl, + 1, /* Ignore explicit timeout because CRLs + might be very large. */ + 0, + opt.ldap_proxy, + host, port, + NULL, NULL, + NULL, NULL, NULL, url, + reader); + + /* FIXME: This option might be used for DoS attacks. Because it + will enlarge the list of servers to consult without a limit and + all LDAP queries w/o a host are will then try each host in + turn. */ + if (!err && opt.add_new_ldapservers && !opt.ldap_proxy) + { + if (host) + add_server_to_servers (host, port); + else if (url) + { + char *tmp = host_and_port_from_url (url, &port); + if (tmp) + { + add_server_to_servers (tmp, port); + xfree (tmp); + } + } + } + + /* If the lookup failed and we are not only using the proxy, we try + again using our default list of servers. */ + if (err && !(opt.ldap_proxy && opt.only_ldap_proxy)) + { + struct ldapserver_iter iter; + + if (DBG_LOOKUP) + log_debug ("no hostname in URL or query failed; " + "trying all default hostnames\n"); + + for (ldapserver_iter_begin (&iter, ctrl); + err && ! ldapserver_iter_end_p (&iter); + ldapserver_iter_next (&iter)) + { + ldap_server_t server = iter.server; + + err = run_ldap_wrapper (ctrl, + 0, + 0, + NULL, + server->host, server->port, + NULL, NULL, + NULL, NULL, NULL, url, + reader); + if (!err) + break; + } + } + + return err; +} + + + +/* Perform an LDAP query on all configured servers. On error the + error code of the last try is returned. */ +gpg_error_t +attr_fetch_ldap (ctrl_t ctrl, + const char *dn, const char *attr, ksba_reader_t *reader) +{ + gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION); + struct ldapserver_iter iter; + + *reader = NULL; + + /* FIXME; we might want to look at the Base SN to try matching + servers first. */ + for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); + ldapserver_iter_next (&iter)) + { + ldap_server_t server = iter.server; + + err = run_ldap_wrapper (ctrl, + 0, + 0, + opt.ldap_proxy, + server->host, server->port, + server->user, server->pass, + dn, "objectClass=*", attr, NULL, + reader); + if (!err) + break; /* Probably found a result. Ready. */ + } + return err; +} + + +/* Parse PATTERN and return a new strlist to be used for the actual + LDAP query. Bit 0 of the flags field is set if that pattern is + actually a base specification. Caller must release the returned + strlist. NULL is returned on error. + + * Possible patterns: + * + * KeyID + * Fingerprint + * OpenPGP userid + * x Email address Indicated by a left angle bracket. + * Exact word match in user id or subj. name + * x Subj. DN indicated bu a leading slash + * Issuer DN + * Serial number + subj. DN + * x Substring match indicated by a leading '*; is also the default. + */ + +strlist_t +parse_one_pattern (const char *pattern) +{ + strlist_t result = NULL; + char *p; + + switch (*pattern) + { + case '<': /* Email. */ + { + pattern++; + result = xmalloc (sizeof *result + 5 + strlen (pattern)); + result->next = NULL; + result->flags = 0; + p = stpcpy (stpcpy (result->d, "mail="), pattern); + if (p[-1] == '>') + *--p = 0; + if (!*result->d) /* Error. */ + { + xfree (result); + result = NULL; + } + break; + } + case '/': /* Subject DN. */ + pattern++; + if (*pattern) + { + result = xmalloc (sizeof *result + strlen (pattern)); + result->next = NULL; + result->flags = 1; /* Base spec. */ + strcpy (result->d, pattern); + } + break; + case '#': /* Issuer DN. */ + pattern++; + if (*pattern == '/') /* Just issuer DN. */ + { + pattern++; + } + else /* Serial number + issuer DN */ + { + } + break; + case '*': + pattern++; + default: /* Take as substring match. */ + { + const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))"; + + if (*pattern) + { + result = xmalloc (sizeof *result + + strlen (format) + 3 * strlen (pattern)); + result->next = NULL; + result->flags = 0; + sprintf (result->d, format, pattern, pattern, pattern); + } + } + break; + } + + return result; +} + +/* Take the string STRING and escape it according to the URL rules. + Retun a newly allocated string. */ +static char * +escape4url (const char *string) +{ + const char *s; + char *buf, *p; + size_t n; + + if (!string) + string = ""; + + for (s=string,n=0; *s; s++) + if (strchr (UNENCODED_URL_CHARS, *s)) + n++; + else + n += 3; + + buf = malloc (n+1); + if (!buf) + return NULL; + + for (s=string,p=buf; *s; s++) + if (strchr (UNENCODED_URL_CHARS, *s)) + *p++ = *s; + else + { + sprintf (p, "%%%02X", *(const unsigned char *)s); + p += 3; + } + *p = 0; + + return buf; +} + + + +/* Create a LDAP URL from DN and FILTER and return it in URL. We don't + need the host and port because this will be specified using the + override options. */ +static gpg_error_t +make_url (char **url, const char *dn, const char *filter) +{ + gpg_error_t err; + char *u_dn, *u_filter; + char const attrs[] = (USERCERTIFICATE "," +/* USERSMIMECERTIFICATE "," */ + CACERTIFICATE "," + X509CACERT ); + + *url = NULL; + + u_dn = escape4url (dn); + if (!u_dn) + return gpg_error_from_errno (errno); + + u_filter = escape4url (filter); + if (!u_filter) + { + err = gpg_error_from_errno (errno); + xfree (u_dn); + return err; + } + *url = malloc ( 8 + strlen (u_dn) + + 1 + strlen (attrs) + + 5 + strlen (u_filter) + 1 ); + if (!*url) + { + err = gpg_error_from_errno (errno); + xfree (u_dn); + xfree (u_filter); + return err; + } + + stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (*url, "ldap:///"), + u_dn), + "?"), + attrs), + "?sub?"), + u_filter); + xfree (u_dn); + xfree (u_filter); + return 0; +} + + +/* Prepare an LDAP query to return the attribute ATTR for the DN. All + configured default servers are queried until one responds. This + function returns an error code or 0 and a CONTEXT on success. */ +gpg_error_t +start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, + const char *dn, const char *attr) +{ + gpg_error_t err; + struct ldapserver_iter iter; + + *context = xtrycalloc (1, sizeof **context); + if (!*context) + return gpg_error_from_errno (errno); + + /* FIXME; we might want to look at the Base SN to try matching + servers first. */ + err = gpg_error (GPG_ERR_CONFIGURATION); + + for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); + ldapserver_iter_next (&iter)) + { + ldap_server_t server = iter.server; + + err = run_ldap_wrapper (ctrl, + 0, + 1, + opt.ldap_proxy, + server->host, server->port, + server->user, server->pass, + dn, "objectClass=*", attr, NULL, + &(*context)->reader); + if (!err) + break; /* Probably found a result. */ + } + + if (err) + { + xfree (*context); + *context = NULL; + } + return err; +} + + +/* Prepare an LDAP query to return certificates matching PATTERNS using + the SERVER. This function returns an error code or 0 and a CONTEXT + on success. */ +gpg_error_t +start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, + strlist_t patterns, const ldap_server_t server) +{ + gpg_error_t err; + char *proxy = NULL; + char *host = NULL; + int port; + char *user = NULL; + char *pass = NULL; + const char *base; + char *argv[50]; + int argc = 0; + int argc_malloced = 0; + char portbuf[30], timeoutbuf[30]; + + + *context = NULL; + + if (opt.ldap_proxy && !(proxy = xtrystrdup (opt.ldap_proxy))) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (server) + { + if (server->host && !(host = xtrystrdup (server->host))) + { + err = gpg_error_from_syserror (); + goto leave; + } + port = server->port; + if (server->user && !(user = xtrystrdup (server->user))) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (server->pass && !(pass = xtrystrdup (server->pass))) + { + err = gpg_error_from_syserror (); + goto leave; + } + base = server->base; + + } + else /* Use a default server. */ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + + + if (!base) + base = ""; + + if (pass) /* Note: Must be the first item. */ + { + argv[argc++] = "--pass"; + argv[argc++] = pass; + } + if (opt.verbose) + argv[argc++] = "-vv"; + argv[argc++] = "--log-with-pid"; + argv[argc++] = "--multi"; + if (opt.ldaptimeout) + { + snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout); + argv[argc++] = "--timeout"; + argv[argc++] = timeoutbuf; + } + if (opt.ldap_proxy) + { + argv[argc++] = "--proxy"; + argv[argc++] = proxy; + } + if (host) + { + argv[argc++] = "--host"; + argv[argc++] = host; + } + if (port) + { + snprintf (portbuf, sizeof portbuf, "%d", port); + argv[argc++] = "--port"; + argv[argc++] = portbuf; + } + if (user) + { + argv[argc++] = "--user"; + argv[argc++] = user; + } + + /* All entries in argv from this index on are malloc'ed. */ + argc_malloced = argc; + + for (; patterns; patterns = patterns->next) + { + strlist_t sl; + char *url; + + if (argc >= DIM (argv) - 1) + { + /* Too many patterns. It does not make sense to allow an + arbitrary number of patters because the length of the + command line is limited anyway. */ + /* fixme: cleanup. */ + return gpg_error (GPG_ERR_RESOURCE_LIMIT); + } + sl = parse_one_pattern (patterns->d); + if (!sl) + { + log_error (_("start_cert_fetch: invalid pattern '%s'\n"), + patterns->d); + err = gpg_error (GPG_ERR_INV_USER_ID); + goto leave; + } + if ((sl->flags & 1)) + err = make_url (&url, sl->d, "objectClass=*"); + else + err = make_url (&url, base, sl->d); + free_strlist (sl); + if (err) + goto leave; + argv[argc++] = url; + } + argv[argc] = NULL; + + *context = xtrycalloc (1, sizeof **context); + if (!*context) + { + err = gpg_error_from_errno (errno); + goto leave; + } + + err = ldap_wrapper (ctrl, &(*context)->reader, (const char**)argv); + + if (err) + { + xfree (*context); + *context = NULL; + } + + leave: + for (; argc_malloced < argc; argc_malloced++) + xfree (argv[argc_malloced]); + xfree (proxy); + xfree (host); + xfree (user); + xfree (pass); + return err; +} + + +/* Read a fixed amount of data from READER into BUFFER. */ +static gpg_error_t +read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) +{ + gpg_error_t err; + size_t nread; + + while (count) + { + err = ksba_reader_read (reader, buffer, count, &nread); + if (err) + return err; + buffer += nread; + count -= nread; + } + return 0; +} + + +/* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no + (more) certificates are available or any other error + code. GPG_ERR_TRUNCATED may be returned to indicate that the result + has been truncated. */ +gpg_error_t +fetch_next_cert_ldap (cert_fetch_context_t context, + unsigned char **value, size_t *valuelen) +{ + gpg_error_t err; + unsigned char hdr[5]; + char *p, *pend; + unsigned long n; + int okay = 0; + /* int is_cms = 0; */ + + *value = NULL; + *valuelen = 0; + + err = 0; + while (!err) + { + err = read_buffer (context->reader, hdr, 5); + if (err) + break; + n = buf32_to_ulong (hdr+1); + if (*hdr == 'V' && okay) + { +#if 0 /* That code is not yet ready. */ + + if (is_cms) + { + /* The certificate needs to be parsed from CMS data. */ + ksba_cms_t cms; + ksba_stop_reason_t stopreason; + int i; + + err = ksba_cms_new (&cms); + if (err) + goto leave; + err = ksba_cms_set_reader_writer (cms, context->reader, NULL); + if (err) + { + log_error ("ksba_cms_set_reader_writer failed: %s\n", + gpg_strerror (err)); + goto leave; + } + + do + { + err = ksba_cms_parse (cms, &stopreason); + if (err) + { + log_error ("ksba_cms_parse failed: %s\n", + gpg_strerror (err)); + goto leave; + } + + if (stopreason == KSBA_SR_BEGIN_DATA) + log_error ("userSMIMECertificate is not " + "a certs-only message\n"); + } + while (stopreason != KSBA_SR_READY); + + for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) + { + check_and_store (ctrl, stats, cert, 0); + ksba_cert_release (cert); + cert = NULL; + } + if (!i) + log_error ("no certificate found\n"); + else + any = 1; + } + else +#endif + { + *value = xtrymalloc (n); + if (!*value) + return gpg_error_from_errno (errno); + *valuelen = n; + err = read_buffer (context->reader, *value, n); + break; /* Ready or error. */ + } + } + else if (!n && *hdr == 'A') + okay = 0; + else if (n) + { + if (n > context->tmpbufsize) + { + xfree (context->tmpbuf); + context->tmpbufsize = 0; + context->tmpbuf = xtrymalloc (n+1); + if (!context->tmpbuf) + return gpg_error_from_errno (errno); + context->tmpbufsize = n; + } + err = read_buffer (context->reader, context->tmpbuf, n); + if (err) + break; + if (*hdr == 'A') + { + p = context->tmpbuf; + p[n] = 0; /*(we allocated one extra byte for this.)*/ + /* fixme: is_cms = 0; */ + if ( (pend = strchr (p, ';')) ) + *pend = 0; /* Strip off the extension. */ + if (!ascii_strcasecmp (p, USERCERTIFICATE)) + { + if (DBG_LOOKUP) + log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", + USERCERTIFICATE); + okay = 1; + } + else if (!ascii_strcasecmp (p, CACERTIFICATE)) + { + if (DBG_LOOKUP) + log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", + CACERTIFICATE); + okay = 1; + } + else if (!ascii_strcasecmp (p, X509CACERT)) + { + if (DBG_LOOKUP) + log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", + CACERTIFICATE); + okay = 1; + } +/* else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */ +/* { */ +/* if (DBG_LOOKUP) */ +/* log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */ +/* USERSMIMECERTIFICATE); */ +/* okay = 1; */ +/* is_cms = 1; */ +/* } */ + else + { + if (DBG_LOOKUP) + log_debug ("fetch_next_cert_ldap: got attribute '%s'" + " - ignored\n", p); + okay = 0; + } + } + else if (*hdr == 'E') + { + p = context->tmpbuf; + p[n] = 0; /*(we allocated one extra byte for this.)*/ + if (!strcmp (p, "truncated")) + { + context->truncated = 1; + log_info (_("ldap_search hit the size limit of" + " the server\n")); + } + } + } + } + + if (err) + { + xfree (*value); + *value = NULL; + *valuelen = 0; + if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated) + { + context->truncated = 0; /* So that the next call would return EOF. */ + err = gpg_error (GPG_ERR_TRUNCATED); + } + } + + return err; +} + + +void +end_cert_fetch_ldap (cert_fetch_context_t context) +{ + if (context) + { + ksba_reader_t reader = context->reader; + + xfree (context->tmpbuf); + xfree (context); + ldap_wrapper_release_context (reader); + ksba_reader_release (reader); + } +} diff --git a/dirmngr/ldapserver.c b/dirmngr/ldapserver.c new file mode 100644 index 0000000..16e13e2 --- /dev/null +++ b/dirmngr/ldapserver.c @@ -0,0 +1,132 @@ +/* dirmngr.c - LDAP access + Copyright (C) 2008 g10 Code GmbH + + This file is part of DirMngr. + + DirMngr 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. + + DirMngr 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 +#endif + +#include "dirmngr.h" +#include "ldapserver.h" + + +/* Release the list of SERVERS. As usual it is okay to call this + function with SERVERS passed as NULL. */ +void +ldapserver_list_free (ldap_server_t servers) +{ + while (servers) + { + ldap_server_t tmp = servers->next; + xfree (servers->host); + xfree (servers->user); + if (servers->pass) + memset (servers->pass, 0, strlen (servers->pass)); + xfree (servers->pass); + xfree (servers->base); + xfree (servers); + servers = tmp; + } +} + + +/* Parse a single LDAP server configuration line. Returns the server + or NULL in case of errors. The configuration line is assumed to be + colon seprated with these fields: + + 1. field: Hostname + 2. field: Portnumber + 3. field: Username + 4. field: Password + 5. field: Base DN + + FILENAME and LINENO are used for diagnostic purposes only. +*/ +ldap_server_t +ldapserver_parse_one (char *line, + const char *filename, unsigned int lineno) +{ + char *p; + char *endp; + ldap_server_t server; + int fieldno; + int fail = 0; + + /* Parse the colon separated fields. */ + server = xcalloc (1, sizeof *server); + for (fieldno = 1, p = line; p; p = endp, fieldno++ ) + { + endp = strchr (p, ':'); + if (endp) + *endp++ = '\0'; + trim_spaces (p); + switch (fieldno) + { + case 1: + if (*p) + server->host = xstrdup (p); + else + { + log_error (_("%s:%u: no hostname given\n"), + filename, lineno); + fail = 1; + } + break; + + case 2: + if (*p) + server->port = atoi (p); + break; + + case 3: + if (*p) + server->user = xstrdup (p); + break; + + case 4: + if (*p && !server->user) + { + log_error (_("%s:%u: password given without user\n"), + filename, lineno); + fail = 1; + } + else if (*p) + server->pass = xstrdup (p); + break; + + case 5: + if (*p) + server->base = xstrdup (p); + break; + + default: + /* (We silently ignore extra fields.) */ + break; + } + } + + if (fail) + { + log_info (_("%s:%u: skipping this line\n"), filename, lineno); + ldapserver_list_free (server); + server = NULL; + } + + return server; +} diff --git a/dirmngr/ldapserver.h b/dirmngr/ldapserver.h new file mode 100644 index 0000000..b6eb452 --- /dev/null +++ b/dirmngr/ldapserver.h @@ -0,0 +1,90 @@ +/* ldapserver.h + Copyright (C) 2008 g10 Code GmbH + + This file is part of DirMngr. + + DirMngr 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. + + DirMngr 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, see . */ + +#ifndef LDAPSERVER_H +#define LDAPSERVER_H + +#include "dirmngr.h" + +/* Release the list of SERVERS. As usual it is okay to call this + function with SERVERS passed as NULL. */ +void ldapserver_list_free (ldap_server_t servers); + + +/* Parse a single LDAP server configuration line. Returns the server + or NULL in case of errors. The configuration line is assumed to be + colon separated with these fields: + + 1. field: Hostname + 2. field: Portnumber + 3. field: Username + 4. field: Password + 5. field: Base DN + + FILENAME and LINENO are used for diagnostic purposes only. +*/ +ldap_server_t ldapserver_parse_one (char *line, + const char *filename, unsigned int lineno); + + +/* Iterate over all servers. */ + +struct ldapserver_iter +{ + ctrl_t ctrl; + enum { LDAPSERVER_SESSION, LDAPSERVER_OPT } group; + ldap_server_t server; +}; + + +static inline void +ldapserver_iter_next (struct ldapserver_iter *iter) +{ + if (iter->server) + iter->server = iter->server->next; + + if (! iter->server) + { + if (iter->group == LDAPSERVER_SESSION) + { + iter->group = LDAPSERVER_OPT; + iter->server = opt.ldapservers; + } + } +} + + +static inline int +ldapserver_iter_end_p (struct ldapserver_iter *iter) +{ + return (iter->group == LDAPSERVER_OPT && iter->server == NULL); +} + + +static inline void +ldapserver_iter_begin (struct ldapserver_iter *iter, ctrl_t ctrl) +{ + iter->ctrl = ctrl; + iter->group = LDAPSERVER_SESSION; + iter->server = get_ldapservers_from_ctrl (ctrl); + + while (iter->server == NULL && ! ldapserver_iter_end_p (iter)) + ldapserver_iter_next (iter); +} + +#endif /* LDAPSERVER_H */ diff --git a/dirmngr/loadswdb.c b/dirmngr/loadswdb.c new file mode 100644 index 0000000..2d6bdc1 --- /dev/null +++ b/dirmngr/loadswdb.c @@ -0,0 +1,397 @@ +/* loadswdb.c - Load the swdb file from versions.gnupg.org + * Copyright (C) 2016 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include + +#include +#include +#include + +#include "dirmngr.h" +#include "../common/ccparray.h" +#include "../common/exectool.h" +#include "misc.h" +#include "ks-engine.h" + + +/* Get the time from the current swdb file and store it at R_FILEDATE + * and R_VERIFIED. If the file does not exist 0 is stored at there. + * The function returns 0 on sucess or an error code. */ +static gpg_error_t +time_of_saved_swdb (const char *fname, time_t *r_filedate, time_t *r_verified) +{ + gpg_error_t err; + estream_t fp = NULL; + char *line = NULL; + size_t length_of_line = 0; + size_t maxlen; + ssize_t len; + char *fields[2]; + gnupg_isotime_t isot; + time_t filedate = (time_t)(-1); + time_t verified = (time_t)(-1); + + *r_filedate = 0; + *r_verified = 0; + + fp = es_fopen (fname, "r"); + err = fp? 0 : gpg_error_from_syserror (); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_ENOENT) + err = 0; /* No file - assume time is the year of Unix. */ + goto leave; + } + + /* Note that the parser uses the first occurance of a matching + * values and ignores possible duplicated values. */ + maxlen = 2048; /* Set limit. */ + while ((len = es_read_line (fp, &line, &length_of_line, &maxlen)) > 0) + { + if (!maxlen) + { + err = gpg_error (GPG_ERR_LINE_TOO_LONG); + goto leave; + } + /* Strip newline and carriage return, if present. */ + while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) + line[--len] = '\0'; + + if (split_fields (line, fields, DIM (fields)) < DIM(fields)) + continue; /* Skip empty lines and names w/o a value. */ + if (*fields[0] == '#') + continue; /* Skip comments. */ + + /* Record the meta data. */ + if (filedate == (time_t)(-1) && !strcmp (fields[0], ".filedate")) + { + if (string2isotime (isot, fields[1])) + filedate = isotime2epoch (isot); + } + else if (verified == (time_t)(-1) && !strcmp (fields[0], ".verified")) + { + if (string2isotime (isot, fields[1])) + verified = isotime2epoch (isot); + } + } + if (len < 0 || es_ferror (fp)) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (filedate == (time_t)(-1) || verified == (time_t)(-1)) + { + err = gpg_error (GPG_ERR_INV_TIME); + goto leave; + } + + *r_filedate = filedate; + *r_verified = verified; + + leave: + if (err) + log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); + xfree (line); + es_fclose (fp); + return err; +} + + + +/* Read a file from URL and return it as an estream memory buffer at + * R_FP. */ +static gpg_error_t +fetch_file (ctrl_t ctrl, const char *url, estream_t *r_fp) +{ + gpg_error_t err; + estream_t fp = NULL; + estream_t httpfp = NULL; + size_t nread, nwritten; + char buffer[1024]; + + if ((err = ks_http_fetch (ctrl, url, &httpfp))) + goto leave; + + /* We now read the data from the web server into a memory buffer. + * To avoid excessive memory use in case of a ill behaving server we + * put a 64 k size limit on the buffer. As of today the actual size + * of the swdb.lst file is 3k. */ + fp = es_fopenmem (64*1024, "rw"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); + goto leave; + } + + for (;;) + { + if (es_read (httpfp, buffer, sizeof buffer, &nread)) + { + err = gpg_error_from_syserror (); + log_error ("error reading '%s': %s\n", + es_fname_get (httpfp), gpg_strerror (err)); + goto leave; + } + + if (!nread) + break; /* Ready. */ + if (es_write (fp, buffer, nread, &nwritten)) + { + err = gpg_error_from_syserror (); + log_error ("error writing '%s': %s\n", + es_fname_get (fp), gpg_strerror (err)); + goto leave; + } + else if (nread != nwritten) + { + err = gpg_error (GPG_ERR_EIO); + log_error ("error writing '%s': %s\n", + es_fname_get (fp), "short write"); + goto leave; + } + } + + es_rewind (fp); + *r_fp = fp; + fp = NULL; + + leave: + es_fclose (httpfp); + es_fclose (fp); + return err; +} + + +/* Communication object for verify_status_cb. */ +struct verify_status_parm_s +{ + time_t sigtime; + int anyvalid; +}; + +static void +verify_status_cb (void *opaque, const char *keyword, char *args) +{ + struct verify_status_parm_s *parm = opaque; + + /* We care only about the first valid signature. */ + if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid) + { + char *fields[3]; + + parm->anyvalid = 1; + if (split_fields (args, fields, DIM (fields)) >= 3) + parm->sigtime = parse_timestamp (fields[2], NULL); + } +} + + + +/* Load the swdb file into the current home directory. Do this onlky + * when needed unless FORCE is set which will always get a new + * copy. */ +gpg_error_t +dirmngr_load_swdb (ctrl_t ctrl, int force) +{ + gpg_error_t err; + char *fname = NULL; /* The swdb.lst file. */ + char *tmp_fname = NULL; /* The temporary swdb.lst file. */ + char *keyfile_fname = NULL; + estream_t swdb = NULL; + estream_t swdb_sig = NULL; + ccparray_t ccp; + const char **argv = NULL; + struct verify_status_parm_s verify_status_parm = { (time_t)(-1), 0 }; + estream_t outfp = NULL; + time_t now = gnupg_get_time (); + time_t filedate = 0; /* ".filedate" from our swdb. */ + time_t verified = 0; /* ".verified" from our swdb. */ + gnupg_isotime_t isotime; + + + fname = make_filename_try (gnupg_homedir (), "swdb.lst", NULL); + if (!fname) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Check whether there is a need to get an update. */ + if (!force) + { + static int not_older_than; + static time_t lastcheck; + + if (!not_older_than) + { + /* To balance access to the server we use a random time from + * 5 to 7 days for update checks. */ + not_older_than = 5 * 86400; + not_older_than += (get_uint_nonce () % (2*86400)); + } + + if (now - lastcheck < 3600) + { + /* We checked our swdb file in the last hour - don't check + * again to avoid unnecessary disk access. */ + err = 0; + goto leave; + } + lastcheck = now; + + err = time_of_saved_swdb (fname, &filedate, &verified); + if (gpg_err_code (err) == GPG_ERR_INV_TIME) + err = 0; /* Force reading. */ + if (err) + goto leave; + if (filedate >= now) + goto leave; /* Current or newer. */ + if (now - filedate < not_older_than) + goto leave; /* Our copy is pretty new (not older than 7 days). */ + if (verified > now && now - verified < 3*3600) + goto leave; /* We downloaded and verified in the last 3 hours. */ + } + + /* Create the filename of the file with the keys. */ + keyfile_fname = make_filename_try (gnupg_datadir (), "distsigkey.gpg", NULL); + if (!keyfile_fname) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Fetch the swdb from the web. */ + err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst", &swdb); + if (err) + goto leave; + err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst.sig", &swdb_sig); + if (err) + goto leave; + + /* Run gpgv. */ + ccparray_init (&ccp, 0); + ccparray_put (&ccp, "--enable-special-filenames"); + ccparray_put (&ccp, "--status-fd=2"); + ccparray_put (&ccp, "--keyring"); + ccparray_put (&ccp, keyfile_fname); + ccparray_put (&ccp, "--"); + ccparray_put (&ccp, "-&@INEXTRA@"); + ccparray_put (&ccp, "-"); + ccparray_put (&ccp, NULL); + argv = ccparray_get (&ccp, NULL); + if (!argv) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV), + argv, swdb, swdb_sig, NULL, + verify_status_cb, &verify_status_parm); + if (!err && verify_status_parm.sigtime == (time_t)(-1)) + err = gpg_error (verify_status_parm.anyvalid? GPG_ERR_BAD_SIGNATURE + /**/ : GPG_ERR_INV_TIME ); + if (err) + goto leave; + + /* If our swdb is not older than the downloaded one. We don't + * bother to update. */ + if (!force && filedate >= verify_status_parm.sigtime) + goto leave; + + /* Create a file name for a temporary file in the home directory. + * We will later rename that file to the real name. */ + { + char *tmpstr; + +#ifdef HAVE_W32_SYSTEM + tmpstr = es_bsprintf ("tmp-%u-swdb", (unsigned int)getpid ()); +#else + tmpstr = es_bsprintf (".#%u.swdb", (unsigned int)getpid ()); +#endif + if (!tmpstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + tmp_fname = make_filename_try (gnupg_homedir (), tmpstr, NULL); + xfree (tmpstr); + if (!tmp_fname) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + outfp = es_fopen (tmp_fname, "w"); + if (!outfp) + { + err = gpg_error_from_syserror (); + log_error (_("error creating '%s': %s\n"), tmp_fname, gpg_strerror (err)); + goto leave; + } + + epoch2isotime (isotime, verify_status_parm.sigtime); + es_fprintf (outfp, ".filedate %s\n", isotime); + epoch2isotime (isotime, now); + es_fprintf (outfp, ".verified %s\n", isotime); + + if (es_fseek (swdb, 0, SEEK_SET)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = copy_stream (swdb, outfp); + if (err) + { + /* Well, it might also be a reading error, but that is pretty + * unlikely for a memory stream. */ + log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err)); + goto leave; + } + + if (es_fclose (outfp)) + { + err = gpg_error_from_syserror (); + log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err)); + goto leave; + } + outfp = NULL; + + err = gnupg_rename_file (tmp_fname, fname, NULL); + if (err) + goto leave; + xfree (tmp_fname); + tmp_fname = NULL; + + + leave: + es_fclose (outfp); + if (tmp_fname) + gnupg_remove (tmp_fname); /* This is a temporary file. */ + xfree (argv); + es_fclose (swdb_sig); + es_fclose (swdb); + xfree (keyfile_fname); + xfree (tmp_fname); + xfree (fname); + return err; +} diff --git a/dirmngr/misc.c b/dirmngr/misc.c new file mode 100644 index 0000000..ac3856e --- /dev/null +++ b/dirmngr/misc.c @@ -0,0 +1,645 @@ +/* misc.c - miscellaneous + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2002, 2003, 2004, 2010 Free Software Foundation, Inc. + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#include +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "util.h" +#include "misc.h" + + +/* Convert the hex encoded STRING back into binary and store the + result into the provided buffer RESULT. The actual size of that + buffer will be returned. The caller should provide RESULT of at + least strlen(STRING)/2 bytes. There is no error detection, the + parsing stops at the first non hex character. With RESULT given as + NULL, the function does only return the size of the buffer which + would be needed. */ +size_t +unhexify (unsigned char *result, const char *string) +{ + const char *s; + size_t n; + + for (s=string,n=0; hexdigitp (s) && hexdigitp(s+1); s += 2) + { + if (result) + result[n] = xtoi_2 (s); + n++; + } + return n; +} + + +char* +hashify_data( const char* data, size_t len ) +{ + unsigned char buf[20]; + gcry_md_hash_buffer (GCRY_MD_SHA1, buf, data, len); + return hexify_data (buf, 20, 0); +} + +char* +hexify_data (const unsigned char* data, size_t len, int with_prefix) +{ + int i; + char *result = xmalloc (2*len + (with_prefix?2:0) + 1); + char *p; + + if (with_prefix) + p = stpcpy (result, "0x"); + else + p = result; + + for (i = 0; i < 2*len; i+=2 ) + snprintf (p+i, 3, "%02X", *data++); + return result; +} + +char * +serial_hex (ksba_sexp_t serial ) +{ + unsigned char* p = serial; + char *endp; + unsigned long n; + char *certid; + + if (!p) + return NULL; + else { + p++; /* ignore initial '(' */ + n = strtoul (p, (char**)&endp, 10); + p = endp; + if (*p!=':') + return NULL; + else { + int i = 0; + certid = xmalloc( sizeof( char )*(2*n + 1 ) ); + for (p++; n; n--, p++) { + sprintf ( certid+i , "%02X", *p); + i += 2; + } + } + } + return certid; +} + + +/* Take an S-Expression encoded blob and return a pointer to the + actual data as well as its length. Return NULL for an invalid + S-Expression.*/ +const unsigned char * +serial_to_buffer (const ksba_sexp_t serial, size_t *length) +{ + unsigned char *p = serial; + char *endp; + unsigned long n; + + if (!p || *p != '(') + return NULL; + p++; + n = strtoul (p, &endp, 10); + p = endp; + if (*p != ':') + return NULL; + p++; + *length = n; + return p; +} + + +/* Do an in-place percent unescaping of STRING. Returns STRING. Note + that this function does not do a '+'-to-space unescaping.*/ +char * +unpercent_string (char *string) +{ + char *s = string; + char *d = string; + + while (*s) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d++ = xtoi_2 ( s); + s += 2; + } + else + *d++ = *s++; + } + *d = 0; + return string; +} + +/* Convert a canonical encoded S-expression in CANON into the GCRY + type. */ +gpg_error_t +canon_sexp_to_gcry (const unsigned char *canon, gcry_sexp_t *r_sexp) +{ + gpg_error_t err; + size_t n; + gcry_sexp_t sexp; + + *r_sexp = NULL; + n = gcry_sexp_canon_len (canon, 0, NULL, NULL); + if (!n) + { + log_error (_("invalid canonical S-expression found\n")); + err = gpg_error (GPG_ERR_INV_SEXP); + } + else if ((err = gcry_sexp_sscan (&sexp, NULL, canon, n))) + log_error (_("converting S-expression failed: %s\n"), gcry_strerror (err)); + else + *r_sexp = sexp; + return err; +} + + +/* Return an allocated buffer with the formatted fingerprint as one + large hexnumber */ +char * +get_fingerprint_hexstring (ksba_cert_t cert) +{ + unsigned char digest[20]; + gcry_md_hd_t md; + int rc; + char *buf; + int i; + + rc = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (rc) + log_fatal (_("gcry_md_open failed: %s\n"), gpg_strerror (rc)); + + rc = ksba_cert_hash (cert, 0, HASH_FNC, md); + if (rc) + { + log_error (_("oops: ksba_cert_hash failed: %s\n"), gpg_strerror (rc)); + memset (digest, 0xff, 20); /* Use a dummy value. */ + } + else + { + gcry_md_final (md); + memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20); + } + gcry_md_close (md); + buf = xmalloc (41); + *buf = 0; + for (i=0; i < 20; i++ ) + sprintf (buf+strlen(buf), "%02X", digest[i]); + return buf; +} + +/* Return an allocated buffer with the formatted fingerprint as one + large hexnumber. This version inserts the usual colons. */ +char * +get_fingerprint_hexstring_colon (ksba_cert_t cert) +{ + unsigned char digest[20]; + gcry_md_hd_t md; + int rc; + char *buf; + int i; + + rc = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (rc) + log_fatal (_("gcry_md_open failed: %s\n"), gpg_strerror (rc)); + + rc = ksba_cert_hash (cert, 0, HASH_FNC, md); + if (rc) + { + log_error (_("oops: ksba_cert_hash failed: %s\n"), gpg_strerror (rc)); + memset (digest, 0xff, 20); /* Use a dummy value. */ + } + else + { + gcry_md_final (md); + memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20); + } + gcry_md_close (md); + buf = xmalloc (61); + *buf = 0; + for (i=0; i < 20; i++ ) + sprintf (buf+strlen(buf), "%02X:", digest[i]); + buf[strlen(buf)-1] = 0; /* Remove railing colon. */ + return buf; +} + + +/* Dump the serial number SERIALNO to the log stream. */ +void +dump_serial (ksba_sexp_t serialno) +{ + char *p; + + p = serial_hex (serialno); + log_printf ("%s", p?p:"?"); + xfree (p); +} + + +/* Dump STRING to the log file but choose the best readable + format. */ +void +dump_string (const char *string) +{ + + if (!string) + log_printf ("[error]"); + else + { + const unsigned char *s; + + for (s=string; *s; s++) + { + if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0)) + break; + } + if (!*s && *string != '[') + log_printf ("%s", string); + else + { + log_printf ( "[ "); + log_printhex (NULL, string, strlen (string)); + log_printf ( " ]"); + } + } +} + +/* Dump an KSBA cert object to the log stream. Prefix the output with + TEXT. This is used for debugging. */ +void +dump_cert (const char *text, ksba_cert_t cert) +{ + ksba_sexp_t sexp; + char *p; + ksba_isotime_t t; + + log_debug ("BEGIN Certificate '%s':\n", text? text:""); + if (cert) + { + sexp = ksba_cert_get_serial (cert); + p = serial_hex (sexp); + log_debug (" serial: %s\n", p?p:"?"); + xfree (p); + ksba_free (sexp); + + ksba_cert_get_validity (cert, 0, t); + log_debug (" notBefore: "); + dump_isotime (t); + log_printf ("\n"); + ksba_cert_get_validity (cert, 1, t); + log_debug (" notAfter: "); + dump_isotime (t); + log_printf ("\n"); + + p = ksba_cert_get_issuer (cert, 0); + log_debug (" issuer: "); + dump_string (p); + ksba_free (p); + log_printf ("\n"); + + p = ksba_cert_get_subject (cert, 0); + log_debug (" subject: "); + dump_string (p); + ksba_free (p); + log_printf ("\n"); + + log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert)); + + p = get_fingerprint_hexstring (cert); + log_debug (" SHA1 fingerprint: %s\n", p); + xfree (p); + } + log_debug ("END Certificate\n"); +} + + + +/* Log the certificate's name in "#SN/ISSUERDN" format along with + TEXT. */ +void +cert_log_name (const char *text, ksba_cert_t cert) +{ + log_info ("%s", text? text:"certificate" ); + if (cert) + { + ksba_sexp_t sn; + char *p; + + p = ksba_cert_get_issuer (cert, 0); + sn = ksba_cert_get_serial (cert); + if (p && sn) + { + log_printf (" #"); + dump_serial (sn); + log_printf ("/"); + dump_string (p); + } + else + log_printf (" [invalid]"); + ksba_free (sn); + xfree (p); + } + log_printf ("\n"); +} + + +/* Log the certificate's subject DN along with TEXT. */ +void +cert_log_subject (const char *text, ksba_cert_t cert) +{ + log_info ("%s", text? text:"subject" ); + if (cert) + { + char *p; + + p = ksba_cert_get_subject (cert, 0); + if (p) + { + log_printf (" /"); + dump_string (p); + xfree (p); + } + else + log_printf (" [invalid]"); + } + log_printf ("\n"); +} + + +/* Callback to print infos about the TLS certificates. */ +void +cert_log_cb (http_session_t sess, gpg_error_t err, + const char *hostname, const void **certs, size_t *certlens) +{ + ksba_cert_t cert; + size_t n; + + (void)sess; + + if (!err) + return; /* No error - no need to log anything */ + + log_debug ("expected hostname: %s\n", hostname); + for (n=0; certs[n]; n++) + { + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, certs[n], certlens[n]); + if (err) + log_error ("error parsing cert for logging: %s\n", gpg_strerror (err)); + else + { + char textbuf[20]; + snprintf (textbuf, sizeof textbuf, "server[%u]", (unsigned int)n); + dump_cert (textbuf, cert); + } + + ksba_cert_release (cert); + } +} + + +/**************** + * Remove all %xx escapes; this is done inplace. + * Returns: New length of the string. + */ +static int +remove_percent_escapes (unsigned char *string) +{ + int n = 0; + unsigned char *p, *s; + + for (p = s = string; *s; s++) + { + if (*s == '%') + { + if (s[1] && s[2] && hexdigitp (s+1) && hexdigitp (s+2)) + { + s++; + *p = xtoi_2 (s); + s++; + p++; + n++; + } + else + { + *p++ = *s++; + if (*s) + *p++ = *s++; + if (*s) + *p++ = *s++; + if (*s) + *p = 0; + return -1; /* Bad URI. */ + } + } + else + { + *p++ = *s; + n++; + } + } + *p = 0; /* Always keep a string terminator. */ + return n; +} + + +/* Return the host name and the port (0 if none was given) from the + URL. Return NULL on error or if host is not included in the + URL. */ +char * +host_and_port_from_url (const char *url, int *port) +{ + const char *s, *s2; + char *buf, *p; + int n; + + s = url; + + *port = 0; + + /* Find the scheme */ + if ( !(s2 = strchr (s, ':')) || s2 == s ) + return NULL; /* No scheme given. */ + s = s2+1; + + /* Find the hostname */ + if (*s != '/') + return NULL; /* Does not start with a slash. */ + + s++; + if (*s != '/') + return NULL; /* No host name. */ + s++; + + buf = xtrystrdup (s); + if (!buf) + { + log_error (_("malloc failed: %s\n"), strerror (errno)); + return NULL; + } + if ((p = strchr (buf, '/'))) + *p++ = 0; + strlwr (buf); + if ((p = strchr (p, ':'))) + { + *p++ = 0; + *port = atoi (p); + } + + /* Remove quotes and make sure that no Nul has been encoded. */ + if ((n = remove_percent_escapes (buf)) < 0 + || n != strlen (buf) ) + { + log_error (_("bad URL encoding detected\n")); + xfree (buf); + return NULL; + } + + return buf; +} + + +/* A KSBA reader callback to read from an estream. */ +static int +my_estream_ksba_reader_cb (void *cb_value, char *buffer, size_t count, + size_t *r_nread) +{ + estream_t fp = cb_value; + + if (!fp) + return gpg_error (GPG_ERR_INV_VALUE); + + if (!buffer && !count && !r_nread) + { + es_rewind (fp); + return 0; + } + + *r_nread = es_fread (buffer, 1, count, fp); + if (!*r_nread) + return -1; /* EOF or error. */ + return 0; /* Success. */ +} + + +/* Create a KSBA reader object and connect it to the estream FP. */ +gpg_error_t +create_estream_ksba_reader (ksba_reader_t *r_reader, estream_t fp) +{ + gpg_error_t err; + ksba_reader_t reader; + + *r_reader = NULL; + err = ksba_reader_new (&reader); + if (!err) + err = ksba_reader_set_cb (reader, my_estream_ksba_reader_cb, fp); + if (err) + { + log_error (_("error initializing reader object: %s\n"), + gpg_strerror (err)); + ksba_reader_release (reader); + return err; + } + *r_reader = reader; + return 0; +} + +gpg_error_t +armor_data (char **r_string, const void *data, size_t datalen) +{ + gpg_error_t err; + struct b64state b64state; + estream_t fp; + long length; + char *buffer; + size_t nread; + + *r_string = NULL; + + fp = es_fopenmem (0, "rw,samethread"); + if (!fp) + return gpg_error_from_syserror (); + + if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK")) + || (err=b64enc_write (&b64state, data, datalen)) + || (err = b64enc_finish (&b64state))) + { + es_fclose (fp); + return err; + } + + /* FIXME: To avoid the extra buffer allocation estream should + provide a function to snatch the internal allocated memory from + such a memory stream. */ + length = es_ftell (fp); + if (length < 0) + { + err = gpg_error_from_syserror (); + es_fclose (fp); + return err; + } + + buffer = xtrymalloc (length+1); + if (!buffer) + { + err = gpg_error_from_syserror (); + es_fclose (fp); + return err; + } + + es_rewind (fp); + if (es_read (fp, buffer, length, &nread)) + { + err = gpg_error_from_syserror (); + es_fclose (fp); + return err; + } + buffer[nread] = 0; + es_fclose (fp); + + *r_string = buffer; + return 0; +} + +/* Copy all data from IN to OUT. */ +gpg_error_t +copy_stream (estream_t in, estream_t out) +{ + char buffer[512]; + size_t nread; + + while (!es_read (in, buffer, sizeof buffer, &nread)) + { + if (!nread) + return 0; /* EOF */ + if (es_write (out, buffer, nread, NULL)) + break; + + } + return gpg_error_from_syserror (); +} diff --git a/dirmngr/misc.h b/dirmngr/misc.h new file mode 100644 index 0000000..be4049e --- /dev/null +++ b/dirmngr/misc.h @@ -0,0 +1,91 @@ +/* misc.h - miscellaneous + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#ifndef MISC_H +#define MISC_H + +/* Convert hex encoded string back to binary. */ +size_t unhexify (unsigned char *result, const char *string); + +/* Returns SHA1 hash of the data. */ +char* hashify_data( const char* data, size_t len ); + +/* Returns data as a hex string. */ +char* hexify_data (const unsigned char* data, size_t len, int with_prefix); + +/* Returns the serial number as a hex string. */ +char* serial_hex ( ksba_sexp_t serial ); + +/* Take an S-Expression encoded blob and return a pointer to the + actual data as well as its length. */ +const unsigned char *serial_to_buffer (const ksba_sexp_t serial, + size_t *length); + +/* Do an in-place percent unescaping of STRING. Returns STRING. */ +char *unpercent_string (char *string); + +gpg_error_t canon_sexp_to_gcry (const unsigned char *canon, + gcry_sexp_t *r_sexp); + +/* Return an allocated hex-string with the SHA-1 fingerprint of + CERT. */ +char *get_fingerprint_hexstring (ksba_cert_t cert); +/* Return an allocated hex-string with the SHA-1 fingerprint of + CERT. This version inserts the usual colons. */ +char *get_fingerprint_hexstring_colon (ksba_cert_t cert); + +/* Log CERT in short format with s/n and issuer DN prefixed by TEXT. */ +void cert_log_name (const char *text, ksba_cert_t cert); + +/* Log CERT in short format with the subject DN prefixed by TEXT. */ +void cert_log_subject (const char *text, ksba_cert_t cert); + +/* Dump the serial number SERIALNO to the log stream. */ +void dump_serial (ksba_sexp_t serialno); + +/* Dump STRING to the log file but choose the best readable + format. */ +void dump_string (const char *string); + +/* Dump an KSBA cert object to the log stream. Prefix the output with + TEXT. This is used for debugging. */ +void dump_cert (const char *text, ksba_cert_t cert); + +/* Callback to print infos about the TLS certificates. */ +void cert_log_cb (http_session_t sess, gpg_error_t err, + const char *hostname, const void **certs, size_t *certlens); + +/* Return the host name and the port (0 if none was given) from the + URL. Return NULL on error or if host is not included in the + URL. */ +char *host_and_port_from_url (const char *url, int *port); + +/* Create a KSBA reader object and connect it to the estream FP. */ +gpg_error_t create_estream_ksba_reader (ksba_reader_t *r_reader, estream_t fp); + +/* Encode the binary data in {DATA,DATALEN} as ASCII-armored data and + stored it as a NUL-terminated string in *R_STRING. The caller is + responsible for freeing *R_STRING. */ +gpg_error_t armor_data (char **r_string, const void *data, size_t datalen); + +/* Copy all data from IN to OUT. */ +gpg_error_t copy_stream (estream_t in, estream_t out); + +#endif /* MISC_H */ diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c new file mode 100644 index 0000000..8c893aa --- /dev/null +++ b/dirmngr/ocsp.c @@ -0,0 +1,801 @@ +/* ocsp.c - OCSP management + * Copyright (C) 2004, 2007 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "misc.h" +#include "http.h" +#include "validate.h" +#include "certcache.h" +#include "ocsp.h" + +/* The maximum size we allow as a response from an OCSP reponder. */ +#define MAX_RESPONSE_SIZE 65536 + + +static const char oidstr_ocsp[] = "1.3.6.1.5.5.7.48.1"; + + +/* Telesec attribute used to implement a positive confirmation. + + CertHash ::= SEQUENCE { + HashAlgorithm AlgorithmIdentifier, + certificateHash OCTET STRING } + */ +static const char oidstr_certHash[] = "1.3.36.8.3.13"; + + + + +/* Read from FP and return a newly allocated buffer in R_BUFFER with the + entire data read from FP. */ +static gpg_error_t +read_response (estream_t fp, unsigned char **r_buffer, size_t *r_buflen) +{ + gpg_error_t err; + unsigned char *buffer; + size_t bufsize, nbytes; + + *r_buffer = NULL; + *r_buflen = 0; + + bufsize = 4096; + buffer = xtrymalloc (bufsize); + if (!buffer) + return gpg_error_from_errno (errno); + + nbytes = 0; + for (;;) + { + unsigned char *tmp; + size_t nread = 0; + + assert (nbytes < bufsize); + nread = es_fread (buffer+nbytes, 1, bufsize-nbytes, fp); + if (nread < bufsize-nbytes && es_ferror (fp)) + { + err = gpg_error_from_errno (errno); + log_error (_("error reading from responder: %s\n"), + strerror (errno)); + xfree (buffer); + return err; + } + if ( !(nread == bufsize-nbytes && !es_feof (fp))) + { /* Response successfully received. */ + nbytes += nread; + *r_buffer = buffer; + *r_buflen = nbytes; + return 0; + } + + nbytes += nread; + + /* Need to enlarge the buffer. */ + if (bufsize >= MAX_RESPONSE_SIZE) + { + log_error (_("response from server too large; limit is %d bytes\n"), + MAX_RESPONSE_SIZE); + xfree (buffer); + return gpg_error (GPG_ERR_TOO_LARGE); + } + + bufsize += 4096; + tmp = xtryrealloc (buffer, bufsize); + if (!tmp) + { + err = gpg_error_from_errno (errno); + xfree (buffer); + return err; + } + buffer = tmp; + } +} + + +/* Construct an OCSP request, send it to the configured OCSP responder + and parse the response. On success the OCSP context may be used to + further process the response. */ +static gpg_error_t +do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, + const char *url, ksba_cert_t cert, ksba_cert_t issuer_cert) +{ + gpg_error_t err; + unsigned char *request, *response; + size_t requestlen, responselen; + http_t http; + ksba_ocsp_response_status_t response_status; + const char *t; + int redirects_left = 2; + char *free_this = NULL; + + (void)ctrl; + + if (opt.use_tor) + { + /* For now we do not allow OCSP via Tor due to possible privacy + concerns. Needs further research. */ + log_error (_("OCSP request not possible due to Tor mode\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + if (opt.disable_http) + { + log_error (_("OCSP request not possible due to disabled HTTP\n")); + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + + err = ksba_ocsp_add_target (ocsp, cert, issuer_cert); + if (err) + { + log_error (_("error setting OCSP target: %s\n"), gpg_strerror (err)); + return err; + } + + { + size_t n; + unsigned char nonce[32]; + + n = ksba_ocsp_set_nonce (ocsp, NULL, 0); + if (n > sizeof nonce) + n = sizeof nonce; + gcry_create_nonce (nonce, n); + ksba_ocsp_set_nonce (ocsp, nonce, n); + } + + err = ksba_ocsp_build_request (ocsp, &request, &requestlen); + if (err) + { + log_error (_("error building OCSP request: %s\n"), gpg_strerror (err)); + return err; + } + + once_more: + err = http_open (&http, HTTP_REQ_POST, url, NULL, NULL, + ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) + | (opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + ctrl->http_proxy, NULL, NULL, NULL); + if (err) + { + log_error (_("error connecting to '%s': %s\n"), url, gpg_strerror (err)); + xfree (free_this); + return err; + } + + es_fprintf (http_get_write_ptr (http), + "Content-Type: application/ocsp-request\r\n" + "Content-Length: %lu\r\n", + (unsigned long)requestlen ); + http_start_data (http); + if (es_fwrite (request, requestlen, 1, http_get_write_ptr (http)) != 1) + { + err = gpg_error_from_errno (errno); + log_error ("error sending request to '%s': %s\n", url, strerror (errno)); + http_close (http, 0); + xfree (request); + xfree (free_this); + return err; + } + xfree (request); + request = NULL; + + err = http_wait_response (http); + if (err || http_get_status_code (http) != 200) + { + if (err) + log_error (_("error reading HTTP response for '%s': %s\n"), + url, gpg_strerror (err)); + else + { + switch (http_get_status_code (http)) + { + case 301: + case 302: + { + const char *s = http_get_header (http, "Location"); + + log_info (_("URL '%s' redirected to '%s' (%u)\n"), + url, s?s:"[none]", http_get_status_code (http)); + if (s && *s && redirects_left-- ) + { + xfree (free_this); url = NULL; + free_this = xtrystrdup (s); + if (!free_this) + err = gpg_error_from_errno (errno); + else + { + url = free_this; + http_close (http, 0); + goto once_more; + } + } + else + err = gpg_error (GPG_ERR_NO_DATA); + log_error (_("too many redirections\n")); + } + break; + + default: + log_error (_("error accessing '%s': http status %u\n"), + url, http_get_status_code (http)); + err = gpg_error (GPG_ERR_NO_DATA); + break; + } + } + http_close (http, 0); + xfree (free_this); + return err; + } + + err = read_response (http_get_read_ptr (http), &response, &responselen); + http_close (http, 0); + if (err) + { + log_error (_("error reading HTTP response for '%s': %s\n"), + url, gpg_strerror (err)); + xfree (free_this); + return err; + } + + err = ksba_ocsp_parse_response (ocsp, response, responselen, + &response_status); + if (err) + { + log_error (_("error parsing OCSP response for '%s': %s\n"), + url, gpg_strerror (err)); + xfree (response); + xfree (free_this); + return err; + } + + switch (response_status) + { + case KSBA_OCSP_RSPSTATUS_SUCCESS: t = "success"; break; + case KSBA_OCSP_RSPSTATUS_MALFORMED: t = "malformed"; break; + case KSBA_OCSP_RSPSTATUS_INTERNAL: t = "internal error"; break; + case KSBA_OCSP_RSPSTATUS_TRYLATER: t = "try later"; break; + case KSBA_OCSP_RSPSTATUS_SIGREQUIRED: t = "must sign request"; break; + case KSBA_OCSP_RSPSTATUS_UNAUTHORIZED: t = "unauthorized"; break; + case KSBA_OCSP_RSPSTATUS_REPLAYED: t = "replay detected"; break; + case KSBA_OCSP_RSPSTATUS_OTHER: t = "other (unknown)"; break; + case KSBA_OCSP_RSPSTATUS_NONE: t = "no status"; break; + default: t = "[unknown status]"; break; + } + if (response_status == KSBA_OCSP_RSPSTATUS_SUCCESS) + { + if (opt.verbose) + log_info (_("OCSP responder at '%s' status: %s\n"), url, t); + + err = ksba_ocsp_hash_response (ocsp, response, responselen, + HASH_FNC, md); + if (err) + log_error (_("hashing the OCSP response for '%s' failed: %s\n"), + url, gpg_strerror (err)); + } + else + { + log_error (_("OCSP responder at '%s' status: %s\n"), url, t); + err = gpg_error (GPG_ERR_GENERAL); + } + + xfree (response); + xfree (free_this); + return err; +} + + +/* Validate that CERT is indeed valid to sign an OCSP response. If + SIGNER_FPR_LIST is not NULL we simply check that CERT matches one + of the fingerprints in this list. */ +static gpg_error_t +validate_responder_cert (ctrl_t ctrl, ksba_cert_t cert, + fingerprint_list_t signer_fpr_list) +{ + gpg_error_t err; + char *fpr; + + if (signer_fpr_list) + { + fpr = get_fingerprint_hexstring (cert); + for (; signer_fpr_list && strcmp (signer_fpr_list->hexfpr, fpr); + signer_fpr_list = signer_fpr_list->next) + ; + if (signer_fpr_list) + err = 0; + else + { + log_error (_("not signed by a default OCSP signer's certificate")); + err = gpg_error (GPG_ERR_BAD_CA_CERT); + } + xfree (fpr); + } + else + { + /* We avoid duplicating the entire certificate validation code + from gpgsm here. Because we have no way calling back to the + client and letting it compute the validity, we use the ugly + hack of telling the client that the response will only be + valid if the certificate given in this status message is + valid. + + Note, that in theory we could simply ask the client via an + inquire to validate a certificate but this might involve + calling DirMngr again recursivly - we can't do that as of now + (neither DirMngr nor gpgsm have the ability for concurrent + access to DirMngr. */ + + /* FIXME: We should cache this certificate locally, so that the next + call to dirmngr won't need to look it up - if this works at + all. */ + fpr = get_fingerprint_hexstring (cert); + dirmngr_status (ctrl, "ONLY_VALID_IF_CERT_VALID", fpr, NULL); + xfree (fpr); + err = 0; + } + + return err; +} + + +/* Helper for check_signature. */ +static int +check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, + gcry_sexp_t s_hash, fingerprint_list_t signer_fpr_list) +{ + gpg_error_t err; + ksba_sexp_t pubkey; + gcry_sexp_t s_pkey = NULL; + + pubkey = ksba_cert_get_public_key (cert); + if (!pubkey) + err = gpg_error (GPG_ERR_INV_OBJ); + else + err = canon_sexp_to_gcry (pubkey, &s_pkey); + xfree (pubkey); + if (!err) + err = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (!err) + err = validate_responder_cert (ctrl, cert, signer_fpr_list); + if (!err) + { + gcry_sexp_release (s_pkey); + return 0; /* Successfully verified the signature. */ + } + + /* We simply ignore all errors. */ + gcry_sexp_release (s_pkey); + return -1; +} + + +/* Check the signature of an OCSP repsonse. OCSP is the context, + S_SIG the signature value and MD the handle of the hash we used for + the response. This function automagically finds the correct public + key. If SIGNER_FPR_LIST is not NULL, the default OCSP reponder has been + used and thus the certificate is one of those identified by + the fingerprints. */ +static gpg_error_t +check_signature (ctrl_t ctrl, + ksba_ocsp_t ocsp, gcry_sexp_t s_sig, gcry_md_hd_t md, + fingerprint_list_t signer_fpr_list) +{ + gpg_error_t err; + int algo, cert_idx; + gcry_sexp_t s_hash; + ksba_cert_t cert; + + /* Create a suitable S-expression with the hash value of our response. */ + gcry_md_final (md); + algo = gcry_md_get_algo (md); + if (algo != GCRY_MD_SHA1 ) + { + log_error (_("only SHA-1 is supported for OCSP responses\n")); + return gpg_error (GPG_ERR_DIGEST_ALGO); + } + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash sha1 %b))", + gcry_md_get_algo_dlen (algo), + gcry_md_read (md, algo)); + if (err) + { + log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); + return err; + } + + /* Get rid of old OCSP specific certificate references. */ + release_ctrl_ocsp_certs (ctrl); + + if (signer_fpr_list && !signer_fpr_list->next) + { + /* There is exactly one signer fingerprint given. Thus we use + the default OCSP responder's certificate and instantly know + the certificate to use. */ + cert = get_cert_byhexfpr (signer_fpr_list->hexfpr); + if (!cert) + cert = get_cert_local (ctrl, signer_fpr_list->hexfpr); + if (cert) + { + err = check_signature_core (ctrl, cert, s_sig, s_hash, + signer_fpr_list); + ksba_cert_release (cert); + cert = NULL; + if (!err) + { + gcry_sexp_release (s_hash); + return 0; /* Successfully verified the signature. */ + } + } + } + else + { + char *name; + ksba_sexp_t keyid; + + /* Put all certificates included in the response into the cache + and setup a list of those certificate which will later be + preferred used when locating certificates. */ + for (cert_idx=0; (cert = ksba_ocsp_get_cert (ocsp, cert_idx)); + cert_idx++) + { + cert_ref_t cref; + + cref = xtrymalloc (sizeof *cref); + if (!cref) + log_error (_("allocating list item failed: %s\n"), + gcry_strerror (err)); + else if (!cache_cert_silent (cert, &cref->fpr)) + { + cref->next = ctrl->ocsp_certs; + ctrl->ocsp_certs = cref; + } + else + xfree (cref); + } + + /* Get the certificate by means of the responder ID. */ + err = ksba_ocsp_get_responder_id (ocsp, &name, &keyid); + if (err) + { + log_error (_("error getting responder ID: %s\n"), + gcry_strerror (err)); + return err; + } + cert = find_cert_bysubject (ctrl, name, keyid); + if (!cert) + { + log_error ("responder certificate "); + if (name) + log_printf ("'/%s' ", name); + if (keyid) + { + log_printf ("{"); + dump_serial (keyid); + log_printf ("} "); + } + log_printf ("not found\n"); + } + ksba_free (name); + ksba_free (keyid); + + if (cert) + { + err = check_signature_core (ctrl, cert, s_sig, s_hash, + signer_fpr_list); + ksba_cert_release (cert); + if (!err) + { + gcry_sexp_release (s_hash); + return 0; /* Successfully verified the signature. */ + } + } + } + + gcry_sexp_release (s_hash); + log_error (_("no suitable certificate found to verify the OCSP response\n")); + return gpg_error (GPG_ERR_NO_PUBKEY); +} + + +/* Check whether the certificate either given by fingerprint CERT_FPR + or directly through the CERT object is valid by running an OCSP + transaction. With FORCE_DEFAULT_RESPONDER set only the configured + default responder is used. */ +gpg_error_t +ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, + int force_default_responder) +{ + gpg_error_t err; + ksba_ocsp_t ocsp = NULL; + ksba_cert_t issuer_cert = NULL; + ksba_sexp_t sigval = NULL; + gcry_sexp_t s_sig = NULL; + ksba_isotime_t current_time; + ksba_isotime_t this_update, next_update, revocation_time, produced_at; + ksba_isotime_t tmp_time; + ksba_status_t status; + ksba_crl_reason_t reason; + char *url_buffer = NULL; + const char *url; + gcry_md_hd_t md = NULL; + int i, idx; + char *oid; + ksba_name_t name; + fingerprint_list_t default_signer = NULL; + + /* Get the certificate. */ + if (cert) + { + ksba_cert_ref (cert); + + err = find_issuing_cert (ctrl, cert, &issuer_cert); + if (err) + { + log_error (_("issuer certificate not found: %s\n"), + gpg_strerror (err)); + goto leave; + } + } + else + { + cert = get_cert_local (ctrl, cert_fpr); + if (!cert) + { + log_error (_("caller did not return the target certificate\n")); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + issuer_cert = get_issuing_cert_local (ctrl, NULL); + if (!issuer_cert) + { + log_error (_("caller did not return the issuing certificate\n")); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + } + + /* Create an OCSP instance. */ + err = ksba_ocsp_new (&ocsp); + if (err) + { + log_error (_("failed to allocate OCSP context: %s\n"), + gpg_strerror (err)); + goto leave; + } + + + + /* Figure out the OCSP responder to use. + 1. Try to get the reponder from the certificate. + We do only take http and https style URIs into account. + 2. If this fails use the default responder, if any. + */ + url = NULL; + for (idx=0; !url && !opt.ignore_ocsp_service_url && !force_default_responder + && !(err=ksba_cert_get_authority_info_access (cert, idx, + &oid, &name)); idx++) + { + if ( !strcmp (oid, oidstr_ocsp) ) + { + for (i=0; !url && ksba_name_enum (name, i); i++) + { + char *p = ksba_name_get_uri (name, i); + if (p && (!ascii_strncasecmp (p, "http:", 5) + || !ascii_strncasecmp (p, "https:", 6))) + url = url_buffer = p; + else + xfree (p); + } + } + ksba_name_release (name); + ksba_free (oid); + } + if (err && gpg_err_code (err) != GPG_ERR_EOF) + { + log_error (_("can't get authorityInfoAccess: %s\n"), gpg_strerror (err)); + goto leave; + } + if (!url) + { + if (!opt.ocsp_responder || !*opt.ocsp_responder) + { + log_info (_("no default OCSP responder defined\n")); + err = gpg_error (GPG_ERR_CONFIGURATION); + goto leave; + } + if (!opt.ocsp_signer) + { + log_info (_("no default OCSP signer defined\n")); + err = gpg_error (GPG_ERR_CONFIGURATION); + goto leave; + } + url = opt.ocsp_responder; + default_signer = opt.ocsp_signer; + if (opt.verbose) + log_info (_("using default OCSP responder '%s'\n"), url); + } + else + { + if (opt.verbose) + log_info (_("using OCSP responder '%s'\n"), url); + } + + /* Ask the OCSP responder. */ + err = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (err) + { + log_error (_("failed to establish a hashing context for OCSP: %s\n"), + gpg_strerror (err)); + goto leave; + } + err = do_ocsp_request (ctrl, ocsp, md, url, cert, issuer_cert); + if (err) + goto leave; + + /* We got a useful answer, check that the answer has a valid signature. */ + sigval = ksba_ocsp_get_sig_val (ocsp, produced_at); + if (!sigval || !*produced_at) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + if ( (err = canon_sexp_to_gcry (sigval, &s_sig)) ) + goto leave; + xfree (sigval); + sigval = NULL; + err = check_signature (ctrl, ocsp, s_sig, md, default_signer); + if (err) + goto leave; + + /* We only support one certificate per request. Check that the + answer matches the right certificate. */ + err = ksba_ocsp_get_status (ocsp, cert, + &status, this_update, next_update, + revocation_time, &reason); + if (err) + { + log_error (_("error getting OCSP status for target certificate: %s\n"), + gpg_strerror (err)); + goto leave; + } + + /* In case the certificate has been revoked, we better invalidate + our cached validation status. */ + if (status == KSBA_STATUS_REVOKED) + { + time_t validated_at = 0; /* That is: No cached validation available. */ + err = ksba_cert_set_user_data (cert, "validated_at", + &validated_at, sizeof (validated_at)); + if (err) + { + log_error ("set_user_data(validated_at) failed: %s\n", + gpg_strerror (err)); + err = 0; /* The certificate is anyway revoked, and that is a + more important message than the failure of our + cache. */ + } + } + + + if (opt.verbose) + { + log_info (_("certificate status is: %s (this=%s next=%s)\n"), + status == KSBA_STATUS_GOOD? _("good"): + status == KSBA_STATUS_REVOKED? _("revoked"): + status == KSBA_STATUS_UNKNOWN? _("unknown"): + status == KSBA_STATUS_NONE? _("none"): "?", + this_update, next_update); + if (status == KSBA_STATUS_REVOKED) + log_info (_("certificate has been revoked at: %s due to: %s\n"), + revocation_time, + reason == KSBA_CRLREASON_UNSPECIFIED? "unspecified": + reason == KSBA_CRLREASON_KEY_COMPROMISE? "key compromise": + reason == KSBA_CRLREASON_CA_COMPROMISE? "CA compromise": + reason == KSBA_CRLREASON_AFFILIATION_CHANGED? + "affiliation changed": + reason == KSBA_CRLREASON_SUPERSEDED? "superseded": + reason == KSBA_CRLREASON_CESSATION_OF_OPERATION? + "cessation of operation": + reason == KSBA_CRLREASON_CERTIFICATE_HOLD? + "certificate on hold": + reason == KSBA_CRLREASON_REMOVE_FROM_CRL? + "removed from CRL": + reason == KSBA_CRLREASON_PRIVILEGE_WITHDRAWN? + "privilege withdrawn": + reason == KSBA_CRLREASON_AA_COMPROMISE? "AA compromise": + reason == KSBA_CRLREASON_OTHER? "other":"?"); + + } + + + if (status == KSBA_STATUS_REVOKED) + err = gpg_error (GPG_ERR_CERT_REVOKED); + else if (status == KSBA_STATUS_UNKNOWN) + err = gpg_error (GPG_ERR_NO_DATA); + else if (status != KSBA_STATUS_GOOD) + err = gpg_error (GPG_ERR_GENERAL); + + /* Allow for some clock skew. */ + gnupg_get_isotime (current_time); + add_seconds_to_isotime (current_time, opt.ocsp_max_clock_skew); + + if (strcmp (this_update, current_time) > 0 ) + { + log_error (_("OCSP responder returned a status in the future\n")); + log_info ("used now: %s this_update: %s\n", current_time, this_update); + if (!err) + err = gpg_error (GPG_ERR_TIME_CONFLICT); + } + + /* Check that THIS_UPDATE is not too far back in the past. */ + gnupg_copy_time (tmp_time, this_update); + add_seconds_to_isotime (tmp_time, + opt.ocsp_max_period+opt.ocsp_max_clock_skew); + if (!*tmp_time || strcmp (tmp_time, current_time) < 0 ) + { + log_error (_("OCSP responder returned a non-current status\n")); + log_info ("used now: %s this_update: %s\n", + current_time, this_update); + if (!err) + err = gpg_error (GPG_ERR_TIME_CONFLICT); + } + + /* Check that we are not beyound NEXT_UPDATE (plus some extra time). */ + if (*next_update) + { + gnupg_copy_time (tmp_time, next_update); + add_seconds_to_isotime (tmp_time, + opt.ocsp_current_period+opt.ocsp_max_clock_skew); + if (!*tmp_time && strcmp (tmp_time, current_time) < 0 ) + { + log_error (_("OCSP responder returned an too old status\n")); + log_info ("used now: %s next_update: %s\n", + current_time, next_update); + if (!err) + err = gpg_error (GPG_ERR_TIME_CONFLICT); + } + } + + + leave: + gcry_md_close (md); + gcry_sexp_release (s_sig); + xfree (sigval); + ksba_cert_release (issuer_cert); + ksba_cert_release (cert); + ksba_ocsp_release (ocsp); + xfree (url_buffer); + return err; +} + + +/* Release the list of OCSP certificates hold in the CTRL object. */ +void +release_ctrl_ocsp_certs (ctrl_t ctrl) +{ + while (ctrl->ocsp_certs) + { + cert_ref_t tmp = ctrl->ocsp_certs->next; + xfree (ctrl->ocsp_certs); + ctrl->ocsp_certs = tmp; + } +} diff --git a/dirmngr/ocsp.h b/dirmngr/ocsp.h new file mode 100644 index 0000000..cfab7dd --- /dev/null +++ b/dirmngr/ocsp.h @@ -0,0 +1,31 @@ +/* ocsp.h - OCSP management + * Copyright (C) 2003 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 OCSP_H +#define OCSP_H + +gpg_error_t ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, + int force_default_responder); + +/* Release the list of OCSP certificates hold in the CTRL object. */ +void release_ctrl_ocsp_certs (ctrl_t ctrl); + +#endif /*OCSP_H*/ diff --git a/dirmngr/server.c b/dirmngr/server.c new file mode 100644 index 0000000..a785238 --- /dev/null +++ b/dirmngr/server.c @@ -0,0 +1,2691 @@ +/* server.c - LDAP and Keyserver access server + * Copyright (C) 2002 Klarälvdalens Datakonsult AB + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011, 2015 g10 Code GmbH + * Copyright (C) 2014, 2015, 2016 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include + +#include "crlcache.h" +#include "crlfetch.h" +#if USE_LDAP +# include "ldapserver.h" +#endif +#include "ocsp.h" +#include "certcache.h" +#include "validate.h" +#include "misc.h" +#if USE_LDAP +# include "ldap-wrapper.h" +#endif +#include "ks-action.h" +#include "ks-engine.h" /* (ks_hkp_print_hosttable) */ +#if USE_LDAP +# include "ldap-parse-uri.h" +#endif +#include "dns-stuff.h" +#include "mbox-util.h" +#include "zb32.h" +#include "server-help.h" + +/* To avoid DoS attacks we limit the size of a certificate to + something reasonable. The DoS was actually only an issue back when + Dirmngr was a system service and not a user service. */ +#define MAX_CERT_LENGTH (16*1024) + +/* The same goes for OpenPGP keyblocks, but here we need to allow for + much longer blocks; a 200k keyblock is not too unusual for keys + with a lot of signatures (e.g. 0x5b0358a2). 9C31503C6D866396 even + has 770 KiB as of 2015-08-23. To avoid adding a runtime option we + now use 20MiB which should really be enough. Well, a key with + several pictures could be larger (the parser as a 18MiB limit for + attribute packets) but it won't be nice to the keyservers to send + them such large blobs. */ +#define MAX_KEYBLOCK_LENGTH (20*1024*1024) + + +#define PARM_ERROR(t) assuan_set_error (ctx, \ + gpg_error (GPG_ERR_ASS_PARAMETER), (t)) +#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) + + + +/* Control structure per connection. */ +struct server_local_s +{ + /* Data used to associate an Assuan context with local server data */ + assuan_context_t assuan_ctx; + + /* Per-session LDAP servers. */ + ldap_server_t ldapservers; + + /* Per-session list of keyservers. */ + uri_item_t keyservers; + + /* If this flag is set to true this dirmngr process will be + terminated after the end of this session. */ + int stopme; + + /* State variable private to is_tor_running. */ + int tor_state; + + /* If the first both flags are set the assuan logging of data lines + * is suppressed. The count variable is used to show the number of + * non-logged bytes. */ + size_t inhibit_data_logging_count; + unsigned int inhibit_data_logging : 1; + unsigned int inhibit_data_logging_now : 1; +}; + + +/* Cookie definition for assuan data line output. */ +static gpgrt_ssize_t data_line_cookie_write (void *cookie, + const void *buffer, size_t size); +static int data_line_cookie_close (void *cookie); +static es_cookie_io_functions_t data_line_cookie_functions = + { + NULL, + data_line_cookie_write, + NULL, + data_line_cookie_close + }; + + + + + +/* Accessor for the local ldapservers variable. */ +ldap_server_t +get_ldapservers_from_ctrl (ctrl_t ctrl) +{ + if (ctrl && ctrl->server_local) + return ctrl->server_local->ldapservers; + else + return NULL; +} + +/* Release an uri_item_t list. */ +static void +release_uri_item_list (uri_item_t list) +{ + while (list) + { + uri_item_t tmp = list->next; + http_release_parsed_uri (list->parsed_uri); + xfree (list); + list = tmp; + } +} + +/* Release all configured keyserver info from CTRL. */ +void +release_ctrl_keyservers (ctrl_t ctrl) +{ + if (! ctrl->server_local) + return; + + release_uri_item_list (ctrl->server_local->keyservers); + ctrl->server_local->keyservers = NULL; +} + + + +/* Helper to print a message while leaving a command. */ +static gpg_error_t +leave_cmd (assuan_context_t ctx, gpg_error_t err) +{ + if (err) + { + const char *name = assuan_get_command_name (ctx); + if (!name) + name = "?"; + if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) + log_error ("command '%s' failed: %s\n", name, + gpg_strerror (err)); + else + log_error ("command '%s' failed: %s <%s>\n", name, + gpg_strerror (err), gpg_strsource (err)); + } + return err; +} + + +/* This is a wrapper around assuan_send_data which makes debugging the + output in verbose mode easier. */ +static gpg_error_t +data_line_write (assuan_context_t ctx, const void *buffer_arg, size_t size) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + const char *buffer = buffer_arg; + gpg_error_t err; + + /* If we do not want logging, enable it it here. */ + if (ctrl && ctrl->server_local && ctrl->server_local->inhibit_data_logging) + ctrl->server_local->inhibit_data_logging_now = 1; + + if (opt.verbose && buffer && size) + { + /* Ease reading of output by sending a physical line at each LF. */ + const char *p; + size_t n, nbytes; + + nbytes = size; + do + { + p = memchr (buffer, '\n', nbytes); + n = p ? (p - buffer) + 1 : nbytes; + err = assuan_send_data (ctx, buffer, n); + if (err) + { + gpg_err_set_errno (EIO); + goto leave; + } + buffer += n; + nbytes -= n; + if (nbytes && (err=assuan_send_data (ctx, NULL, 0))) /* Flush line. */ + { + gpg_err_set_errno (EIO); + goto leave; + } + } + while (nbytes); + } + else + { + err = assuan_send_data (ctx, buffer, size); + if (err) + { + gpg_err_set_errno (EIO); /* For use by data_line_cookie_write. */ + goto leave; + } + } + + leave: + if (ctrl && ctrl->server_local && ctrl->server_local->inhibit_data_logging) + { + ctrl->server_local->inhibit_data_logging_now = 0; + ctrl->server_local->inhibit_data_logging_count += size; + } + + return err; +} + + +/* A write handler used by es_fopencookie to write assuan data + lines. */ +static gpgrt_ssize_t +data_line_cookie_write (void *cookie, const void *buffer, size_t size) +{ + assuan_context_t ctx = cookie; + + if (data_line_write (ctx, buffer, size)) + return -1; + return (gpgrt_ssize_t)size; +} + + +static int +data_line_cookie_close (void *cookie) +{ + assuan_context_t ctx = cookie; + + if (DBG_IPC) + { + ctrl_t ctrl = assuan_get_pointer (ctx); + + if (ctrl && ctrl->server_local + && ctrl->server_local->inhibit_data_logging + && ctrl->server_local->inhibit_data_logging_count) + log_debug ("(%zu bytes sent via D lines not shown)\n", + ctrl->server_local->inhibit_data_logging_count); + } + if (assuan_send_data (ctx, NULL, 0)) + { + gpg_err_set_errno (EIO); + return -1; + } + + return 0; +} + + +/* Copy the % and + escaped string S into the buffer D and replace the + escape sequences. Note, that it is sufficient to allocate the + target string D as long as the source string S, i.e.: strlen(s)+1. + Note further that if S contains an escaped binary Nul the resulting + string D will contain the 0 as well as all other characters but it + will be impossible to know whether this is the original EOS or a + copied Nul. */ +static void +strcpy_escaped_plus (char *d, const unsigned char *s) +{ + while (*s) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d++ = xtoi_2 ( s); + s += 2; + } + else if (*s == '+') + *d++ = ' ', s++; + else + *d++ = *s++; + } + *d = 0; +} + + +/* This function returns true if a Tor server is running. The sattus + is cached for the current connection. */ +static int +is_tor_running (ctrl_t ctrl) +{ + /* Check whether we can connect to the proxy. */ + + if (!ctrl || !ctrl->server_local) + return 0; /* Ooops. */ + + if (!ctrl->server_local->tor_state) + { + assuan_fd_t sock; + + sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR); + if (sock == ASSUAN_INVALID_FD) + ctrl->server_local->tor_state = -1; /* Not running. */ + else + { + assuan_sock_close (sock); + ctrl->server_local->tor_state = 1; /* Running. */ + } + } + return (ctrl->server_local->tor_state > 0); +} + + +/* Return an error if the assuan context does not belong to the owner + of the process or to root. On error FAILTEXT is set as Assuan + error string. */ +static gpg_error_t +check_owner_permission (assuan_context_t ctx, const char *failtext) +{ +#ifdef HAVE_W32_SYSTEM + /* Under Windows the dirmngr is always run under the control of the + user. */ + (void)ctx; + (void)failtext; +#else + gpg_err_code_t ec; + assuan_peercred_t cred; + + ec = gpg_err_code (assuan_get_peercred (ctx, &cred)); + if (!ec && cred->uid && cred->uid != getuid ()) + ec = GPG_ERR_EPERM; + if (ec) + return set_error (ec, failtext); +#endif + return 0; +} + + + +/* Common code for get_cert_local and get_issuer_cert_local. */ +static ksba_cert_t +do_get_cert_local (ctrl_t ctrl, const char *name, const char *command) +{ + unsigned char *value; + size_t valuelen; + int rc; + char *buf; + ksba_cert_t cert; + + if (name) + { + buf = xmalloc ( strlen (command) + 1 + strlen(name) + 1); + strcpy (stpcpy (stpcpy (buf, command), " "), name); + } + else + buf = xstrdup (command); + + rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, + &value, &valuelen, MAX_CERT_LENGTH); + xfree (buf); + if (rc) + { + log_error (_("assuan_inquire(%s) failed: %s\n"), + command, gpg_strerror (rc)); + return NULL; + } + + if (!valuelen) + { + xfree (value); + return NULL; + } + + rc = ksba_cert_new (&cert); + if (!rc) + { + rc = ksba_cert_init_from_mem (cert, value, valuelen); + if (rc) + { + ksba_cert_release (cert); + cert = NULL; + } + } + xfree (value); + return cert; +} + + + +/* Ask back to return a certificate for name, given as a regular + gpgsm certificate indentificates (e.g. fingerprint or one of the + other methods). Alternatively, NULL may be used for NAME to + return the current target certificate. Either return the certificate + in a KSBA object or NULL if it is not available. +*/ +ksba_cert_t +get_cert_local (ctrl_t ctrl, const char *name) +{ + if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) + { + if (opt.debug) + log_debug ("get_cert_local called w/o context\n"); + return NULL; + } + return do_get_cert_local (ctrl, name, "SENDCERT"); + +} + +/* Ask back to return the issuing certificate for name, given as a + regular gpgsm certificate indentificates (e.g. fingerprint or one + of the other methods). Alternatively, NULL may be used for NAME to + return thecurrent target certificate. Either return the certificate + in a KSBA object or NULL if it is not available. + +*/ +ksba_cert_t +get_issuing_cert_local (ctrl_t ctrl, const char *name) +{ + if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) + { + if (opt.debug) + log_debug ("get_issuing_cert_local called w/o context\n"); + return NULL; + } + return do_get_cert_local (ctrl, name, "SENDISSUERCERT"); +} + +/* Ask back to return a certificate with subject NAME and a + subjectKeyIdentifier of KEYID. */ +ksba_cert_t +get_cert_local_ski (ctrl_t ctrl, const char *name, ksba_sexp_t keyid) +{ + unsigned char *value; + size_t valuelen; + int rc; + char *buf; + ksba_cert_t cert; + char *hexkeyid; + + if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) + { + if (opt.debug) + log_debug ("get_cert_local_ski called w/o context\n"); + return NULL; + } + if (!name || !keyid) + { + log_debug ("get_cert_local_ski called with insufficient arguments\n"); + return NULL; + } + + hexkeyid = serial_hex (keyid); + if (!hexkeyid) + { + log_debug ("serial_hex() failed\n"); + return NULL; + } + + buf = xtrymalloc (15 + strlen (hexkeyid) + 2 + strlen(name) + 1); + if (!buf) + { + + log_error ("can't allocate enough memory: %s\n", strerror (errno)); + xfree (hexkeyid); + return NULL; + } + strcpy (stpcpy (stpcpy (stpcpy (buf, "SENDCERT_SKI "), hexkeyid)," /"),name); + xfree (hexkeyid); + + rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, + &value, &valuelen, MAX_CERT_LENGTH); + xfree (buf); + if (rc) + { + log_error (_("assuan_inquire(%s) failed: %s\n"), "SENDCERT_SKI", + gpg_strerror (rc)); + return NULL; + } + + if (!valuelen) + { + xfree (value); + return NULL; + } + + rc = ksba_cert_new (&cert); + if (!rc) + { + rc = ksba_cert_init_from_mem (cert, value, valuelen); + if (rc) + { + ksba_cert_release (cert); + cert = NULL; + } + } + xfree (value); + return cert; +} + + +/* Ask the client via an inquiry to check the istrusted status of the + certificate specified by the hexified fingerprint HEXFPR. Returns + 0 if the certificate is trusted by the client or an error code. */ +gpg_error_t +get_istrusted_from_client (ctrl_t ctrl, const char *hexfpr) +{ + unsigned char *value; + size_t valuelen; + int rc; + char request[100]; + + if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx + || !hexfpr) + return gpg_error (GPG_ERR_INV_ARG); + + snprintf (request, sizeof request, "ISTRUSTED %s", hexfpr); + rc = assuan_inquire (ctrl->server_local->assuan_ctx, request, + &value, &valuelen, 100); + if (rc) + { + log_error (_("assuan_inquire(%s) failed: %s\n"), + request, gpg_strerror (rc)); + return rc; + } + /* The expected data is: "1" or "1 cruft" (not a C-string). */ + if (valuelen && *value == '1' && (valuelen == 1 || spacep (value+1))) + rc = 0; + else + rc = gpg_error (GPG_ERR_NOT_TRUSTED); + xfree (value); + return rc; +} + + + + +/* Ask the client to return the certificate associated with the + current command. This is sometimes needed because the client usually + sends us just the cert ID, assuming that the request can be + satisfied from the cache, where the cert ID is used as key. */ +static int +inquire_cert_and_load_crl (assuan_context_t ctx) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char *value = NULL; + size_t valuelen; + ksba_cert_t cert = NULL; + + err = assuan_inquire( ctx, "SENDCERT", &value, &valuelen, 0); + if (err) + return err; + +/* { */ +/* FILE *fp = fopen ("foo.der", "r"); */ +/* value = xmalloc (2000); */ +/* valuelen = fread (value, 1, 2000, fp); */ +/* fclose (fp); */ +/* } */ + + if (!valuelen) /* No data returned; return a comprehensible error. */ + return gpg_error (GPG_ERR_MISSING_CERT); + + err = ksba_cert_new (&cert); + if (err) + goto leave; + err = ksba_cert_init_from_mem (cert, value, valuelen); + if(err) + goto leave; + xfree (value); value = NULL; + + err = crl_cache_reload_crl (ctrl, cert); + + leave: + ksba_cert_release (cert); + xfree (value); + return err; +} + + +/* Handle OPTION commands. */ +static gpg_error_t +option_handler (assuan_context_t ctx, const char *key, const char *value) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + + if (!strcmp (key, "force-crl-refresh")) + { + int i = *value? atoi (value) : 0; + ctrl->force_crl_refresh = i; + } + else if (!strcmp (key, "audit-events")) + { + int i = *value? atoi (value) : 0; + ctrl->audit_events = i; + } + else if (!strcmp (key, "http-proxy")) + { + xfree (ctrl->http_proxy); + if (!*value || !strcmp (value, "none")) + ctrl->http_proxy = NULL; + else if (!(ctrl->http_proxy = xtrystrdup (value))) + err = gpg_error_from_syserror (); + } + else if (!strcmp (key, "honor-keyserver-url-used")) + { + /* Return an error if we are running in Tor mode. */ + if (opt.use_tor) + err = gpg_error (GPG_ERR_FORBIDDEN); + } + else + err = gpg_error (GPG_ERR_UNKNOWN_OPTION); + + return err; +} + + + +static const char hlp_dns_cert[] = + "DNS_CERT \n" + "DNS_CERT --pka \n" + "DNS_CERT --dane \n" + "\n" + "Return the CERT record for . is one of\n" + " * Return the first record of any supported subtype\n" + " PGP Return the first record of subtype PGP (3)\n" + " IPGP Return the first record of subtype IPGP (6)\n" + "If the content of a certificate is available (PGP) it is returned\n" + "by data lines. Fingerprints and URLs are returned via status lines.\n" + "In --pka mode the fingerprint and if available an URL is returned.\n" + "In --dane mode the key is returned from RR type 61"; +static gpg_error_t +cmd_dns_cert (assuan_context_t ctx, char *line) +{ + /* ctrl_t ctrl = assuan_get_pointer (ctx); */ + gpg_error_t err = 0; + int pka_mode, dane_mode; + char *mbox = NULL; + char *namebuf = NULL; + char *encodedhash = NULL; + const char *name; + int certtype; + char *p; + void *key = NULL; + size_t keylen; + unsigned char *fpr = NULL; + size_t fprlen; + char *url = NULL; + + pka_mode = has_option (line, "--pka"); + dane_mode = has_option (line, "--dane"); + line = skip_options (line); + + if (pka_mode && dane_mode) + { + err = PARM_ERROR ("either --pka or --dane may be given"); + goto leave; + } + + if (pka_mode || dane_mode) + ; /* No need to parse here - we do this later. */ + else + { + p = strchr (line, ' '); + if (!p) + { + err = PARM_ERROR ("missing arguments"); + goto leave; + } + *p++ = 0; + if (!strcmp (line, "*")) + certtype = DNS_CERTTYPE_ANY; + else if (!strcmp (line, "IPGP")) + certtype = DNS_CERTTYPE_IPGP; + else if (!strcmp (line, "PGP")) + certtype = DNS_CERTTYPE_PGP; + else + { + err = PARM_ERROR ("unknown subtype"); + goto leave; + } + while (spacep (p)) + p++; + line = p; + if (!*line) + { + err = PARM_ERROR ("name missing"); + goto leave; + } + } + + if (opt.use_tor && (err = enable_dns_tormode (0))) + { + /* Tor mode is requested but the DNS code can't enable it. */ + assuan_set_error (ctx, err, "error enabling Tor mode"); + goto leave; + } + + if (pka_mode || dane_mode) + { + char *domain; /* Points to mbox. */ + char hashbuf[32]; /* For SHA-1 and SHA-256. */ + + /* We lowercase ascii characters but the DANE I-D does not allow + this. FIXME: Check after the release of the RFC whether to + change this. */ + mbox = mailbox_from_userid (line); + if (!mbox || !(domain = strchr (mbox, '@'))) + { + err = set_error (GPG_ERR_INV_USER_ID, "no mailbox in user id"); + goto leave; + } + *domain++ = 0; + + if (pka_mode) + { + gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); + encodedhash = zb32_encode (hashbuf, 8*20); + if (!encodedhash) + { + err = gpg_error_from_syserror (); + goto leave; + } + namebuf = strconcat (encodedhash, "._pka.", domain, NULL); + if (!namebuf) + { + err = gpg_error_from_syserror (); + goto leave; + } + name = namebuf; + certtype = DNS_CERTTYPE_IPGP; + } + else + { + /* Note: The hash is truncated to 28 bytes and we lowercase + the result only for aesthetic reasons. */ + gcry_md_hash_buffer (GCRY_MD_SHA256, hashbuf, mbox, strlen (mbox)); + encodedhash = bin2hex (hashbuf, 28, NULL); + if (!encodedhash) + { + err = gpg_error_from_syserror (); + goto leave; + } + ascii_strlwr (encodedhash); + namebuf = strconcat (encodedhash, "._openpgpkey.", domain, NULL); + if (!namebuf) + { + err = gpg_error_from_syserror (); + goto leave; + } + name = namebuf; + certtype = DNS_CERTTYPE_RR61; + } + } + else + name = line; + + err = get_dns_cert (name, certtype, &key, &keylen, &fpr, &fprlen, &url); + if (err) + goto leave; + + if (key) + { + err = data_line_write (ctx, key, keylen); + if (err) + goto leave; + } + + if (fpr) + { + char *tmpstr; + + tmpstr = bin2hex (fpr, fprlen, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + err = assuan_write_status (ctx, "FPR", tmpstr); + xfree (tmpstr); + } + if (err) + goto leave; + } + + if (url) + { + err = assuan_write_status (ctx, "URL", url); + if (err) + goto leave; + } + + + leave: + xfree (key); + xfree (fpr); + xfree (url); + xfree (mbox); + xfree (namebuf); + xfree (encodedhash); + return leave_cmd (ctx, err); +} + + + +static const char hlp_wkd_get[] = + "WKD_GET [--submission-address|--policy-flags] \n" + "\n" + "Return the key or other info for \n" + "from the Web Key Directory."; +static gpg_error_t +cmd_wkd_get (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + char *mbox = NULL; + char *domain; /* Points to mbox. */ + char sha1buf[20]; + char *uri = NULL; + char *encodedhash = NULL; + int opt_submission_addr; + int opt_policy_flags; + int no_log = 0; + + opt_submission_addr = has_option (line, "--submission-address"); + opt_policy_flags = has_option (line, "--policy-flags"); + line = skip_options (line); + + mbox = mailbox_from_userid (line); + if (!mbox || !(domain = strchr (mbox, '@'))) + { + err = set_error (GPG_ERR_INV_USER_ID, "no mailbox in user id"); + goto leave; + } + *domain++ = 0; + + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, mbox, strlen (mbox)); + encodedhash = zb32_encode (sha1buf, 8*20); + if (!encodedhash) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (opt_submission_addr) + { + uri = strconcat ("https://", + domain, + "/.well-known/openpgpkey/submission-address", + NULL); + } + else if (opt_policy_flags) + { + uri = strconcat ("https://", + domain, + "/.well-known/openpgpkey/policy", + NULL); + } + else + { + uri = strconcat ("https://", + domain, + "/.well-known/openpgpkey/hu/", + encodedhash, + NULL); + no_log = 1; + } + if (!uri) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Setup an output stream and perform the get. */ + { + estream_t outfp; + + outfp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!outfp) + err = set_error (GPG_ERR_ASS_GENERAL, + "error setting up a data stream"); + else + { + if (no_log) + ctrl->server_local->inhibit_data_logging = 1; + ctrl->server_local->inhibit_data_logging_now = 0; + ctrl->server_local->inhibit_data_logging_count = 0; + err = ks_action_fetch (ctrl, uri, outfp); + es_fclose (outfp); + ctrl->server_local->inhibit_data_logging = 0; + } + } + + leave: + xfree (uri); + xfree (encodedhash); + xfree (mbox); + return leave_cmd (ctx, err); +} + + + +static const char hlp_ldapserver[] = + "LDAPSERVER \n" + "\n" + "Add a new LDAP server to the list of configured LDAP servers.\n" + "DATA is in the same format as expected in the configure file."; +static gpg_error_t +cmd_ldapserver (assuan_context_t ctx, char *line) +{ +#if USE_LDAP + ctrl_t ctrl = assuan_get_pointer (ctx); + ldap_server_t server; + ldap_server_t *last_next_p; + + while (spacep (line)) + line++; + if (*line == '\0') + return leave_cmd (ctx, PARM_ERROR (_("ldapserver missing"))); + + server = ldapserver_parse_one (line, "", 0); + if (! server) + return leave_cmd (ctx, gpg_error (GPG_ERR_INV_ARG)); + + last_next_p = &ctrl->server_local->ldapservers; + while (*last_next_p) + last_next_p = &(*last_next_p)->next; + *last_next_p = server; + return leave_cmd (ctx, 0); +#else + (void)line; + return leave_cmd (ctx, gpg_error (GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + + +static const char hlp_isvalid[] = + "ISVALID [--only-ocsp] [--force-default-responder]" + " |\n" + "\n" + "This command checks whether the certificate identified by the\n" + "certificate_id is valid. This is done by consulting CRLs or\n" + "whatever has been configured. Note, that the returned error codes\n" + "are from gpg-error.h. The command may callback using the inquire\n" + "function. See the manual for details.\n" + "\n" + "The CERTIFICATE_ID is a hex encoded string consisting of two parts,\n" + "delimited by a single dot. The first part is the SHA-1 hash of the\n" + "issuer name and the second part the serial number.\n" + "\n" + "Alternatively the certificate's fingerprint may be given in which\n" + "case an OCSP request is done before consulting the CRL.\n" + "\n" + "If the option --only-ocsp is given, no fallback to a CRL check will\n" + "be used.\n" + "\n" + "If the option --force-default-responder is given, only the default\n" + "OCSP responder will be used and any other methods of obtaining an\n" + "OCSP responder URL won't be used."; +static gpg_error_t +cmd_isvalid (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + char *issuerhash, *serialno; + gpg_error_t err; + int did_inquire = 0; + int ocsp_mode = 0; + int only_ocsp; + int force_default_responder; + + only_ocsp = has_option (line, "--only-ocsp"); + force_default_responder = has_option (line, "--force-default-responder"); + line = skip_options (line); + + issuerhash = xstrdup (line); /* We need to work on a copy of the + line because that same Assuan + context may be used for an inquiry. + That is because Assuan reuses its + line buffer. + */ + + serialno = strchr (issuerhash, '.'); + if (serialno) + *serialno++ = 0; + else + { + char *endp = strchr (issuerhash, ' '); + if (endp) + *endp = 0; + if (strlen (issuerhash) != 40) + { + xfree (issuerhash); + return leave_cmd (ctx, PARM_ERROR (_("serialno missing in cert ID"))); + } + ocsp_mode = 1; + } + + + again: + if (ocsp_mode) + { + /* Note, that we ignore the given issuer hash and instead rely + on the current certificate semantics used with this + command. */ + if (!opt.allow_ocsp) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + err = ocsp_isvalid (ctrl, NULL, NULL, force_default_responder); + /* Fixme: If we got no ocsp response and --only-ocsp is not used + we should fall back to CRL mode. Thus we need to clear + OCSP_MODE, get the issuerhash and the serialno from the + current certificate and jump to again. */ + } + else if (only_ocsp) + err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else + { + switch (crl_cache_isvalid (ctrl, + issuerhash, serialno, + ctrl->force_crl_refresh)) + { + case CRL_CACHE_VALID: + err = 0; + break; + case CRL_CACHE_INVALID: + err = gpg_error (GPG_ERR_CERT_REVOKED); + break; + case CRL_CACHE_DONTKNOW: + if (did_inquire) + err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (!(err = inquire_cert_and_load_crl (ctx))) + { + did_inquire = 1; + goto again; + } + break; + case CRL_CACHE_CANTUSE: + err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + break; + default: + log_fatal ("crl_cache_isvalid returned invalid code\n"); + } + } + + xfree (issuerhash); + return leave_cmd (ctx, err); +} + + +/* If the line contains a SHA-1 fingerprint as the first argument, + return the FPR vuffer on success. The function checks that the + fingerprint consists of valid characters and prints and error + message if it does not and returns NULL. Fingerprints are + considered optional and thus no explicit error is returned. NULL is + also returned if there is no fingerprint at all available. + FPR must be a caller provided buffer of at least 20 bytes. + + Note that colons within the fingerprint are allowed to separate 2 + hex digits; this allows for easier cutting and pasting using the + usual fingerprint rendering. +*/ +static unsigned char * +get_fingerprint_from_line (const char *line, unsigned char *fpr) +{ + const char *s; + int i; + + for (s=line, i=0; *s && *s != ' '; s++ ) + { + if ( hexdigitp (s) && hexdigitp (s+1) ) + { + if ( i >= 20 ) + return NULL; /* Fingerprint too long. */ + fpr[i++] = xtoi_2 (s); + s++; + } + else if ( *s != ':' ) + return NULL; /* Invalid. */ + } + if ( i != 20 ) + return NULL; /* Fingerprint to short. */ + return fpr; +} + + + +static const char hlp_checkcrl[] = + "CHECKCRL []\n" + "\n" + "Check whether the certificate with FINGERPRINT (SHA-1 hash of the\n" + "entire X.509 certificate blob) is valid or not by consulting the\n" + "CRL responsible for this certificate. If the fingerprint has not\n" + "been given or the certificate is not known, the function \n" + "inquires the certificate using an\n" + "\n" + " INQUIRE TARGETCERT\n" + "\n" + "and the caller is expected to return the certificate for the\n" + "request (which should match FINGERPRINT) as a binary blob.\n" + "Processing then takes place without further interaction; in\n" + "particular dirmngr tries to locate other required certificate by\n" + "its own mechanism which includes a local certificate store as well\n" + "as a list of trusted root certificates.\n" + "\n" + "The return value is the usual gpg-error code or 0 for ducesss;\n" + "i.e. the certificate validity has been confirmed by a valid CRL."; +static gpg_error_t +cmd_checkcrl (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char fprbuffer[20], *fpr; + ksba_cert_t cert; + + fpr = get_fingerprint_from_line (line, fprbuffer); + cert = fpr? get_cert_byfpr (fpr) : NULL; + + if (!cert) + { + /* We do not have this certificate yet or the fingerprint has + not been given. Inquire it from the client. */ + unsigned char *value = NULL; + size_t valuelen; + + err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", + &value, &valuelen, MAX_CERT_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + if (!valuelen) /* No data returned; return a comprehensible error. */ + err = gpg_error (GPG_ERR_MISSING_CERT); + else + { + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, value, valuelen); + } + xfree (value); + if(err) + goto leave; + } + + assert (cert); + + err = crl_cache_cert_isvalid (ctrl, cert, ctrl->force_crl_refresh); + if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN) + { + err = crl_cache_reload_crl (ctrl, cert); + if (!err) + err = crl_cache_cert_isvalid (ctrl, cert, 0); + } + + leave: + ksba_cert_release (cert); + return leave_cmd (ctx, err); +} + + +static const char hlp_checkocsp[] = + "CHECKOCSP [--force-default-responder] []\n" + "\n" + "Check whether the certificate with FINGERPRINT (SHA-1 hash of the\n" + "entire X.509 certificate blob) is valid or not by asking an OCSP\n" + "responder responsible for this certificate. The optional\n" + "fingerprint may be used for a quick check in case an OCSP check has\n" + "been done for this certificate recently (we always cache OCSP\n" + "responses for a couple of minutes). If the fingerprint has not been\n" + "given or there is no cached result, the function inquires the\n" + "certificate using an\n" + "\n" + " INQUIRE TARGETCERT\n" + "\n" + "and the caller is expected to return the certificate for the\n" + "request (which should match FINGERPRINT) as a binary blob.\n" + "Processing then takes place without further interaction; in\n" + "particular dirmngr tries to locate other required certificates by\n" + "its own mechanism which includes a local certificate store as well\n" + "as a list of trusted root certificates.\n" + "\n" + "If the option --force-default-responder is given, only the default\n" + "OCSP responder will be used and any other methods of obtaining an\n" + "OCSP responder URL won't be used.\n" + "\n" + "The return value is the usual gpg-error code or 0 for ducesss;\n" + "i.e. the certificate validity has been confirmed by a valid CRL."; +static gpg_error_t +cmd_checkocsp (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char fprbuffer[20], *fpr; + ksba_cert_t cert; + int force_default_responder; + + force_default_responder = has_option (line, "--force-default-responder"); + line = skip_options (line); + + fpr = get_fingerprint_from_line (line, fprbuffer); + cert = fpr? get_cert_byfpr (fpr) : NULL; + + if (!cert) + { + /* We do not have this certificate yet or the fingerprint has + not been given. Inquire it from the client. */ + unsigned char *value = NULL; + size_t valuelen; + + err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", + &value, &valuelen, MAX_CERT_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + if (!valuelen) /* No data returned; return a comprehensible error. */ + err = gpg_error (GPG_ERR_MISSING_CERT); + else + { + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, value, valuelen); + } + xfree (value); + if(err) + goto leave; + } + + assert (cert); + + if (!opt.allow_ocsp) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + err = ocsp_isvalid (ctrl, cert, NULL, force_default_responder); + + leave: + ksba_cert_release (cert); + return leave_cmd (ctx, err); +} + + + +static int +lookup_cert_by_url (assuan_context_t ctx, const char *url) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + unsigned char *value = NULL; + size_t valuelen; + + /* Fetch single certificate given it's URL. */ + err = fetch_cert_by_url (ctrl, url, &value, &valuelen); + if (err) + { + log_error (_("fetch_cert_by_url failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Send the data, flush the buffer and then send an END. */ + err = assuan_send_data (ctx, value, valuelen); + if (!err) + err = assuan_send_data (ctx, NULL, 0); + if (!err) + err = assuan_write_line (ctx, "END"); + if (err) + { + log_error (_("error sending data: %s\n"), gpg_strerror (err)); + goto leave; + } + + leave: + + return err; +} + + +/* Send the certificate, flush the buffer and then send an END. */ +static gpg_error_t +return_one_cert (void *opaque, ksba_cert_t cert) +{ + assuan_context_t ctx = opaque; + gpg_error_t err; + const unsigned char *der; + size_t derlen; + + der = ksba_cert_get_image (cert, &derlen); + if (!der) + err = gpg_error (GPG_ERR_INV_CERT_OBJ); + else + { + err = assuan_send_data (ctx, der, derlen); + if (!err) + err = assuan_send_data (ctx, NULL, 0); + if (!err) + err = assuan_write_line (ctx, "END"); + } + if (err) + log_error (_("error sending data: %s\n"), gpg_strerror (err)); + return err; +} + + +/* Lookup certificates from the internal cache or using the ldap + servers. */ +static int +lookup_cert_by_pattern (assuan_context_t ctx, char *line, + int single, int cache_only) +{ + gpg_error_t err = 0; + char *p; + strlist_t sl, list = NULL; + int truncated = 0, truncation_forced = 0; + int count = 0; + int local_count = 0; +#if USE_LDAP + ctrl_t ctrl = assuan_get_pointer (ctx); + unsigned char *value = NULL; + size_t valuelen; + struct ldapserver_iter ldapserver_iter; + cert_fetch_context_t fetch_context; +#endif /*USE_LDAP*/ + int any_no_data = 0; + + /* Break the line down into an STRLIST */ + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + err = gpg_error_from_errno (errno); + goto leave; + } + memset (sl, 0, sizeof *sl); + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + /* First look through the internal cache. The certifcates returned + here are not counted towards the truncation limit. */ + if (single && !cache_only) + ; /* Do not read from the local cache in this case. */ + else + { + for (sl=list; sl; sl = sl->next) + { + err = get_certs_bypattern (sl->d, return_one_cert, ctx); + if (!err) + local_count++; + if (!err && single) + goto ready; + + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + { + err = 0; + if (cache_only) + any_no_data = 1; + } + else if (gpg_err_code (err) == GPG_ERR_INV_NAME && !cache_only) + { + /* No real fault because the internal pattern lookup + can't yet cope with all types of pattern. */ + err = 0; + } + if (err) + goto ready; + } + } + + /* Loop over all configured servers unless we want only the + certificates from the cache. */ +#if USE_LDAP + for (ldapserver_iter_begin (&ldapserver_iter, ctrl); + !cache_only && !ldapserver_iter_end_p (&ldapserver_iter) + && ldapserver_iter.server->host && !truncation_forced; + ldapserver_iter_next (&ldapserver_iter)) + { + ldap_server_t ldapserver = ldapserver_iter.server; + + if (DBG_LOOKUP) + log_debug ("cmd_lookup: trying %s:%d base=%s\n", + ldapserver->host, ldapserver->port, + ldapserver->base?ldapserver->base : "[default]"); + + /* Fetch certificates matching pattern */ + err = start_cert_fetch (ctrl, &fetch_context, list, ldapserver); + if ( gpg_err_code (err) == GPG_ERR_NO_DATA ) + { + if (DBG_LOOKUP) + log_debug ("cmd_lookup: no data\n"); + err = 0; + any_no_data = 1; + continue; + } + if (err) + { + log_error (_("start_cert_fetch failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Fetch the certificates for this query. */ + while (!truncation_forced) + { + xfree (value); value = NULL; + err = fetch_next_cert (fetch_context, &value, &valuelen); + if (gpg_err_code (err) == GPG_ERR_NO_DATA ) + { + err = 0; + any_no_data = 1; + break; /* Ready. */ + } + if (gpg_err_code (err) == GPG_ERR_TRUNCATED) + { + truncated = 1; + err = 0; + break; /* Ready. */ + } + if (gpg_err_code (err) == GPG_ERR_EOF) + { + err = 0; + break; /* Ready. */ + } + if (!err && !value) + { + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + if (err) + { + log_error (_("fetch_next_cert failed: %s\n"), + gpg_strerror (err)); + end_cert_fetch (fetch_context); + goto leave; + } + + if (DBG_LOOKUP) + log_debug ("cmd_lookup: returning one cert%s\n", + truncated? " (truncated)":""); + + /* Send the data, flush the buffer and then send an END line + as a certificate delimiter. */ + err = assuan_send_data (ctx, value, valuelen); + if (!err) + err = assuan_send_data (ctx, NULL, 0); + if (!err) + err = assuan_write_line (ctx, "END"); + if (err) + { + log_error (_("error sending data: %s\n"), gpg_strerror (err)); + end_cert_fetch (fetch_context); + goto leave; + } + + if (++count >= opt.max_replies ) + { + truncation_forced = 1; + log_info (_("max_replies %d exceeded\n"), opt.max_replies ); + } + if (single) + break; + } + + end_cert_fetch (fetch_context); + } +#endif /*USE_LDAP*/ + + ready: + if (truncated || truncation_forced) + { + char str[50]; + + sprintf (str, "%d", count); + assuan_write_status (ctx, "TRUNCATED", str); + } + + if (!err && !count && !local_count && any_no_data) + err = gpg_error (GPG_ERR_NO_DATA); + + leave: + free_strlist (list); + return err; +} + + +static const char hlp_lookup[] = + "LOOKUP [--url] [--single] [--cache-only] \n" + "\n" + "Lookup certificates matching PATTERN. With --url the pattern is\n" + "expected to be one URL.\n" + "\n" + "If --url is not given: To allow for multiple patterns (which are ORed)\n" + "quoting is required: Spaces are translated to \"+\" or \"%20\";\n" + "obviously this requires that the usual escape quoting rules are applied.\n" + "\n" + "If --url is given no special escaping is required because URLs are\n" + "already escaped this way.\n" + "\n" + "If --single is given the first and only the first match will be\n" + "returned. If --cache-only is _not_ given, no local query will be\n" + "done.\n" + "\n" + "If --cache-only is given no external lookup is done so that only\n" + "certificates from the cache may get returned."; +static gpg_error_t +cmd_lookup (assuan_context_t ctx, char *line) +{ + gpg_error_t err; + int lookup_url, single, cache_only; + + lookup_url = has_leading_option (line, "--url"); + single = has_leading_option (line, "--single"); + cache_only = has_leading_option (line, "--cache-only"); + line = skip_options (line); + + if (lookup_url && cache_only) + err = gpg_error (GPG_ERR_NOT_FOUND); + else if (lookup_url && single) + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else if (lookup_url) + err = lookup_cert_by_url (ctx, line); + else + err = lookup_cert_by_pattern (ctx, line, single, cache_only); + + return leave_cmd (ctx, err); +} + + +static const char hlp_loadcrl[] = + "LOADCRL [--url] \n" + "\n" + "Load the CRL in the file with name FILENAME into our cache. Note\n" + "that FILENAME should be given with an absolute path because\n" + "Dirmngrs cwd is not known. With --url the CRL is directly loaded\n" + "from the given URL.\n" + "\n" + "This command is usually used by gpgsm using the invocation \"gpgsm\n" + "--call-dirmngr loadcrl \". A direct invocation of Dirmngr\n" + "is not useful because gpgsm might need to callback gpgsm to ask for\n" + "the CA's certificate."; +static gpg_error_t +cmd_loadcrl (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + int use_url = has_leading_option (line, "--url"); + + line = skip_options (line); + + if (use_url) + { + ksba_reader_t reader; + + err = crl_fetch (ctrl, line, &reader); + if (err) + log_error (_("fetching CRL from '%s' failed: %s\n"), + line, gpg_strerror (err)); + else + { + err = crl_cache_insert (ctrl, line, reader); + if (err) + log_error (_("processing CRL from '%s' failed: %s\n"), + line, gpg_strerror (err)); + crl_close_reader (reader); + } + } + else + { + char *buf; + + buf = xtrymalloc (strlen (line)+1); + if (!buf) + err = gpg_error_from_syserror (); + else + { + strcpy_escaped_plus (buf, line); + err = crl_cache_load (ctrl, buf); + xfree (buf); + } + } + + return leave_cmd (ctx, err); +} + + +static const char hlp_listcrls[] = + "LISTCRLS\n" + "\n" + "List the content of all CRLs in a readable format. This command is\n" + "usually used by gpgsm using the invocation \"gpgsm --call-dirmngr\n" + "listcrls\". It may also be used directly using \"dirmngr\n" + "--list-crls\"."; +static gpg_error_t +cmd_listcrls (assuan_context_t ctx, char *line) +{ + gpg_error_t err; + estream_t fp; + + (void)line; + + fp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!fp) + err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); + else + { + err = crl_cache_list (fp); + es_fclose (fp); + } + return leave_cmd (ctx, err); +} + + +static const char hlp_cachecert[] = + "CACHECERT\n" + "\n" + "Put a certificate into the internal cache. This command might be\n" + "useful if a client knows in advance certificates required for a\n" + "test and wants to make sure they get added to the internal cache.\n" + "It is also helpful for debugging. To get the actual certificate,\n" + "this command immediately inquires it using\n" + "\n" + " INQUIRE TARGETCERT\n" + "\n" + "and the caller is expected to return the certificate for the\n" + "request as a binary blob."; +static gpg_error_t +cmd_cachecert (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + ksba_cert_t cert = NULL; + unsigned char *value = NULL; + size_t valuelen; + + (void)line; + + err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", + &value, &valuelen, MAX_CERT_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + if (!valuelen) /* No data returned; return a comprehensible error. */ + err = gpg_error (GPG_ERR_MISSING_CERT); + else + { + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, value, valuelen); + } + xfree (value); + if(err) + goto leave; + + err = cache_cert (cert); + + leave: + ksba_cert_release (cert); + return leave_cmd (ctx, err); +} + + +static const char hlp_validate[] = + "VALIDATE\n" + "\n" + "Validate a certificate using the certificate validation function\n" + "used internally by dirmngr. This command is only useful for\n" + "debugging. To get the actual certificate, this command immediately\n" + "inquires it using\n" + "\n" + " INQUIRE TARGETCERT\n" + "\n" + "and the caller is expected to return the certificate for the\n" + "request as a binary blob."; +static gpg_error_t +cmd_validate (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + ksba_cert_t cert = NULL; + unsigned char *value = NULL; + size_t valuelen; + + (void)line; + + err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", + &value, &valuelen, MAX_CERT_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + if (!valuelen) /* No data returned; return a comprehensible error. */ + err = gpg_error (GPG_ERR_MISSING_CERT); + else + { + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, value, valuelen); + } + xfree (value); + if(err) + goto leave; + + /* If we have this certificate already in our cache, use the cached + version for validation because this will take care of any cached + results. */ + { + unsigned char fpr[20]; + ksba_cert_t tmpcert; + + cert_compute_fpr (cert, fpr); + tmpcert = get_cert_byfpr (fpr); + if (tmpcert) + { + ksba_cert_release (cert); + cert = tmpcert; + } + } + + err = validate_cert_chain (ctrl, cert, NULL, VALIDATE_MODE_CERT, NULL); + + leave: + ksba_cert_release (cert); + return leave_cmd (ctx, err); +} + + + +/* Parse an keyserver URI and store it in a new uri item which is + returned at R_ITEM. On error return an error code. */ +static gpg_error_t +make_keyserver_item (const char *uri, uri_item_t *r_item) +{ + gpg_error_t err; + uri_item_t item; + + *r_item = NULL; + item = xtrymalloc (sizeof *item + strlen (uri)); + if (!item) + return gpg_error_from_syserror (); + + item->next = NULL; + item->parsed_uri = NULL; + strcpy (item->uri, uri); + +#if USE_LDAP + if (ldap_uri_p (item->uri)) + err = ldap_parse_uri (&item->parsed_uri, uri); + else +#endif + { + err = http_parse_uri (&item->parsed_uri, uri, 1); + } + + if (err) + xfree (item); + else + *r_item = item; + return err; +} + + +/* If no keyserver is stored in CTRL but a global keyserver has been + set, put that global keyserver into CTRL. We need use this + function to help migrate from the old gpg based keyserver + configuration to the new dirmngr based configuration. */ +static gpg_error_t +ensure_keyserver (ctrl_t ctrl) +{ + gpg_error_t err; + uri_item_t item; + uri_item_t onion_items = NULL; + uri_item_t plain_items = NULL; + uri_item_t ui; + strlist_t sl; + + if (ctrl->server_local->keyservers) + return 0; /* Already set for this session. */ + if (!opt.keyserver) + { + /* No global option set. Fall back to default: */ + return make_keyserver_item (DIRMNGR_DEFAULT_KEYSERVER, + &ctrl->server_local->keyservers); + } + + for (sl = opt.keyserver; sl; sl = sl->next) + { + err = make_keyserver_item (sl->d, &item); + if (err) + goto leave; + if (item->parsed_uri->onion) + { + item->next = onion_items; + onion_items = item; + } + else + { + item->next = plain_items; + plain_items = item; + } + } + + /* Decide which to use. Note that the sesssion has no keyservers + yet set. */ + if (onion_items && !onion_items->next && plain_items && !plain_items->next) + { + /* If there is just one onion and one plain keyserver given, we take + only one depending on whether Tor is running or not. */ + if (is_tor_running (ctrl)) + { + ctrl->server_local->keyservers = onion_items; + onion_items = NULL; + } + else + { + ctrl->server_local->keyservers = plain_items; + plain_items = NULL; + } + } + else if (!is_tor_running (ctrl)) + { + /* Tor is not running. It does not make sense to add Onion + addresses. */ + ctrl->server_local->keyservers = plain_items; + plain_items = NULL; + } + else + { + /* In all other cases add all keyservers. */ + ctrl->server_local->keyservers = onion_items; + onion_items = NULL; + for (ui = ctrl->server_local->keyservers; ui && ui->next; ui = ui->next) + ; + if (ui) + ui->next = plain_items; + else + ctrl->server_local->keyservers = plain_items; + plain_items = NULL; + } + + leave: + release_uri_item_list (onion_items); + release_uri_item_list (plain_items); + + return err; +} + + +static const char hlp_keyserver[] = + "KEYSERVER [] [|]\n" + "Options are:\n" + " --help\n" + " --clear Remove all configured keyservers\n" + " --resolve Resolve HKP host names and rotate\n" + " --hosttable Print table of known hosts and pools\n" + " --dead Mark as dead\n" + " --alive Mark as alive\n" + "\n" + "If called without arguments list all configured keyserver URLs.\n" + "If called with an URI add this as keyserver. Note that keyservers\n" + "are configured on a per-session base. A default keyserver may already be\n" + "present, thus the \"--clear\" option must be used to get full control.\n" + "If \"--clear\" and an URI are used together the clear command is\n" + "obviously executed first. A RESET command does not change the list\n" + "of configured keyservers."; +static gpg_error_t +cmd_keyserver (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + int clear_flag, add_flag, help_flag, host_flag, resolve_flag; + int dead_flag, alive_flag; + uri_item_t item = NULL; /* gcc 4.4.5 is not able to detect that it + is always initialized. */ + + clear_flag = has_option (line, "--clear"); + help_flag = has_option (line, "--help"); + resolve_flag = has_option (line, "--resolve"); + host_flag = has_option (line, "--hosttable"); + dead_flag = has_option (line, "--dead"); + alive_flag = has_option (line, "--alive"); + line = skip_options (line); + add_flag = !!*line; + + if (help_flag) + { + err = ks_action_help (ctrl, line); + goto leave; + } + + if (resolve_flag) + { + err = ensure_keyserver (ctrl); + if (!err) + err = ks_action_resolve (ctrl, ctrl->server_local->keyservers); + if (err) + goto leave; + } + + if (alive_flag && dead_flag) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "no support for zombies"); + goto leave; + } + if (dead_flag) + { + err = check_owner_permission (ctx, "no permission to use --dead"); + if (err) + goto leave; + } + if (alive_flag || dead_flag) + { + if (!*line) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "name of host missing"); + goto leave; + } + + err = ks_hkp_mark_host (ctrl, line, alive_flag); + if (err) + goto leave; + } + + if (host_flag) + { + err = ks_hkp_print_hosttable (ctrl); + if (err) + goto leave; + } + if (resolve_flag || host_flag || alive_flag || dead_flag) + goto leave; + + if (add_flag) + { + err = make_keyserver_item (line, &item); + if (err) + goto leave; + } + if (clear_flag) + release_ctrl_keyservers (ctrl); + if (add_flag) + { + item->next = ctrl->server_local->keyservers; + ctrl->server_local->keyservers = item; + } + + if (!add_flag && !clear_flag && !help_flag) + { + /* List configured keyservers. However, we first add a global + keyserver. */ + uri_item_t u; + + err = ensure_keyserver (ctrl); + if (err) + { + assuan_set_error (ctx, err, + "Bad keyserver configuration in dirmngr.conf"); + goto leave; + } + + for (u=ctrl->server_local->keyservers; u; u = u->next) + dirmngr_status (ctrl, "KEYSERVER", u->uri, NULL); + } + err = 0; + + leave: + return leave_cmd (ctx, err); +} + + + +static const char hlp_ks_search[] = + "KS_SEARCH {}\n" + "\n" + "Search the configured OpenPGP keyservers (see command KEYSERVER)\n" + "for keys matching PATTERN"; +static gpg_error_t +cmd_ks_search (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + strlist_t list, sl; + char *p; + estream_t outfp; + + /* No options for now. */ + line = skip_options (line); + + /* Break the line down into an strlist. Each pattern is + percent-plus escaped. */ + list = NULL; + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + err = gpg_error_from_syserror (); + goto leave; + } + sl->flags = 0; + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + err = ensure_keyserver (ctrl); + if (err) + goto leave; + + /* Setup an output stream and perform the search. */ + outfp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!outfp) + err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); + else + { + err = ks_action_search (ctrl, ctrl->server_local->keyservers, + list, outfp); + es_fclose (outfp); + } + + leave: + free_strlist (list); + return leave_cmd (ctx, err); +} + + + +static const char hlp_ks_get[] = + "KS_GET {}\n" + "\n" + "Get the keys matching PATTERN from the configured OpenPGP keyservers\n" + "(see command KEYSERVER). Each pattern should be a keyid, a fingerprint,\n" + "or an exact name indicated by the '=' prefix."; +static gpg_error_t +cmd_ks_get (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + strlist_t list, sl; + char *p; + estream_t outfp; + + /* No options for now. */ + line = skip_options (line); + + /* Break the line into a strlist. Each pattern is by + definition percent-plus escaped. However we only support keyids + and fingerprints and thus the client has no need to apply the + escaping. */ + list = NULL; + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + err = gpg_error_from_syserror (); + goto leave; + } + sl->flags = 0; + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + err = ensure_keyserver (ctrl); + if (err) + goto leave; + + /* Setup an output stream and perform the get. */ + outfp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!outfp) + err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); + else + { + ctrl->server_local->inhibit_data_logging = 1; + ctrl->server_local->inhibit_data_logging_now = 0; + ctrl->server_local->inhibit_data_logging_count = 0; + err = ks_action_get (ctrl, ctrl->server_local->keyservers, list, outfp); + es_fclose (outfp); + ctrl->server_local->inhibit_data_logging = 0; + } + + leave: + free_strlist (list); + return leave_cmd (ctx, err); +} + + +static const char hlp_ks_fetch[] = + "KS_FETCH \n" + "\n" + "Get the key(s) from URL."; +static gpg_error_t +cmd_ks_fetch (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + estream_t outfp; + + /* No options for now. */ + line = skip_options (line); + + err = ensure_keyserver (ctrl); /* FIXME: Why do we needs this here? */ + if (err) + goto leave; + + /* Setup an output stream and perform the get. */ + outfp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!outfp) + err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); + else + { + ctrl->server_local->inhibit_data_logging = 1; + ctrl->server_local->inhibit_data_logging_now = 0; + ctrl->server_local->inhibit_data_logging_count = 0; + err = ks_action_fetch (ctrl, line, outfp); + es_fclose (outfp); + ctrl->server_local->inhibit_data_logging = 0; + } + + leave: + return leave_cmd (ctx, err); +} + + + +static const char hlp_ks_put[] = + "KS_PUT\n" + "\n" + "Send a key to the configured OpenPGP keyservers. The actual key material\n" + "is then requested by Dirmngr using\n" + "\n" + " INQUIRE KEYBLOCK\n" + "\n" + "The client shall respond with a binary version of the keyblock (e.g.,\n" + "the output of `gpg --export KEYID'). For LDAP\n" + "keyservers Dirmngr may ask for meta information of the provided keyblock\n" + "using:\n" + "\n" + " INQUIRE KEYBLOCK_INFO\n" + "\n" + "The client shall respond with a colon delimited info lines (the output\n" + "of 'for x in keys sigs; do gpg --list-$x --with-colons KEYID; done').\n"; +static gpg_error_t +cmd_ks_put (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char *value = NULL; + size_t valuelen; + unsigned char *info = NULL; + size_t infolen; + + /* No options for now. */ + line = skip_options (line); + + err = ensure_keyserver (ctrl); + if (err) + goto leave; + + /* Ask for the key material. */ + err = assuan_inquire (ctx, "KEYBLOCK", + &value, &valuelen, MAX_KEYBLOCK_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + if (!valuelen) /* No data returned; return a comprehensible error. */ + { + err = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + /* Ask for the key meta data. Not actually needed for HKP servers + but we do it anyway to test the client implementaion. */ + err = assuan_inquire (ctx, "KEYBLOCK_INFO", + &info, &infolen, MAX_KEYBLOCK_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Send the key. */ + err = ks_action_put (ctrl, ctrl->server_local->keyservers, + value, valuelen, info, infolen); + + leave: + xfree (info); + xfree (value); + return leave_cmd (ctx, err); +} + + + +static const char hlp_loadswdb[] = + "LOADSWDB [--force]\n" + "\n" + "Load and verify the swdb.lst from the Net."; +static gpg_error_t +cmd_loadswdb (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + + err = dirmngr_load_swdb (ctrl, has_option (line, "--force")); + + return leave_cmd (ctx, err); +} + + + +static const char hlp_getinfo[] = + "GETINFO \n" + "\n" + "Multi purpose command to return certain information. \n" + "Supported values of WHAT are:\n" + "\n" + "version - Return the version of the program.\n" + "pid - Return the process id of the server.\n" + "tor - Return OK if running in Tor mode\n" + "dnsinfo - Return info about the DNS resolver\n" + "socket_name - Return the name of the socket.\n"; +static gpg_error_t +cmd_getinfo (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + + if (!strcmp (line, "version")) + { + const char *s = VERSION; + err = assuan_send_data (ctx, s, strlen (s)); + } + else if (!strcmp (line, "pid")) + { + char numbuf[50]; + + snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ()); + err = assuan_send_data (ctx, numbuf, strlen (numbuf)); + } + else if (!strcmp (line, "socket_name")) + { + const char *s = dirmngr_get_current_socket_name (); + err = assuan_send_data (ctx, s, strlen (s)); + } + else if (!strcmp (line, "tor")) + { + if (opt.use_tor) + { + if (!is_tor_running (ctrl)) + err = assuan_write_status (ctx, "NO_TOR", "Tor not running"); + else + err = 0; + if (!err) + assuan_set_okay_line (ctx, "- Tor mode is enabled"); + } + else + err = set_error (GPG_ERR_FALSE, "Tor mode is NOT enabled"); + } + else if (!strcmp (line, "dnsinfo")) + { + if (standard_resolver_p ()) + assuan_set_okay_line + (ctx, "- Forced use of System resolver (w/o Tor support)"); + else + { +#ifdef USE_LIBDNS + assuan_set_okay_line (ctx, (recursive_resolver_p () + ? "- Libdns recursive resolver" + : "- Libdns stub resolver")); +#else + assuan_set_okay_line (ctx, "- System resolver (w/o Tor support)"); +#endif + } + err = 0; + } + else + err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); + + return leave_cmd (ctx, err); +} + + + +static const char hlp_killdirmngr[] = + "KILLDIRMNGR\n" + "\n" + "This command allows a user - given sufficient permissions -\n" + "to kill this dirmngr process.\n"; +static gpg_error_t +cmd_killdirmngr (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void)line; + + ctrl->server_local->stopme = 1; + assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1); + return gpg_error (GPG_ERR_EOF); +} + + +static const char hlp_reloaddirmngr[] = + "RELOADDIRMNGR\n" + "\n" + "This command is an alternative to SIGHUP\n" + "to reload the configuration."; +static gpg_error_t +cmd_reloaddirmngr (assuan_context_t ctx, char *line) +{ + (void)ctx; + (void)line; + + dirmngr_sighup_action (); + return 0; +} + + + +/* Tell the assuan library about our commands. */ +static int +register_commands (assuan_context_t ctx) +{ + static struct { + const char *name; + assuan_handler_t handler; + const char * const help; + } table[] = { + { "DNS_CERT", cmd_dns_cert, hlp_dns_cert }, + { "WKD_GET", cmd_wkd_get, hlp_wkd_get }, + { "LDAPSERVER", cmd_ldapserver, hlp_ldapserver }, + { "ISVALID", cmd_isvalid, hlp_isvalid }, + { "CHECKCRL", cmd_checkcrl, hlp_checkcrl }, + { "CHECKOCSP", cmd_checkocsp, hlp_checkocsp }, + { "LOOKUP", cmd_lookup, hlp_lookup }, + { "LOADCRL", cmd_loadcrl, hlp_loadcrl }, + { "LISTCRLS", cmd_listcrls, hlp_listcrls }, + { "CACHECERT", cmd_cachecert, hlp_cachecert }, + { "VALIDATE", cmd_validate, hlp_validate }, + { "KEYSERVER", cmd_keyserver, hlp_keyserver }, + { "KS_SEARCH", cmd_ks_search, hlp_ks_search }, + { "KS_GET", cmd_ks_get, hlp_ks_get }, + { "KS_FETCH", cmd_ks_fetch, hlp_ks_fetch }, + { "KS_PUT", cmd_ks_put, hlp_ks_put }, + { "GETINFO", cmd_getinfo, hlp_getinfo }, + { "LOADSWDB", cmd_loadswdb, hlp_loadswdb }, + { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr }, + { "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr }, + { NULL, NULL } + }; + int i, j, rc; + + for (i=j=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler, + table[i].help); + if (rc) + return rc; + } + return 0; +} + + +/* Note that we do not reset the list of configured keyservers. */ +static gpg_error_t +reset_notify (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + (void)line; + +#if USE_LDAP + ldapserver_list_free (ctrl->server_local->ldapservers); +#endif /*USE_LDAP*/ + ctrl->server_local->ldapservers = NULL; + return 0; +} + + +/* This function is called by our assuan log handler to test whether a + * log message shall really be printed. The function must return + * false to inhibit the logging of MSG. CAT gives the requested log + * category. MSG might be NULL. */ +int +dirmngr_assuan_log_monitor (assuan_context_t ctx, unsigned int cat, + const char *msg) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + (void)cat; + (void)msg; + + if (!ctrl || !ctrl->server_local) + return 1; /* Can't decide - allow logging. */ + + if (!ctrl->server_local->inhibit_data_logging) + return 1; /* Not requested - allow logging. */ + + /* Disallow logging if *_now is true. */ + return !ctrl->server_local->inhibit_data_logging_now; +} + + +/* Startup the server and run the main command loop. With FD = -1, + use stdin/stdout. */ +void +start_command_handler (assuan_fd_t fd) +{ + static const char hello[] = "Dirmngr " VERSION " at your service"; + static char *hello_line; + int rc; + assuan_context_t ctx; + ctrl_t ctrl; + + ctrl = xtrycalloc (1, sizeof *ctrl); + if (ctrl) + ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local); + if (!ctrl || !ctrl->server_local) + { + log_error (_("can't allocate control structure: %s\n"), + strerror (errno)); + xfree (ctrl); + return; + } + + dirmngr_init_default_ctrl (ctrl); + + rc = assuan_new (&ctx); + if (rc) + { + log_error (_("failed to allocate assuan context: %s\n"), + gpg_strerror (rc)); + dirmngr_exit (2); + } + + if (fd == ASSUAN_INVALID_FD) + { + assuan_fd_t filedes[2]; + + filedes[0] = assuan_fdopen (0); + filedes[1] = assuan_fdopen (1); + rc = assuan_init_pipe_server (ctx, filedes); + } + else + { + rc = assuan_init_socket_server (ctx, fd, ASSUAN_SOCKET_SERVER_ACCEPTED); + } + + if (rc) + { + assuan_release (ctx); + log_error (_("failed to initialize the server: %s\n"), + gpg_strerror(rc)); + dirmngr_exit (2); + } + + rc = register_commands (ctx); + if (rc) + { + log_error (_("failed to the register commands with Assuan: %s\n"), + gpg_strerror(rc)); + dirmngr_exit (2); + } + + + if (!hello_line) + { + hello_line = xtryasprintf + ("Home: %s\n" + "Config: %s\n" + "%s", + gnupg_homedir (), + opt.config_filename? opt.config_filename : "[none]", + hello); + } + + ctrl->server_local->assuan_ctx = ctx; + assuan_set_pointer (ctx, ctrl); + + assuan_set_hello_line (ctx, hello_line); + assuan_register_option_handler (ctx, option_handler); + assuan_register_reset_notify (ctx, reset_notify); + + for (;;) + { + rc = assuan_accept (ctx); + if (rc == -1) + break; + if (rc) + { + log_info (_("Assuan accept problem: %s\n"), gpg_strerror (rc)); + break; + } + +#ifndef HAVE_W32_SYSTEM + if (opt.verbose) + { + assuan_peercred_t peercred; + + if (!assuan_get_peercred (ctx, &peercred)) + log_info ("connection from process %ld (%ld:%ld)\n", + (long)peercred->pid, (long)peercred->uid, + (long)peercred->gid); + } +#endif + + rc = assuan_process (ctx); + if (rc) + { + log_info (_("Assuan processing failed: %s\n"), gpg_strerror (rc)); + continue; + } + } + + +#if USE_LDAP + ldap_wrapper_connection_cleanup (ctrl); + + ldapserver_list_free (ctrl->server_local->ldapservers); +#endif /*USE_LDAP*/ + ctrl->server_local->ldapservers = NULL; + + release_ctrl_keyservers (ctrl); + + ctrl->server_local->assuan_ctx = NULL; + assuan_release (ctx); + + if (ctrl->server_local->stopme) + dirmngr_exit (0); + + if (ctrl->refcount) + log_error ("oops: connection control structure still referenced (%d)\n", + ctrl->refcount); + else + { + release_ctrl_ocsp_certs (ctrl); + xfree (ctrl->server_local); + dirmngr_deinit_default_ctrl (ctrl); + xfree (ctrl); + } +} + + +/* Send a status line back to the client. KEYWORD is the status + keyword, the optional string arguments are blank separated added to + the line, the last argument must be a NULL. */ +gpg_error_t +dirmngr_status (ctrl_t ctrl, const char *keyword, ...) +{ + gpg_error_t err = 0; + va_list arg_ptr; + const char *text; + + va_start (arg_ptr, keyword); + + if (ctrl->server_local) + { + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + char buf[950], *p; + size_t n; + + p = buf; + n = 0; + while ( (text = va_arg (arg_ptr, const char *)) ) + { + if (n) + { + *p++ = ' '; + n++; + } + for ( ; *text && n < DIM (buf)-2; n++) + *p++ = *text++; + } + *p = 0; + err = assuan_write_status (ctx, keyword, buf); + } + + va_end (arg_ptr); + return err; +} + + +/* Print a help status line. TEXTLEN gives the length of the text + from TEXT to be printed. The function splits text at LFs. */ +gpg_error_t +dirmngr_status_help (ctrl_t ctrl, const char *text) +{ + gpg_error_t err = 0; + + if (ctrl->server_local) + { + assuan_context_t ctx = ctrl->server_local->assuan_ctx; + char buf[950], *p; + size_t n; + + do + { + p = buf; + n = 0; + for ( ; *text && *text != '\n' && n < DIM (buf)-2; n++) + *p++ = *text++; + if (*text == '\n') + text++; + *p = 0; + err = assuan_write_status (ctx, "#", buf); + } + while (!err && *text); + } + + return err; +} + +/* Send a tick progress indicator back. Fixme: This is only done for + the currently active channel. */ +gpg_error_t +dirmngr_tick (ctrl_t ctrl) +{ + static time_t next_tick = 0; + gpg_error_t err = 0; + time_t now = time (NULL); + + if (!next_tick) + { + next_tick = now + 1; + } + else if ( now > next_tick ) + { + if (ctrl) + { + err = dirmngr_status (ctrl, "PROGRESS", "tick", "? 0 0", NULL); + if (err) + { + /* Take this as in indication for a cancel request. */ + err = gpg_error (GPG_ERR_CANCELED); + } + now = time (NULL); + } + + next_tick = now + 1; + } + return err; +} diff --git a/dirmngr/sks-keyservers.netCA.pem b/dirmngr/sks-keyservers.netCA.pem new file mode 100644 index 0000000..24a2ad2 --- /dev/null +++ b/dirmngr/sks-keyservers.netCA.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFizCCA3OgAwIBAgIJAK9zyLTPn4CPMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV +BAYTAk5PMQ0wCwYDVQQIDARPc2xvMR4wHAYDVQQKDBVza3Mta2V5c2VydmVycy5u +ZXQgQ0ExHjAcBgNVBAMMFXNrcy1rZXlzZXJ2ZXJzLm5ldCBDQTAeFw0xMjEwMDkw +MDMzMzdaFw0yMjEwMDcwMDMzMzdaMFwxCzAJBgNVBAYTAk5PMQ0wCwYDVQQIDARP +c2xvMR4wHAYDVQQKDBVza3Mta2V5c2VydmVycy5uZXQgQ0ExHjAcBgNVBAMMFXNr +cy1rZXlzZXJ2ZXJzLm5ldCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBANdsWy4PXWNUCkS3L//nrd0GqN3dVwoBGZ6w94Tw2jPDPifegwxQozFXkG6I +6A4TK1CJLXPvfz0UP0aBYyPmTNadDinaB9T4jIwd4rnxl+59GiEmqkN3IfPsv5Jj +MkKUmJnvOT0DEVlEaO1UZIwx5WpfprB3mR81/qm4XkAgmYrmgnLXd/pJDAMk7y1F +45b5zWofiD5l677lplcIPRbFhpJ6kDTODXh/XEdtF71EAeaOdEGOvyGDmCO0GWqS +FDkMMPTlieLA/0rgFTcz4xwUYj/cD5e0ZBuSkYsYFAU3hd1cGfBue0cPZaQH2HYx +Qk4zXD8S3F4690fRhr+tki5gyG6JDR67aKp3BIGLqm7f45WkX1hYp+YXywmEziM4 +aSbGYhx8hoFGfq9UcfPEvp2aoc8u5sdqjDslhyUzM1v3m3ZGbhwEOnVjljY6JJLx +MxagxnZZSAY424ZZ3t71E/Mn27dm2w+xFRuoy8JEjv1d+BT3eChM5KaNwrj0IO/y +u8kFIgWYA1vZ/15qMT+tyJTfyrNVV/7Df7TNeWyNqjJ5rBmt0M6NpHG7CrUSkBy9 +p8JhimgjP5r0FlEkgg+lyD+V79H98gQfVgP3pbJICz0SpBQf2F/2tyS4rLm+49rP +fcOajiXEuyhpcmzgusAj/1FjrtlynH1r9mnNaX4e+rLWzvU5AgMBAAGjUDBOMB0G +A1UdDgQWBBTkwyoJFGfYTVISTpM8E+igjdq28zAfBgNVHSMEGDAWgBTkwyoJFGfY +TVISTpM8E+igjdq28zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQAR +OXnYwu3g1ZjHyley3fZI5aLPsaE17cOImVTehC8DcIphm2HOMR/hYTTL+V0G4P+u +gH+6xeRLKSHMHZTtSBIa6GDL03434y9CBuwGvAFCMU2GV8w92/Z7apkAhdLToZA/ +X/iWP2jeaVJhxgEcH8uPrnSlqoPBcKC9PrgUzQYfSZJkLmB+3jEa3HKruy1abJP5 +gAdQvwvcPpvYRnIzUc9fZODsVmlHVFBCl2dlu/iHh2h4GmL4Da2rRkUMlbVTdioB +UYIvMycdOkpH5wJftzw7cpjsudGas0PARDXCFfGyKhwBRFY7Xp7lbjtU5Rz0Gc04 +lPrhDf0pFE98Aw4jJRpFeWMjpXUEaG1cq7D641RpgcMfPFvOHY47rvDTS7XJOaUT +BwRjmDt896s6vMDcaG/uXJbQjuzmmx3W2Idyh3s5SI0GTHb0IwMKYb4eBUIpQOnB +cE77VnCYqKvN1NVYAqhWjXbY7XasZvszCRcOG+W3FqNaHOK/n/0ueb0uijdLan+U +f4p1bjbAox8eAOQS/8a3bzkJzdyBNUKGx1BIK2IBL9bn/HravSDOiNRSnZ/R3l9G +ZauX0tu7IIDlRCILXSyeazu0aj/vdT3YFQXPcvt5Fkf5wiNTo53f72/jYEJd6qph +WrpoKqrwGwTpRUCMhYIUt65hsTxCiJJ5nKe39h46sg== +-----END CERTIFICATE----- diff --git a/dirmngr/t-dns-stuff.c b/dirmngr/t-dns-stuff.c new file mode 100644 index 0000000..b087b5e --- /dev/null +++ b/dirmngr/t-dns-stuff.c @@ -0,0 +1,327 @@ +/* t-dns-cert.c - Module test for dns-stuff.c + * Copyright (C) 2011 Free Software Foundation, Inc. + * Copyright (C) 2011, 2015 Werner Koch + * + * 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 3 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, see . + */ + +#include +#include +#include +#include + + +#include "util.h" +#include "dns-stuff.h" + +#define PGM "t-dns-stuff" + +static int verbose; +static int debug; + + +static void +init_sockets (void) +{ +#ifdef HAVE_W32_SYSTEM + WSADATA wsadat; + + WSAStartup (0x202, &wsadat); +#endif +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + gpg_error_t err; + int any_options = 0; + int opt_tor = 0; + int opt_new_circuit = 0; + int opt_cert = 0; + int opt_srv = 0; + int opt_bracket = 0; + int opt_cname = 0; + char const *name = NULL; + + gpgrt_init (); + log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX); + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [HOST]\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n" + " --standard-resolver use the system's resolver\n" + " --use-tor use Tor\n" + " --new-circuit use a new Tor circuit\n" + " --bracket enclose v6 addresses in brackets\n" + " --cert lookup a CERT RR\n" + " --srv lookup a SRV RR\n" + " --cname lookup a CNAME RR\n" + " --timeout SECONDS timeout after SECONDS\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--use-tor")) + { + opt_tor = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--new-circuit")) + { + opt_new_circuit = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--standard-resolver")) + { + enable_standard_resolver (1); + argc--; argv++; + } + else if (!strcmp (*argv, "--recursive-resolver")) + { + enable_recursive_resolver (1); + argc--; argv++; + } + else if (!strcmp (*argv, "--bracket")) + { + opt_bracket = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--cert")) + { + any_options = opt_cert = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--srv")) + { + any_options = opt_srv = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--cname")) + { + any_options = opt_cname = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--timeout")) + { + argc--; argv++; + if (argc) + { + set_dns_timeout (atoi (*argv)); + argc--; argv++; + } + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + if (!argc && !any_options) + { + opt_cert = 1; + name = "simon.josefsson.org"; + } + else if (argc == 1) + name = *argv; + else + { + fprintf (stderr, PGM ": none or too many host names given\n"); + exit (1); + } + + set_dns_verbose (verbose, debug); + init_sockets (); + + if (opt_tor) + { + err = enable_dns_tormode (opt_new_circuit); + if (err) + { + fprintf (stderr, "error switching into Tor mode: %s\n", + gpg_strerror (err)); + exit (1); + } + } + + if (opt_cert) + { + unsigned char *fpr; + size_t fpr_len; + char *url; + void *key; + size_t keylen; + + if (verbose || any_options) + printf ("CERT lookup on '%s'\n", name); + + err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &keylen, + &fpr, &fpr_len, &url); + if (err) + printf ("get_dns_cert failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else if (key) + { + if (verbose || any_options) + printf ("Key found (%u bytes)\n", (unsigned int)keylen); + } + else + { + if (fpr) + { + int i; + + printf ("Fingerprint found (%d bytes): ", (int)fpr_len); + for (i = 0; i < fpr_len; i++) + printf ("%02X", fpr[i]); + putchar ('\n'); + } + else + printf ("No fingerprint found\n"); + + if (url) + printf ("URL found: %s\n", url); + else + printf ("No URL found\n"); + + } + + xfree (key); + xfree (fpr); + xfree (url); + } + else if (opt_cname) + { + char *cname; + + printf ("CNAME lookup on '%s'\n", name); + err = get_dns_cname (name, &cname); + if (err) + printf ("get_dns_cname failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else + { + printf ("CNAME found: '%s'\n", cname); + } + xfree (cname); + } + else if (opt_srv) + { + struct srventry *srv; + unsigned int count; + int i; + + err = get_dns_srv (name? name : "_hkp._tcp.wwwkeys.pgp.net", + &srv, &count); + if (err) + printf ("get_dns_srv failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else + { + printf ("count=%u\n",count); + for (i=0; i < count; i++) + { + printf("priority=%-8hu ",srv[i].priority); + printf("weight=%-8hu ",srv[i].weight); + printf("port=%-5hu ",srv[i].port); + printf("target=%s\n",srv[i].target); + } + + xfree(srv); + } + } + else /* Standard lookup. */ + { + char *cname; + dns_addrinfo_t aibuf, ai; + char *host; + + printf ("Lookup on '%s'\n", name); + + err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname); + if (err) + { + fprintf (stderr, PGM": resolving '%s' failed: %s\n", + name, gpg_strerror (err)); + exit (1); + } + + if (cname) + printf ("cname: %s\n", cname); + for (ai = aibuf; ai; ai = ai->next) + { + printf ("%s %3d %3d ", + ai->family == AF_INET6? "inet6" : + ai->family == AF_INET? "inet4" : "? ", + ai->socktype, ai->protocol); + + err = resolve_dns_addr (ai->addr, ai->addrlen, + (DNS_NUMERICHOST + | (opt_bracket? DNS_WITHBRACKET:0)), + &host); + if (err) + printf ("[resolve_dns_addr failed: %s]", gpg_strerror (err)); + else + { + printf ("%s", host); + xfree (host); + } + + err = resolve_dns_addr (ai->addr, ai->addrlen, + (opt_bracket? DNS_WITHBRACKET:0), + &host); + if (err) + printf (" [resolve_dns_addr failed (2): %s]", gpg_strerror (err)); + else + { + if (!is_ip_address (host)) + printf (" (%s)", host); + xfree (host); + } + putchar ('\n'); + } + xfree (cname); + free_dns_addrinfo (aibuf); + } + + reload_dns_stuff (1); /* Release objects. */ + + return 0; +} diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c new file mode 100644 index 0000000..a87382a --- /dev/null +++ b/dirmngr/t-http.c @@ -0,0 +1,398 @@ +/* t-http.c + * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010, + * 2011 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * This file 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "logging.h" +#include "http.h" + + +#if HTTP_USE_NTBTLS +# include +#elif HTTP_USE_GNUTLS +# include /* For init, logging, and deinit. */ +#endif /*HTTP_USE_GNUTLS*/ + +#define PGM "t-http" + +static int verbose; +static int debug; +static int no_verify; + +/* static void */ +/* read_dh_params (const char *fname) */ +/* { */ +/* gpg_error_t err; */ +/* int rc; */ +/* FILE *fp; */ +/* struct stat st; */ +/* char *buf; */ +/* size_t buflen; */ +/* gnutls_datum_t datum; */ + +/* fp = fopen (fname, "rb"); */ +/* if (!fp) */ +/* { */ +/* err = gpg_error_from_syserror (); */ +/* log_fatal ("can't open '%s': %s\n", fname, gpg_strerror (err)); */ +/* } */ + +/* if (fstat (fileno(fp), &st)) */ +/* { */ +/* err = gpg_error_from_syserror (); */ +/* log_fatal ("can't stat '%s': %s\n", fname, gpg_strerror (err)); */ +/* } */ + +/* buflen = st.st_size; */ +/* buf = xmalloc (buflen+1); */ +/* if (fread (buf, buflen, 1, fp) != 1) */ +/* { */ +/* err = gpg_error_from_syserror (); */ +/* log_fatal ("error reading '%s': %s\n", fname, gpg_strerror (err)); */ +/* } */ +/* fclose (fp); */ + +/* datum.size = buflen; */ +/* datum.data = buf; */ + +/* rc = gnutls_dh_params_import_pkcs3 (dh_params, &datum, GNUTLS_X509_FMT_PEM); */ +/* if (rc < 0) */ +/* log_fatal ("gnutls_dh_param_import failed: %s\n", gnutls_strerror (rc)); */ + +/* xfree (buf); */ +/* } */ + + + +#if HTTP_USE_GNUTLS +static gpg_error_t +verify_callback (http_t hd, http_session_t session, int reserved) +{ + (void)hd; + (void)reserved; + return no_verify? 0 : http_verify_server_credentials (session); +} +#endif + +#if HTTP_USE_GNUTLS +static void +my_gnutls_log (int level, const char *text) +{ + fprintf (stderr, "gnutls:L%d: %s", level, text); +} +#endif + +/* Prepend FNAME with the srcdir environment variable's value and + return an allocated filename. */ +static char * +prepend_srcdir (const char *fname) +{ + static const char *srcdir; + char *result; + + if (!srcdir && !(srcdir = getenv ("srcdir"))) + srcdir = "."; + + result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1); + strcpy (result, srcdir); + strcat (result, "/"); + strcat (result, fname); + return result; +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + gpg_error_t err; + int rc; + parsed_uri_t uri; + uri_tuple_t r; + http_t hd; + int c; + unsigned int my_http_flags = 0; + int no_out = 0; + int tls_dbg = 0; + const char *cafile = NULL; + http_session_t session = NULL; + + gpgrt_init (); + log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID); + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " URL\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n" + " --gnutls-debug N use GNUTLS debug level N\n" + " --cacert FNAME expect CA certificate in file FNAME\n" + " --no-verify do not verify the certificate\n" + " --force-tls use HTTP_FLAG_FORCE_TLS\n" + " --force-tor use HTTP_FLAG_FORCE_TOR\n" + " --no-out do not print the content\n", + stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--gnutls-debug")) + { + argc--; argv++; + if (argc) + { + tls_dbg = atoi (*argv); + argc--; argv++; + } + } + else if (!strcmp (*argv, "--cacert")) + { + argc--; argv++; + if (argc) + { + cafile = *argv; + argc--; argv++; + } + } + else if (!strcmp (*argv, "--no-verify")) + { + no_verify = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--force-tls")) + { + my_http_flags |= HTTP_FLAG_FORCE_TLS; + argc--; argv++; + } + else if (!strcmp (*argv, "--force-tor")) + { + my_http_flags |= HTTP_FLAG_FORCE_TOR; + argc--; argv++; + } + else if (!strcmp (*argv, "--no-out")) + { + no_out = 1; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + if (argc != 1) + { + fprintf (stderr, PGM ": no or too many URLS given\n"); + exit (1); + } + + if (!cafile) + cafile = prepend_srcdir ("tls-ca.pem"); + + /* http.c makes use of the assuan socket wrapper. */ + assuan_sock_init (); + +#if HTTP_USE_NTBTLS + + (void)err; + + ntbtls_set_debug (tls_dbg, NULL, NULL); + +#elif HTTP_USE_GNUTLS + + rc = gnutls_global_init (); + if (rc) + log_error ("gnutls_global_init failed: %s\n", gnutls_strerror (rc)); + + http_register_tls_callback (verify_callback); + http_register_tls_ca (cafile); + + err = http_session_new (&session, NULL, NULL, HTTP_FLAG_TRUST_DEF); + if (err) + log_error ("http_session_new failed: %s\n", gpg_strerror (err)); + + /* rc = gnutls_dh_params_init(&dh_params); */ + /* if (rc) */ + /* log_error ("gnutls_dh_params_init failed: %s\n", gnutls_strerror (rc)); */ + /* read_dh_params ("dh_param.pem"); */ + + /* rc = gnutls_certificate_set_x509_trust_file */ + /* (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); */ + /* if (rc) */ + /* log_error ("gnutls_certificate_set_x509_trust_file failed: %s\n", */ + /* gnutls_strerror (rc)); */ + + /* gnutls_certificate_set_dh_params (certcred, dh_params); */ + + gnutls_global_set_log_function (my_gnutls_log); + if (tls_dbg) + gnutls_global_set_log_level (tls_dbg); + +#endif /*HTTP_USE_GNUTLS*/ + + rc = http_parse_uri (&uri, *argv, 1); + if (rc) + { + log_error ("'%s': %s\n", *argv, gpg_strerror (rc)); + return 1; + } + + printf ("Scheme: %s\n", uri->scheme); + if (uri->opaque) + printf ("Value : %s\n", uri->path); + else + { + printf ("Auth : %s\n", uri->auth? uri->auth:"[none]"); + printf ("Host : %s\n", uri->host); + printf ("Port : %u\n", uri->port); + printf ("Path : %s\n", uri->path); + for (r = uri->params; r; r = r->next) + { + printf ("Params: %s", r->name); + if (!r->no_value) + { + printf ("=%s", r->value); + if (strlen (r->value) != r->valuelen) + printf (" [real length=%d]", (int) r->valuelen); + } + putchar ('\n'); + } + for (r = uri->query; r; r = r->next) + { + printf ("Query : %s", r->name); + if (!r->no_value) + { + printf ("=%s", r->value); + if (strlen (r->value) != r->valuelen) + printf (" [real length=%d]", (int) r->valuelen); + } + putchar ('\n'); + } + printf ("Flags :%s%s%s%s\n", + uri->is_http? " http":"", + uri->opaque? " opaque":"", + uri->v6lit? " v6lit":"", + uri->onion? " onion":""); + printf ("TLS : %s\n", + uri->use_tls? "yes": + (my_http_flags&HTTP_FLAG_FORCE_TLS)? "forced" : "no"); + printf ("Tor : %s\n", + (my_http_flags&HTTP_FLAG_FORCE_TOR)? "yes" : "no"); + + } + fflush (stdout); + http_release_parsed_uri (uri); + uri = NULL; + + rc = http_open_document (&hd, *argv, NULL, my_http_flags, + NULL, session, NULL, NULL); + if (rc) + { + log_error ("can't get '%s': %s\n", *argv, gpg_strerror (rc)); + return 1; + } + log_info ("open_http_document succeeded; status=%u\n", + http_get_status_code (hd)); + + { + const char **names; + int i; + + names = http_get_header_names (hd); + if (!names) + log_fatal ("http_get_header_names failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + for (i = 0; names[i]; i++) + printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i])); + xfree (names); + } + fflush (stdout); + + switch (http_get_status_code (hd)) + { + case 200: + case 400: + case 401: + case 403: + case 404: + { + unsigned long count = 0; + while ((c = es_getc (http_get_read_ptr (hd))) != EOF) + { + count++; + if (!no_out) + putchar (c); + } + log_info ("Received bytes: %lu\n", count); + } + break; + case 301: + case 302: + case 307: + log_info ("Redirected to: %s\n", http_get_header (hd, "Location")); + break; + } + http_close (hd, 0); + + http_session_release (session); +#ifdef HTTP_USE_GNUTLS + gnutls_global_deinit (); +#endif /*HTTP_USE_GNUTLS*/ + + return 0; +} diff --git a/dirmngr/t-ldap-parse-uri.c b/dirmngr/t-ldap-parse-uri.c new file mode 100644 index 0000000..932ca7d --- /dev/null +++ b/dirmngr/t-ldap-parse-uri.c @@ -0,0 +1,257 @@ +/* t-ldap-parse-uri.c - Regression tests for ldap-parse-uri.c. + * Copyright (C) 2015 g10 Code GmbH + * + * 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 3 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, see . + */ + +#include + +#include "ldap-parse-uri.h" + +#include "t-support.h" + +struct test_ldap_uri_p +{ + const char *uri; + int result; +}; + +void +check_ldap_uri_p (int test_count, struct test_ldap_uri_p *test) +{ + int result = ldap_uri_p (test->uri); + if (result != test->result) + { + printf ("'%s' is %san LDAP schema, but ldap_uri_p says opposite.\n", + test->uri, test->result ? "" : "not "); + fail(1000 * test_count); + } +} + +static void +test_ldap_uri_p (void) +{ + struct test_ldap_uri_p tests[] = { + { "ldap://foo", 1 }, + { "ldap://", 1 }, + { "ldap:", 1 }, + { "ldap", 0 }, + { "ldapfoobar", 0 }, + + { "ldaps://foo", 1 }, + { "ldaps://", 1 }, + { "ldaps:", 1 }, + { "ldaps", 0 }, + { "ldapsfoobar", 0 }, + + { "ldapi://foo", 1 }, + { "ldapi://", 1 }, + { "ldapi:", 1 }, + { "ldapi", 0 }, + { "ldapifoobar", 0 }, + + { "LDAP://FOO", 1 }, + { "LDAP://", 1 }, + { "LDAP:", 1 }, + { "LDAP", 0 }, + { "LDAPFOOBAR", 0 } + }; + + int test_count; + for (test_count = 1; + test_count <= sizeof (tests) / sizeof (tests[0]); + test_count ++) + check_ldap_uri_p (test_count, &tests[test_count - 1]); +} + +struct test_ldap_parse_uri +{ + const char *uri; + const char *scheme; + const char *host; + const int port; + const int use_tls; + const char *path; /* basedn. */ + const char *auth; /* binddn. */ + const char *password; /* query[1]. */ +}; + +static int +cmp (const char *a, const char *b) +{ + if (! a) + a = ""; + if (! b) + b = ""; + + return strcmp (a, b) == 0; +} + +void +check_ldap_parse_uri (int test_count, struct test_ldap_parse_uri *test) +{ + gpg_error_t err; + parsed_uri_t puri; + + err = ldap_parse_uri (&puri, test->uri); + if (err) + { + printf ("Parsing '%s' failed (%d).\n", test->uri, err); + fail (test_count * 1000 + 0); + } + + if (! cmp(test->scheme, puri->scheme)) + { + printf ("scheme mismatch: got '%s', expected '%s'.\n", + puri->scheme, test->scheme); + fail (test_count * 1000 + 1); + } + + if (! cmp(test->host, puri->host)) + { + printf ("host mismatch: got '%s', expected '%s'.\n", + puri->host, test->host); + fail (test_count * 1000 + 2); + } + + if (test->port != puri->port) + { + printf ("port mismatch: got '%d', expected '%d'.\n", + puri->port, test->port); + fail (test_count * 1000 + 3); + } + + if (test->use_tls != puri->use_tls) + { + printf ("use_tls mismatch: got '%d', expected '%d'.\n", + puri->use_tls, test->use_tls); + fail (test_count * 1000 + 4); + } + + if (! cmp(test->path, puri->path)) + { + printf ("path mismatch: got '%s', expected '%s'.\n", + puri->path, test->path); + fail (test_count * 1000 + 5); + } + + if (! cmp(test->auth, puri->auth)) + { + printf ("auth mismatch: got '%s', expected '%s'.\n", + puri->auth, test->auth); + fail (test_count * 1000 + 6); + } + + if (! test->password && ! puri->query) + /* Ok. */ + ; + else if (test->password && ! puri->query) + { + printf ("password mismatch: got NULL, expected '%s'.\n", + test->auth); + fail (test_count * 1000 + 7); + } + else if (! test->password && puri->query) + { + printf ("password mismatch: got something, expected NULL.\n"); + fail (test_count * 1000 + 8); + } + else if (! (test->password && puri->query + && puri->query->name && puri->query->value + && strcmp (puri->query->name, "password") == 0 + && cmp (puri->query->value, test->password))) + { + printf ("password mismatch: got '%s:%s', expected 'password:%s'.\n", + puri->query->name, puri->query->value, + test->password); + fail (test_count * 1000 + 9); + } + + http_release_parsed_uri (puri); +} + +static void +test_ldap_parse_uri (void) +{ + struct test_ldap_parse_uri tests[] = { + { "ldap://", "ldap", NULL, 389, 0, NULL, NULL, NULL }, + { "ldap://host", "ldap", "host", 389, 0, NULL, NULL, NULL }, + { "ldap://host:100", "ldap", "host", 100, 0, NULL, NULL, NULL }, + { "ldaps://host", "ldaps", "host", 636, 1, NULL, NULL, NULL }, + { "ldap://host/ou%3DPGP%20Keys%2Cdc%3DEXAMPLE%2Cdc%3DORG", + "ldap", "host", 389, 0, "ou=PGP Keys,dc=EXAMPLE,dc=ORG" }, + { "ldap://host/????bindname=uid%3Duser%2Cou%3DPGP%20Users%2Cdc%3DEXAMPLE%2Cdc%3DORG,password=foobar", + "ldap", "host", 389, 0, "", + "uid=user,ou=PGP Users,dc=EXAMPLE,dc=ORG", "foobar" } + }; + + int test_count; + for (test_count = 1; + test_count <= sizeof (tests) / sizeof (tests[0]); + test_count ++) + check_ldap_parse_uri (test_count, &tests[test_count - 1]); +} + +struct test_ldap_escape_filter +{ + const char *filter; + const char *result; +}; + +static void +check_ldap_escape_filter (int test_count, struct test_ldap_escape_filter *test) +{ + char *result = ldap_escape_filter (test->filter); + + if (strcmp (result, test->result) != 0) + { + printf ("Filter: '%s'. Escaped: '%s'. Expected: '%s'.\n", + test->filter, result, test->result); + fail (test_count * 1000); + } + + xfree (result); +} + +static void +test_ldap_escape_filter (void) +{ + struct test_ldap_escape_filter tests[] = { + { "foobar", "foobar" }, + { "", "" }, + { "(foo)", "%28foo%29" }, + { "* ( ) \\ /", "%2a %28 %29 %5c %2f" } + }; + + int test_count; + for (test_count = 1; + test_count <= sizeof (tests) / sizeof (tests[0]); + test_count ++) + check_ldap_escape_filter (test_count, &tests[test_count - 1]); +} + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_ldap_uri_p (); + test_ldap_parse_uri (); + test_ldap_escape_filter (); + + return 0; +} diff --git a/dirmngr/t-support.h b/dirmngr/t-support.h new file mode 100644 index 0000000..f773f1e --- /dev/null +++ b/dirmngr/t-support.h @@ -0,0 +1,42 @@ +/* t-support.h - Helper for the regression tests + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of JNLIB, which is a subsystem of GnuPG. + * + * JNLIB is free software; you can redistribute it and/or modify it + * under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - 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. + * + * or both in parallel, as here. + * + * JNLIB 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#ifndef DIRMNGR_T_SUPPORT_H +#define DIRMNGR_T_SUPPORT_H 1 + +/* Macros to print the result of a test. */ +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +#endif /* DIRMNGR_T_SUPPORT_H */ diff --git a/dirmngr/tls-ca.pem b/dirmngr/tls-ca.pem new file mode 100644 index 0000000..c296466 --- /dev/null +++ b/dirmngr/tls-ca.pem @@ -0,0 +1,30 @@ +Issuer ...: /CN=UTN-USERFirst-Hardware/OU=http:\x2f\x2fwww.usertrust.com/O=The USERTRUST Network/L=Salt Lake City/ST=UT/C=US +Serial ...: 44BE0C8B500024B411D3362AFE650AFD +Subject ..: /CN=UTN-USERFirst-Hardware/OU=http:\x2f\x2fwww.usertrust.com/O=The USERTRUST Network/L=Salt Lake City/ST=UT/C=US + +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- diff --git a/dirmngr/validate.c b/dirmngr/validate.c new file mode 100644 index 0000000..b3dc9d8 --- /dev/null +++ b/dirmngr/validate.c @@ -0,0 +1,1155 @@ +/* validate.c - Validate a certificate chain. + * Copyright (C) 2001, 2003, 2004, 2008 Free Software Foundation, Inc. + * Copyright (C) 2004, 2006, 2008 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#include + +#include +#include +#include +#include +#include + +#include "dirmngr.h" +#include "certcache.h" +#include "crlcache.h" +#include "validate.h" +#include "misc.h" + +/* While running the validation function we need to keep track of the + certificates and the validation outcome of each. We use this type + for it. */ +struct chain_item_s +{ + struct chain_item_s *next; + ksba_cert_t cert; /* The certificate. */ + unsigned char fpr[20]; /* Fingerprint of the certificate. */ + int is_self_signed; /* This certificate is self-signed. */ + int is_valid; /* The certifiate is valid except for revocations. */ +}; +typedef struct chain_item_s *chain_item_t; + + +/* A couple of constants with Object Identifiers. */ +static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1"; +static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2"; +static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3"; +static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4"; +static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8"; +static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; + + +/* Prototypes. */ +static gpg_error_t check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); + + + + +/* Check whether CERT contains critical extensions we don't know + about. */ +static gpg_error_t +unknown_criticals (ksba_cert_t cert) +{ + static const char *known[] = { + "2.5.29.15", /* keyUsage */ + "2.5.29.19", /* basic Constraints */ + "2.5.29.32", /* certificatePolicies */ + "2.5.29.37", /* extendedKeyUsage */ + NULL + }; + int i, idx, crit; + const char *oid; + int unsupported; + strlist_t sl; + gpg_error_t err, rc; + + rc = 0; + for (idx=0; !(err=ksba_cert_get_extension (cert, idx, + &oid, &crit, NULL, NULL));idx++) + { + if (!crit) + continue; + for (i=0; known[i] && strcmp (known[i],oid); i++) + ; + unsupported = !known[i]; + + /* If this critical extension is not supported, check the list + of to be ignored extensions to see whether we claim that it + is supported. */ + if (unsupported && opt.ignored_cert_extensions) + { + for (sl=opt.ignored_cert_extensions; + sl && strcmp (sl->d, oid); sl = sl->next) + ; + if (sl) + unsupported = 0; + } + + if (unsupported) + { + log_error (_("critical certificate extension %s is not supported"), + oid); + rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT); + } + } + if (err && gpg_err_code (err) != GPG_ERR_EOF) + rc = err; /* Such an error takes precendence. */ + + return rc; +} + + +/* Basic check for supported policies. */ +static gpg_error_t +check_cert_policy (ksba_cert_t cert) +{ + static const char *allowed[] = { + "2.289.9.9", + NULL + }; + gpg_error_t err; + int idx; + char *p, *haystack; + char *policies; + int any_critical; + + err = ksba_cert_get_cert_policies (cert, &policies); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + return 0; /* No policy given. */ + if (err) + return err; + + /* STRING is a line delimited list of certifiate policies as stored + in the certificate. The line itself is colon delimited where the + first field is the OID of the policy and the second field either + N or C for normal or critical extension */ + if (opt.verbose > 1) + log_info ("certificate's policy list: %s\n", policies); + + /* The check is very minimal but won't give false positives */ + any_critical = !!strstr (policies, ":C"); + + /* See whether we find ALLOWED (which is an OID) in POLICIES */ + for (idx=0; allowed[idx]; idx++) + { + for (haystack=policies; (p=strstr (haystack, allowed[idx])); + haystack = p+1) + { + if ( !(p == policies || p[-1] == '\n') ) + continue; /* Does not match the begin of a line. */ + if (p[strlen (allowed[idx])] != ':') + continue; /* The length does not match. */ + /* Yep - it does match: Return okay. */ + ksba_free (policies); + return 0; + } + } + + if (!any_critical) + { + log_info (_("Note: non-critical certificate policy not allowed")); + err = 0; + } + else + { + log_info (_("certificate policy not allowed")); + err = gpg_error (GPG_ERR_NO_POLICY_MATCH); + } + + ksba_free (policies); + return err; +} + + +static gpg_error_t +allowed_ca (ksba_cert_t cert, int *chainlen) +{ + gpg_error_t err; + int flag; + + err = ksba_cert_is_ca (cert, &flag, chainlen); + if (err) + return err; + if (!flag) + { + if (!is_trusted_cert (cert)) + { + /* The German SigG Root CA's certificate does not flag + itself as a CA; thus we relax this requirement if we + trust a root CA. I think this is reasonable. Note, that + gpgsm implements a far stricter scheme here. */ + if (chainlen) + *chainlen = 3; /* That is what the SigG implements. */ + if (opt.verbose) + log_info (_("accepting root CA not marked as a CA")); + } + else + { + log_error (_("issuer certificate is not marked as a CA")); + return gpg_error (GPG_ERR_BAD_CA_CERT); + } + } + return 0; +} + +/* Helper for validate_cert_chain. */ +static gpg_error_t +check_revocations (ctrl_t ctrl, chain_item_t chain) +{ + gpg_error_t err = 0; + int any_revoked = 0; + int any_no_crl = 0; + int any_crl_too_old = 0; + chain_item_t ci; + + assert (ctrl->check_revocations_nest_level >= 0); + assert (chain); + + if (ctrl->check_revocations_nest_level > 10) + { + log_error (_("CRL checking too deeply nested\n")); + return gpg_error(GPG_ERR_BAD_CERT_CHAIN); + } + ctrl->check_revocations_nest_level++; + + + for (ci=chain; ci; ci = ci->next) + { + assert (ci->cert); + if (ci == chain) + { + /* It does not make sense to check the root certificate for + revocations. In almost all cases this will lead to a + catch-22 as the root certificate is the final trust + anchor for the certificates and the CRLs. We expect the + user to remove root certificates from the list of trusted + certificates in case they have been revoked. */ + if (opt.verbose) + cert_log_name (_("not checking CRL for"), ci->cert); + continue; + } + + if (opt.verbose) + cert_log_name (_("checking CRL for"), ci->cert); + err = crl_cache_cert_isvalid (ctrl, ci->cert, 0); + if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN) + { + err = crl_cache_reload_crl (ctrl, ci->cert); + if (!err) + err = crl_cache_cert_isvalid (ctrl, ci->cert, 0); + } + switch (gpg_err_code (err)) + { + case 0: err = 0; break; + case GPG_ERR_CERT_REVOKED: any_revoked = 1; err = 0; break; + case GPG_ERR_NO_CRL_KNOWN: any_no_crl = 1; err = 0; break; + case GPG_ERR_CRL_TOO_OLD: any_crl_too_old = 1; err = 0; break; + default: break; + } + } + ctrl->check_revocations_nest_level--; + + + if (err) + ; + else if (any_revoked) + err = gpg_error (GPG_ERR_CERT_REVOKED); + else if (any_no_crl) + err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (any_crl_too_old) + err = gpg_error (GPG_ERR_CRL_TOO_OLD); + else + err = 0; + return err; +} + + +/* Check whether CERT is a root certificate. ISSUERDN and SUBJECTDN + are the DNs already extracted by the caller from CERT. Returns + True if this is the case. */ +static int +is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn) +{ + gpg_error_t err; + int result = 0; + ksba_sexp_t serialno; + ksba_sexp_t ak_keyid; + ksba_name_t ak_name; + ksba_sexp_t ak_sn; + const char *ak_name_str; + ksba_sexp_t subj_keyid = NULL; + + if (!issuerdn || !subjectdn) + return 0; /* No. */ + + if (strcmp (issuerdn, subjectdn)) + return 0; /* No. */ + + err = ksba_cert_get_auth_key_id (cert, &ak_keyid, &ak_name, &ak_sn); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + return 1; /* Yes. Without a authorityKeyIdentifier this needs + to be the Root certifcate (our trust anchor). */ + log_error ("error getting authorityKeyIdentifier: %s\n", + gpg_strerror (err)); + return 0; /* Well, it is broken anyway. Return No. */ + } + + serialno = ksba_cert_get_serial (cert); + if (!serialno) + { + log_error ("error getting serialno: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Check whether the auth name's matches the issuer name+sn. If + that is the case this is a root certificate. */ + ak_name_str = ksba_name_enum (ak_name, 0); + if (ak_name_str + && !strcmp (ak_name_str, issuerdn) + && !cmp_simple_canon_sexp (ak_sn, serialno)) + { + result = 1; /* Right, CERT is self-signed. */ + goto leave; + } + + /* Similar for the ak_keyid. */ + if (ak_keyid && !ksba_cert_get_subj_key_id (cert, NULL, &subj_keyid) + && !cmp_simple_canon_sexp (ak_keyid, subj_keyid)) + { + result = 1; /* Right, CERT is self-signed. */ + goto leave; + } + + + leave: + ksba_free (subj_keyid); + ksba_free (ak_keyid); + ksba_name_release (ak_name); + ksba_free (ak_sn); + ksba_free (serialno); + return result; +} + + +/* Validate the certificate CHAIN up to the trust anchor. Optionally + return the closest expiration time in R_EXPTIME (this is useful for + caching issues). MODE is one of the VALIDATE_MODE_* constants. + + Note that VALIDATE_MODE_OCSP is not used due to the removal of the + system service in 2.1.15. Instead only the callback to gpgsm to + validate a certificate is used. + + If R_TRUST_ANCHOR is not NULL and the validation would fail only + because the root certificate is not trusted, the hexified + fingerprint of that root certificate is stored at R_TRUST_ANCHOR + and success is returned. The caller needs to free the value at + R_TRUST_ANCHOR; in all other cases NULL is stored there. */ +gpg_error_t +validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, + int mode, char **r_trust_anchor) +{ + gpg_error_t err = 0; + int depth, maxdepth; + char *issuer = NULL; + char *subject = NULL; + ksba_cert_t subject_cert = NULL, issuer_cert = NULL; + ksba_isotime_t current_time; + ksba_isotime_t exptime; + int any_expired = 0; + int any_no_policy_match = 0; + chain_item_t chain; + + + if (r_exptime) + *r_exptime = 0; + *exptime = 0; + + if (r_trust_anchor) + *r_trust_anchor = NULL; + + if (DBG_X509) + dump_cert ("subject", cert); + + /* May the target certificate be used for this purpose? */ + switch (mode) + { + case VALIDATE_MODE_OCSP: + err = cert_use_ocsp_p (cert); + break; + case VALIDATE_MODE_CRL: + case VALIDATE_MODE_CRL_RECURSIVE: + err = cert_use_crl_p (cert); + break; + default: + err = 0; + break; + } + if (err) + return err; + + /* If we already validated the certificate not too long ago, we can + avoid the excessive computations and lookups unless the caller + asked for the expiration time. */ + if (!r_exptime) + { + size_t buflen; + time_t validated_at; + + err = ksba_cert_get_user_data (cert, "validated_at", + &validated_at, sizeof (validated_at), + &buflen); + if (err || buflen != sizeof (validated_at) || !validated_at) + err = 0; /* Not available or other error. */ + else + { + /* If the validation is not older than 30 minutes we are ready. */ + if (validated_at < gnupg_get_time () + (30*60)) + { + if (opt.verbose) + log_info ("certificate is good (cached)\n"); + /* Note, that we can't jump to leave here as this would + falsely updated the validation timestamp. */ + return 0; + } + } + } + + /* Get the current time. */ + gnupg_get_isotime (current_time); + + /* We walk up the chain until we find a trust anchor. */ + subject_cert = cert; + maxdepth = 10; + chain = NULL; + depth = 0; + for (;;) + { + /* Get the subject and issuer name from the current + certificate. */ + ksba_free (issuer); + ksba_free (subject); + issuer = ksba_cert_get_issuer (subject_cert, 0); + subject = ksba_cert_get_subject (subject_cert, 0); + + if (!issuer) + { + log_error (_("no issuer found in certificate\n")); + err = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + /* Handle the notBefore and notAfter timestamps. */ + { + ksba_isotime_t not_before, not_after; + + err = ksba_cert_get_validity (subject_cert, 0, not_before); + if (!err) + err = ksba_cert_get_validity (subject_cert, 1, not_after); + if (err) + { + log_error (_("certificate with invalid validity: %s"), + gpg_strerror (err)); + err = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + /* Keep track of the nearest expiration time in EXPTIME. */ + if (*not_after) + { + if (!*exptime) + gnupg_copy_time (exptime, not_after); + else if (strcmp (not_after, exptime) < 0 ) + gnupg_copy_time (exptime, not_after); + } + + /* Check whether the certificate is already valid. */ + if (*not_before && strcmp (current_time, not_before) < 0 ) + { + log_error (_("certificate not yet valid")); + log_info ("(valid from "); + dump_isotime (not_before); + log_printf (")\n"); + err = gpg_error (GPG_ERR_CERT_TOO_YOUNG); + goto leave; + } + + /* Now check whether the certificate has expired. */ + if (*not_after && strcmp (current_time, not_after) > 0 ) + { + log_error (_("certificate has expired")); + log_info ("(expired at "); + dump_isotime (not_after); + log_printf (")\n"); + any_expired = 1; + } + } + + /* Do we have any critical extensions in the certificate we + can't handle? */ + err = unknown_criticals (subject_cert); + if (err) + goto leave; /* yes. */ + + /* Check that given policies are allowed. */ + err = check_cert_policy (subject_cert); + if (gpg_err_code (err) == GPG_ERR_NO_POLICY_MATCH) + { + any_no_policy_match = 1; + err = 0; + } + else if (err) + goto leave; + + /* Is this a self-signed certificate? */ + if (is_root_cert ( subject_cert, issuer, subject)) + { + /* Yes, this is our trust anchor. */ + if (check_cert_sig (subject_cert, subject_cert) ) + { + log_error (_("selfsigned certificate has a BAD signature")); + err = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN + : GPG_ERR_BAD_CERT); + goto leave; + } + + /* Is this certificate allowed to act as a CA. */ + err = allowed_ca (subject_cert, NULL); + if (err) + goto leave; /* No. */ + + err = is_trusted_cert (subject_cert); + if (!err) + ; /* Yes we trust this cert. */ + else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED) + { + char *fpr; + + log_error (_("root certificate is not marked trusted")); + fpr = get_fingerprint_hexstring (subject_cert); + log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); + dump_cert ("issuer", subject_cert); + if (r_trust_anchor) + { + /* Caller wants to do another trustiness check. */ + *r_trust_anchor = fpr; + err = 0; + } + else + xfree (fpr); + } + else + { + log_error (_("checking trustworthiness of " + "root certificate failed: %s\n"), + gpg_strerror (err)); + } + if (err) + goto leave; + + /* Prepend the certificate to our list. */ + { + chain_item_t ci; + + ci = xtrycalloc (1, sizeof *ci); + if (!ci) + { + err = gpg_error_from_errno (errno); + goto leave; + } + ksba_cert_ref (subject_cert); + ci->cert = subject_cert; + cert_compute_fpr (subject_cert, ci->fpr); + ci->next = chain; + chain = ci; + } + + if (opt.verbose) + { + if (r_trust_anchor && *r_trust_anchor) + log_info ("root certificate is good but not trusted\n"); + else + log_info ("root certificate is good and trusted\n"); + } + + break; /* Okay: a self-signed certicate is an end-point. */ + } + + /* To avoid loops, we use an arbitrary limit on the length of + the chain. */ + depth++; + if (depth > maxdepth) + { + log_error (_("certificate chain too long\n")); + err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); + goto leave; + } + + /* Find the next cert up the tree. */ + ksba_cert_release (issuer_cert); issuer_cert = NULL; + err = find_issuing_cert (ctrl, subject_cert, &issuer_cert); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + { + log_error (_("issuer certificate not found")); + log_info ("issuer certificate: #/"); + dump_string (issuer); + log_printf ("\n"); + } + else + log_error (_("issuer certificate not found: %s\n"), + gpg_strerror (err)); + /* Use a better understandable error code. */ + err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); + goto leave; + } + +/* try_another_cert: */ + if (DBG_X509) + { + log_debug ("got issuer's certificate:\n"); + dump_cert ("issuer", issuer_cert); + } + + /* Now check the signature of the certificate. Well, we + should delay this until later so that faked certificates + can't be turned into a DoS easily. */ + err = check_cert_sig (issuer_cert, subject_cert); + if (err) + { + log_error (_("certificate has a BAD signature")); +#if 0 + if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE) + { + /* We now try to find other issuer certificates which + might have been used. This is required because some + CAs are reusing the issuer and subject DN for new + root certificates without using a authorityKeyIdentifier. */ + rc = find_up (kh, subject_cert, issuer, 1); + if (!rc) + { + ksba_cert_t tmp_cert; + + rc = keydb_get_cert (kh, &tmp_cert); + if (rc || !compare_certs (issuer_cert, tmp_cert)) + { + /* The find next did not work or returned an + identical certificate. We better stop here + to avoid infinite checks. */ + rc = gpg_error (GPG_ERR_BAD_SIGNATURE); + ksba_cert_release (tmp_cert); + } + else + { + do_list (0, lm, fp, _("found another possible matching " + "CA certificate - trying again")); + ksba_cert_release (issuer_cert); + issuer_cert = tmp_cert; + goto try_another_cert; + } + } + } +#endif + /* We give a more descriptive error code than the one + returned from the signature checking. */ + err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); + goto leave; + } + + /* Check that the length of the chain is not longer than allowed + by the CA. */ + { + int chainlen; + + err = allowed_ca (issuer_cert, &chainlen); + if (err) + goto leave; + if (chainlen >= 0 && (depth - 1) > chainlen) + { + log_error (_("certificate chain longer than allowed by CA (%d)"), + chainlen); + err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); + goto leave; + } + } + + /* May that certificate be used for certification? */ + err = cert_use_cert_p (issuer_cert); + if (err) + goto leave; /* No. */ + + /* Prepend the certificate to our list. */ + { + chain_item_t ci; + + ci = xtrycalloc (1, sizeof *ci); + if (!ci) + { + err = gpg_error_from_errno (errno); + goto leave; + } + ksba_cert_ref (subject_cert); + ci->cert = subject_cert; + cert_compute_fpr (subject_cert, ci->fpr); + ci->next = chain; + chain = ci; + } + + if (opt.verbose) + log_info (_("certificate is good\n")); + + /* Now to the next level up. */ + subject_cert = issuer_cert; + issuer_cert = NULL; + } + + if (!err) + { /* If we encountered an error somewhere during the checks, set + the error code to the most critical one */ + if (any_expired) + err = gpg_error (GPG_ERR_CERT_EXPIRED); + else if (any_no_policy_match) + err = gpg_error (GPG_ERR_NO_POLICY_MATCH); + } + + if (!err && opt.verbose) + { + chain_item_t citem; + + log_info (_("certificate chain is good\n")); + for (citem = chain; citem; citem = citem->next) + cert_log_name (" certificate", citem->cert); + } + + if (!err && mode != VALIDATE_MODE_CRL) + { /* Now that everything is fine, walk the chain and check each + certificate for revocations. + + 1. item in the chain - The root certificate. + 2. item - the CA below the root + last item - the target certificate. + + Now for each certificate in the chain check whether it has + been included in a CRL and thus be revoked. We don't do OCSP + here because this does not seem to make much sense. This + might become a recursive process and we should better cache + our validity results to avoid double work. Far worse a + catch-22 may happen for an improper setup hierarchy and we + need a way to break up such a deadlock. */ + err = check_revocations (ctrl, chain); + } + + if (!err && opt.verbose) + { + if (r_trust_anchor && *r_trust_anchor) + log_info ("target certificate may be valid\n"); + else + log_info ("target certificate is valid\n"); + } + else if (err && opt.verbose) + log_info ("target certificate is NOT valid\n"); + + + leave: + if (!err && !(r_trust_anchor && *r_trust_anchor)) + { + /* With no error we can update the validation cache. We do this + for all certificates in the chain. Note that we can't use + the cache if the caller requested to check the trustiness of + the root certificate himself. Adding such a feature would + require us to also store the fingerprint of root + certificate. */ + chain_item_t citem; + time_t validated_at = gnupg_get_time (); + + for (citem = chain; citem; citem = citem->next) + { + err = ksba_cert_set_user_data (citem->cert, "validated_at", + &validated_at, sizeof (validated_at)); + if (err) + { + log_error ("set_user_data(validated_at) failed: %s\n", + gpg_strerror (err)); + err = 0; + } + } + } + + if (r_exptime) + gnupg_copy_time (r_exptime, exptime); + ksba_free (issuer); + ksba_free (subject); + ksba_cert_release (issuer_cert); + if (subject_cert != cert) + ksba_cert_release (subject_cert); + while (chain) + { + chain_item_t ci_next = chain->next; + if (chain->cert) + ksba_cert_release (chain->cert); + xfree (chain); + chain = ci_next; + } + if (err && r_trust_anchor && *r_trust_anchor) + { + xfree (*r_trust_anchor); + *r_trust_anchor = NULL; + } + return err; +} + + + +/* Return the public key algorithm id from the S-expression PKEY. + FIXME: libgcrypt should provide such a function. Note that this + implementation uses the names as used by libksba. */ +static int +pk_algo_from_sexp (gcry_sexp_t pkey) +{ + gcry_sexp_t l1, l2; + const char *name; + size_t n; + int algo; + + l1 = gcry_sexp_find_token (pkey, "public-key", 0); + if (!l1) + return 0; /* Not found. */ + l2 = gcry_sexp_cadr (l1); + gcry_sexp_release (l1); + + name = gcry_sexp_nth_data (l2, 0, &n); + if (!name) + algo = 0; /* Not found. */ + else if (n==3 && !memcmp (name, "rsa", 3)) + algo = GCRY_PK_RSA; + else if (n==3 && !memcmp (name, "dsa", 3)) + algo = GCRY_PK_DSA; + else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) + algo = GCRY_PK_RSA; + else + algo = 0; + gcry_sexp_release (l2); + return algo; +} + + +/* Check the signature on CERT using the ISSUER_CERT. This function + does only test the cryptographic signature and nothing else. It is + assumed that the ISSUER_CERT is valid. */ +static gpg_error_t +check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) +{ + gpg_error_t err; + const char *algoid; + gcry_md_hd_t md; + int i, algo; + ksba_sexp_t p; + size_t n; + gcry_sexp_t s_sig, s_hash, s_pkey; + const char *s; + char algo_name[16+1]; /* hash algorithm name converted to lower case. */ + int digestlen; + unsigned char *digest; + + /* Hash the target certificate using the algorithm from that certificate. */ + algoid = ksba_cert_get_digest_algo (cert); + algo = gcry_md_map_name (algoid); + if (!algo) + { + log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); + return gpg_error (GPG_ERR_GENERAL); + } + s = gcry_md_algo_name (algo); + for (i=0; *s && i < sizeof algo_name - 1; s++, i++) + algo_name[i] = tolower (*s); + algo_name[i] = 0; + + err = gcry_md_open (&md, algo, 0); + if (err) + { + log_error ("md_open failed: %s\n", gpg_strerror (err)); + return err; + } + if (DBG_HASHING) + gcry_md_debug (md, "hash.cert"); + + err = ksba_cert_hash (cert, 1, HASH_FNC, md); + if (err) + { + log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (err)); + gcry_md_close (md); + return err; + } + gcry_md_final (md); + + /* Get the signature value out of the target certificate. */ + p = ksba_cert_get_sig_val (cert); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + gcry_md_close (md); + ksba_free (p); + return gpg_error (GPG_ERR_BUG); + } + if (DBG_CRYPTO) + { + int j; + log_debug ("signature value:"); + for (j=0; j < n; j++) + log_printf (" %02X", p[j]); + log_printf ("\n"); + } + + err = gcry_sexp_sscan ( &s_sig, NULL, p, n); + ksba_free (p); + if (err) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err)); + gcry_md_close (md); + return err; + } + + /* Get the public key from the issuer certificate. */ + p = ksba_cert_get_public_key (issuer_cert); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + gcry_md_close (md); + ksba_free (p); + gcry_sexp_release (s_sig); + return gpg_error (GPG_ERR_BUG); + } + err = gcry_sexp_sscan ( &s_pkey, NULL, p, n); + ksba_free (p); + if (err) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err)); + gcry_md_close (md); + gcry_sexp_release (s_sig); + return err; + } + + + /* Prepare the values for signature verification. At this point we + have these values: + + S_PKEY - S-expression with the issuer's public key. + S_SIG - Signature value as given in the certrificate. + MD - Finalized hash context with hash of the certificate. + ALGO_NAME - Lowercase hash algorithm name + */ + digestlen = gcry_md_get_algo_dlen (algo); + digest = gcry_md_read (md, algo); + if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA) + { + if (digestlen != 20) + { + log_error (_("DSA requires the use of a 160 bit hash algorithm\n")); + gcry_md_close (md); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_pkey); + return gpg_error (GPG_ERR_INTERNAL); + } + if ( gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", + (int)digestlen, digest) ) + BUG (); + } + else /* Not DSA. */ + { + if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + algo_name, (int)digestlen, digest) ) + BUG (); + + } + + err = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (DBG_X509) + log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); + gcry_md_close (md); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_pkey); + return err; +} + + + +/* Return 0 if the cert is usable for encryption. A MODE of 0 checks + for signing, a MODE of 1 checks for encryption, a MODE of 2 checks + for verification and a MODE of 3 for decryption (just for + debugging). MODE 4 is for certificate signing, MODE 5 for OCSP + response signing, MODE 6 is for CRL signing. */ +static int +cert_usage_p (ksba_cert_t cert, int mode) +{ + gpg_error_t err; + unsigned int use; + char *extkeyusages; + int have_ocsp_signing = 0; + + err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; /* No policy given. */ + if (!err) + { + unsigned int extusemask = ~0; /* Allow all. */ + + if (extkeyusages) + { + char *p, *pend; + int any_critical = 0; + + extusemask = 0; + + p = extkeyusages; + while (p && (pend=strchr (p, ':'))) + { + *pend++ = 0; + /* Only care about critical flagged usages. */ + if ( *pend == 'C' ) + { + any_critical = 1; + if ( !strcmp (p, oid_kp_serverAuth)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_KEY_ENCIPHERMENT + | KSBA_KEYUSAGE_KEY_AGREEMENT); + else if ( !strcmp (p, oid_kp_clientAuth)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_KEY_AGREEMENT); + else if ( !strcmp (p, oid_kp_codeSigning)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE); + else if ( !strcmp (p, oid_kp_emailProtection)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_NON_REPUDIATION + | KSBA_KEYUSAGE_KEY_ENCIPHERMENT + | KSBA_KEYUSAGE_KEY_AGREEMENT); + else if ( !strcmp (p, oid_kp_timeStamping)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_NON_REPUDIATION); + } + + /* This is a hack to cope with OCSP. Note that we do + not yet fully comply with the requirements and that + the entire CRL/OCSP checking thing should undergo a + thorough review and probably redesign. */ + if ( !strcmp (p, oid_kp_ocspSigning)) + have_ocsp_signing = 1; + + if ((p = strchr (pend, '\n'))) + p++; + } + ksba_free (extkeyusages); + extkeyusages = NULL; + + if (!any_critical) + extusemask = ~0; /* Reset to the don't care mask. */ + } + + + err = ksba_cert_get_key_usage (cert, &use); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + { + err = 0; + if (opt.verbose && mode < 2) + log_info (_("no key usage specified - assuming all usages\n")); + use = ~0; + } + + /* Apply extKeyUsage. */ + use &= extusemask; + + } + if (err) + { + log_error (_("error getting key usage information: %s\n"), + gpg_strerror (err)); + ksba_free (extkeyusages); + return err; + } + + if (mode == 4) + { + if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) + return 0; + log_info (_("certificate should not have " + "been used for certification\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + + if (mode == 5) + { + if (use != ~0 + && (have_ocsp_signing + || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN + |KSBA_KEYUSAGE_CRL_SIGN)))) + return 0; + log_info (_("certificate should not have " + "been used for OCSP response signing\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + + if (mode == 6) + { + if ((use & (KSBA_KEYUSAGE_CRL_SIGN))) + return 0; + log_info (_("certificate should not have " + "been used for CRL signing\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + + if ((use & ((mode&1)? + (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): + (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + ) + return 0; + + log_info (mode==3? _("certificate should not have been used " + "for encryption\n"): + mode==2? _("certificate should not have been used for signing\n"): + mode==1? _("certificate is not usable for encryption\n"): + _("certificate is not usable for signing\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); +} + +/* Return 0 if the certificate CERT is usable for certification. */ +gpg_error_t +cert_use_cert_p (ksba_cert_t cert) +{ + return cert_usage_p (cert, 4); +} + +/* Return 0 if the certificate CERT is usable for signing OCSP + responses. */ +gpg_error_t +cert_use_ocsp_p (ksba_cert_t cert) +{ + return cert_usage_p (cert, 5); +} + +/* Return 0 if the certificate CERT is usable for signing CRLs. */ +gpg_error_t +cert_use_crl_p (ksba_cert_t cert) +{ + return cert_usage_p (cert, 6); +} diff --git a/dirmngr/validate.h b/dirmngr/validate.h new file mode 100644 index 0000000..0d9283c --- /dev/null +++ b/dirmngr/validate.h @@ -0,0 +1,55 @@ +/* validate.h - Certificate validation + * Copyright (C) 2004 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr 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. + * + * DirMngr 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 + */ + +#ifndef VALIDATE_H +#define VALIDATE_H + + +enum { + /* Simple certificate validation mode. */ + VALIDATE_MODE_CERT = 0, + /* Standard CRL issuer certificate validation; i.e. CRLs are not + considered for CRL issuer certificates. */ + VALIDATE_MODE_CRL = 1, + /* Full CRL validation. */ + VALIDATE_MODE_CRL_RECURSIVE = 2, + /* Validation as used for OCSP. */ + VALIDATE_MODE_OCSP = 3 +}; + + +/* Validate the certificate CHAIN up to the trust anchor. Optionally + return the closest expiration time in R_EXPTIME. */ +gpg_error_t validate_cert_chain (ctrl_t ctrl, + ksba_cert_t cert, ksba_isotime_t r_exptime, + int mode, char **r_trust_anchor); + +/* Return 0 if the certificate CERT is usable for certification. */ +gpg_error_t cert_use_cert_p (ksba_cert_t cert); + +/* Return 0 if the certificate CERT is usable for signing OCSP + responses. */ +gpg_error_t cert_use_ocsp_p (ksba_cert_t cert); + +/* Return 0 if the certificate CERT is usable for signing CRLs. */ +gpg_error_t cert_use_crl_p (ksba_cert_t cert); + + +#endif /*VALIDATE_H*/ diff --git a/dirmngr/w32-ldap-help.h b/dirmngr/w32-ldap-help.h new file mode 100644 index 0000000..566a346 --- /dev/null +++ b/dirmngr/w32-ldap-help.h @@ -0,0 +1,169 @@ +/* w32-ldap-help.h - Map utf8 based API into a wchar_t API. + * Copyright (C) 2010 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 3 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, see . + */ + +#ifndef W32_LDAP_HELP_H +#define W32_LDAP_HELP_H + +#ifndef HAVE_W32CE_SYSTEM +# error This is only required for W32CE. +#endif + + +static inline LDAP * +_dirmngr_ldap_init (const char *host, unsigned short port) +{ + LDAP *ld; + wchar_t *whost = NULL; + + if (host) + { + whost = utf8_to_wchar (host); + if (!whost) + return NULL; + } + ld = ldap_init (whost, port); + xfree (whost); + return ld; +} + + +static inline ULONG +_dirmngr_ldap_simple_bind_s (LDAP *ld, const char *user, const char *pass) +{ + ULONG ret; + wchar_t *wuser, *wpass; + + wuser = user? utf8_to_wchar (user) : NULL; + wpass = pass? utf8_to_wchar (pass) : NULL; + /* We can't easily map errnos to ldap_errno, thus we pass a NULL to + the function in the hope that the server will throw an error. */ + ret = ldap_simple_bind_s (ld, wuser, wpass); + xfree (wpass); + xfree (wuser); + return ret; +} + + +static inline ULONG +_dirmngr_ldap_search_st (LDAP *ld, const char *base, ULONG scope, + const char *filter, char **attrs, + ULONG attrsonly, struct timeval *timeout, + LDAPMessage **res) +{ + ULONG ret = LDAP_NO_MEMORY; + wchar_t *wbase = NULL; + wchar_t *wfilter = NULL; + wchar_t **wattrs = NULL; + int i; + + if (base) + { + wbase = utf8_to_wchar (base); + if (!wbase) + goto leave; + } + if (filter) + { + wfilter = utf8_to_wchar (filter); + if (!wfilter) + goto leave; + } + if (attrs) + { + for (i=0; attrs[i]; i++) + ; + wattrs = xtrycalloc (i+1, sizeof *wattrs); + if (!wattrs) + goto leave; + for (i=0; attrs[i]; i++) + { + wattrs[i] = utf8_to_wchar (attrs[i]); + if (!wattrs[i]) + goto leave; + } + } + + ret = ldap_search_st (ld, wbase, scope, wfilter, wattrs, attrsonly, + (struct l_timeval *)timeout, res); + + leave: + if (wattrs) + { + for (i=0; wattrs[i]; i++) + xfree (wattrs[i]); + xfree (wattrs); + } + xfree (wfilter); + xfree (wbase); + return ret; +} + + +static inline char * +_dirmngr_ldap_first_attribute (LDAP *ld, LDAPMessage *msg, BerElement **elem) +{ + wchar_t *wattr; + char *attr; + + wattr = ldap_first_attribute (ld, msg, elem); + if (!wattr) + return NULL; + attr = wchar_to_utf8 (wattr); + ldap_memfree (wattr); + return attr; +} + + +static inline char * +_dirmngr_ldap_next_attribute (LDAP *ld, LDAPMessage *msg, BerElement *elem) +{ + wchar_t *wattr; + char *attr; + + wattr = ldap_next_attribute (ld, msg, elem); + if (!wattr) + return NULL; + attr = wchar_to_utf8 (wattr); + ldap_memfree (wattr); + return attr; +} + +static inline BerValue ** +_dirmngr_ldap_get_values_len (LDAP *ld, LDAPMessage *msg, const char *attr) +{ + BerValue **ret; + wchar_t *wattr; + + if (attr) + { + wattr = utf8_to_wchar (attr); + if (!wattr) + return NULL; + } + else + wattr = NULL; + + ret = ldap_get_values_len (ld, msg, wattr); + xfree (wattr); + + return ret; +} + + +#endif /*W32_LDAP_HELP_H*/ diff --git a/doc/ChangeLog-2011 b/doc/ChangeLog-2011 new file mode 100644 index 0000000..b830c0e --- /dev/null +++ b/doc/ChangeLog-2011 @@ -0,0 +1,871 @@ +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-10-12 Werner Koch + + * gpg.texi: Add a bunch of opindex items. + + * yat2m.c (parse_file): Add hack to allow table indentation. + +2011-08-12 Werner Koch + + * texi.css: Override some elements. + * gnupg-log-tr.png: New. + * gnupg.texi: Use transparent logo. + +2011-03-01 Werner Koch + + * gpgsm.texi (CSR and certificate creation): New. + * gpg.texi (Unattended GPG key generation): New. + +2010-10-29 David Shaw + + * gpg.texi (GPG Configuration Options): Clarify that show-photos + doesn't work with --with-colons. --personal-digest-preferences + does not have a default any longer. + +2010-10-18 Werner Koch + + * DETAILS: Fix description of IMPORT_RES. Reported by Nicholas Cole. + +2010-10-11 Daniel Kahn Gillmor (wk) + + * gpg.texi (GPG Configuration Options) : Describe %v + and %V. + +2010-10-05 Werner Koch + + * Makefile.am (faq.txt faq.html, faq-online): New. + +2010-10-04 Werner Koch + + * faq.org: New. + * FAQ: Make it a static file with a pointer to the online location. + * Makefile.am (EXTRA_DIST): Remove faq.raw and faq.html. + (FAQ, faq.html): Remove these targets + +2010-09-28 Werner Koch + + * Makefile.am (AM_MAKEINFOFLAGS): Add define gpgtwoone. + +2010-09-28 David Shaw + + * gpg.texi (OpenPGP Options): Clarify that --force-v3-sigs + disables (not enables) v4 options. --force-v3-sigs defaults to + no. + +2010-08-18 Werner Koch + + * tools.texi (watchgnupg): Add examples section. + +2010-06-10 Werner Koch + + * Makefile.am (gnupg_TEXINFOS): Add dirmngr.texi. + (myman_sources): Ditto. + (myman_pages): Add dirmngr and dirmngr-client pages. + (noinst_MANS): Move gnupg.7 to man_MANS. + + * gnupg.texi: Include dirmngr.texi and add a menu entry. + * dirmngr.texi: New. Taken from the current SVN of the dirmngr + package and adjusted to fit into the GnuPG manual. Moved + dirmngr-cleint stuff to ... + * tools.texi (dirmngr-client): ... new. + +2009-11-18 Werner Koch + + * gpg.texi (GPG Key related Options): Describe + --skip-hidden-recipients. + +2009-10-19 David Shaw + + * gpg.texi (GPG Configuration Options): Clarify that ca-cert-file + is a generic store, the details of which depend on the underlying + libraries. + +2009-08-24 David Shaw + + * gpg.texi: Suggested new ordering for --edit-key. + +2009-08-17 David Shaw + + * gpg.texi (OpenPGP Options): Clarify that + personal-foo-preferences overrides recipient preferences (safely). + +2009-08-14 David Shaw + + * gpg.texi (GPG Configuration Options): Document keyserver options + check-cert and ca-cert-file. + +2009-08-06 Werner Koch + + * DETAILS: Describe the new INV_SNDR and NO_SNDR.. + +2009-07-31 David Shaw + + * gpg.texi (OpenPGP Options): Don't mention + --no-sk-comment (doesn't exist any longer). + +2009-07-23 David Shaw + + * gpg.texi (GPG Configuration Options): LDAP uses DNS-SD to locate + a server before falling back to keys.{domain}. + +2009-07-23 Werner Koch + + * help.txt (gpgsm.crl-problem): New. + +2009-07-22 Werner Koch + + * scdaemon.texi, instguide.texi, gpgsm.texi, sysnotes.texi + * glossary.texi, howto-create-a-server-cert.texi, tools.texi + * gpg-agent.texi, gpg.texi, debugging.texi: Typo fixes. Reported + by Jeroen Schot. Fixes bug#1093. + + * gpg.texi (GPG Configuration Options): Tell what files to backup. + * sysnotes.texi: Remove some warning notes for W32. + +2009-07-20 Werner Koch + + * gpg.texi (Operational GPG Commands): Add a note for --send-keys. + Fixes bug#1090. + +2009-07-06 Werner Koch + + * debugging.texi (Common Problems): Add a note about corrupted + keys in --search-keys. + +2009-06-02 Werner Koch + + * tools.texi (watchgnupg): Typo fix. Fixes bug#1065. + + * gpg-agent.texi (Agent Commands): Update description of --daemon. + +2009-05-20 Werner Koch + + * gpg.texi (GPG Configuration Options): Explain new meaning of + --enable-dsa2. + +2009-03-16 David Shaw + + * gpg.texi (GPG Configuration Options): Document keyserver-options + debug. + +2009-03-04 Werner Koch + + * help.txt (gpg.keygen.size): Add a link to web page. + +2009-03-03 Werner Koch + + * gpg.texi (Operational GPG Commands): "merge-only" is an + import-option. Reported by Joseph Oreste Bruni. + +2009-03-02 Werner Koch + + * gpg-agent.texi (Invoking GPG-AGENT): Modernized instructions. + (Agent Options): Fix spelling of option --lc-ctype. + +2009-01-12 Werner Koch + + * faq.raw: Fix bug reorting address. + +2008-12-12 Werner Koch + + * gpgsm.texi (General GPGSM Commands): Fix --help, --version and + --warranty wording. + +2008-12-08 Werner Koch + + * DETAILS: Clarify the use of "trust" and "validity" as suggested + by Daniel Kahn Gillmor. Fix some typos. Remove the outdated + sections on packet headers and pipemode. Point to the libgcrypt + manual for a description of the key generation. + +2008-11-12 Werner Koch + + * gpg-agent.texi (Agent Options): Use Posix $() instead of + backticks to avoid rendering problems. + +2008-10-13 Werner Koch + + * gpgsm.texi (Certificate Management): Explain hot to delete the + secret key. + +2008-10-01 Werner Koch + + * tools.texi (Controlling gpg-connect-agent): Describe /datafile. + +2008-09-23 David Shaw + + * gpg.texi (OpenPGP Key Management): Clarify setpref a bit. + +2008-08-30 Werner Koch + + * yat2m.c (write_th): Print a note that this is generated source. + (VERSION): Bump up to 1.0. + +2008-07-30 Werner Koch + + * gpgsm.texi (GPGSM Configuration): Mention com-cert.pem. + +2008-06-25 Werner Koch + + * qualified.txt: Add new BnetzA certs 12R and 13R. + * com-certs.pem: Ditto. + * examples/trustlist.txt: Ditto. + +2008-06-19 Werner Koch + + * tools.texi (Listing options): Describe new complect gpgconf type + "alias list". + +2008-06-16 Werner Koch + + * DETAILS (group): Document %ask-passphrase. + +2008-05-26 Werner Koch + + * gpgv.texi: Minor fixes. Fixes bug#918. + + * opt-homedir.texi: Typo fixes. Fixes bug#917. + +2008-05-26 Marcus Brinkmann + + * tools.texi (Invoking gpgconf): Document --list-dirs. + +2008-05-20 Marcus Brinkmann + + * tools.texi (Invoking gpgconf): Add --dry-run and --check-options. + (Checking programs): Document --check-options. + +2008-05-15 Marcus Brinkmann + + * gpg.texi (Operational GPG Commands): Mention the way to change + the default signing key. + +2008-05-06 Werner Koch + + * Makefile.am (myman_pages): Add gpg-zip.1. + + * tools.texi (gpg-zip): Add new section. + +2008-04-08 Werner Koch + + * gpg.texi (GPG Configuration Options): Change subkeys.pgp.net to + keys.gnupg.net. Describe --auto-key-locate mechanisms local and + nodefault. + +2008-04-03 Werner Koch + + * yat2m.c (proc_texi_cmd): Remove extra apostrophe from @samp and + use open and close quote to @file and @env. + +2008-04-02 Werner Koch + + * opt-homedir.texi: Remove special case for Registry key. + + * yat2m.c (proc_texi_cmd): Use the \(aq glyph for @samp. This is + bug#898. + (proc_texi_buffer): Handle backslashs correctly. + +2008-03-27 Werner Koch + + * Makefile.am (nobase_dist_doc_DATA, dist_html_DATA): New. Move + relevant files to here. + (install-html-local): Remove. + +2008-02-27 Marcus Brinkmann + + * tools.texi (Listing options): Document new types. + +2008-02-26 Werner Koch + + * gpg.texi (GPG Configuration Options): Mention rfc4398. + +2008-02-05 David Shaw + + * gpg.texi (GPG Esoteric Options): Tweak mention of Tempest font + to add a "claimed" in there. + +2008-01-29 Justin Pryzby (wk) + + * gpg-agent.texi (Agent Options): Grammar fixes + + * qualified.txt: Spelling fixes. + +2008-01-28 Justin Pryzby (wk) + + * gpg-agent.texi, yat2m.c, scdaemon.texi, qualified.txt + * tools.texi, gpgsm.texi: Typo fixes and minor grammer fixes. + +2008-01-10 Werner Koch + + * qualified.txt: Add missing country tag to the last entries. + Reported by Marcus Brinkmann. + +2008-01-10 Marcus Brinkmann + + * tools.texi (gpgconf): Some clarifications. + +2008-01-02 Werner Koch + + * gpg.texi (GPG Esoteric Options): Mention --log-file. + +2007-12-13 Werner Koch + + * qualified.txt: Add 2 root certs from S-Trust for 2008-2012. + * examples/trustlist.txt: Ditto. + * com-certs.pem: Ditto. + + * gpgsm.texi (Esoteric Options): Document --extra-digest-algo. + +2007-12-12 Werner Koch + + * gpg.texi: Typo fixes. From Christer Andersson. + +2007-12-04 Werner Koch + + * help.txt: New online help file. + * help.be.txt, help.ca.txt, help.cs.txt, help.da.txt, help.de.txt + * help.el.txt, help.eo.txt, help.es.txt, help.et.txt, help.fi.txt + * help.fr.txt, help.gl.txt, help.hu.txt, help.id.txt, help.it.txt + * help.ja.txt, help.nb.txt, help.pl.txt, help.pt.txt + * help.pt_BR.txt, help.ro.txt, help.ru.txt, help.sk.txt + * help.sv.txt, help.tr.txt, help.zh_CN.txt, help.zh_TW.txt: New + online file, generated from teh current po files. + * Makefile.am (dist_pkgdata_DATA): Add them. + +2007-11-19 Werner Koch + + * gpg.texi (GPG Configuration Options): English Grammar fix. + Thanks to Gerg Troxel. + + * gpgsm.texi (Certificate Options): Document + --auto-issuer-key-retrieve. + +2007-11-15 Werner Koch + + * gpg.texi (GPG Configuration): Add PINENTRY_USER_DATA. + + * gpg-agent.texi (Agent Options): Add xauthority. + +2007-10-31 Marcus Brinkmann + + * gpg-agent.texi (Agent Options): Fix typos, by Bernhard Reiter. + +2007-10-27 David Shaw + + * gpg.texi: Document --rfc4880 (the new --openpgp). + +2007-10-25 David Shaw + + * gpg.texi: Clarify --force-v3-sigs, --pgp2, and --pgp6 a bit. + +2007-10-23 Werner Koch + + * tools.texi (Listing global options): New. + +2007-10-19 Werner Koch + + * tools.texi (Controlling gpg-connect-agent): Updated. + +2007-08-29 Werner Koch + + * tools.texi (Checking programs): New. + +2007-08-27 Werner Koch + + * examples/pwpattern.list: New. + +2007-08-24 Werner Koch + + * debugging.texi (Common Problems): Add "A root certifciate does + not validate." + +2007-08-14 Werner Koch + + * glossary.texi (Glossary): Add a more items. + +2007-08-13 Werner Koch + + * yat2m.c (proc_texi_cmd): Do not put @samp content between two + newlines. + + * gpg-agent.texi (Agent Configuration): Explain the CM flag for + trustlist.txt. + +2007-08-09 Werner Koch + + * gpgsm.texi (Certificate Options): Describe --validation-model. + +2007-07-23 Werner Koch + + * scdaemon.texi (Scdaemon Commands): Remove obsolete --print-atr. + +2007-07-17 Werner Koch + + * gpgsm.texi (Input and Output): Document --default-key. + +2007-07-04 Werner Koch + + * gpl.texi: Updated to GPLv3. + +2007-06-22 Werner Koch + + * gpg.texi (Operational GPG Commands): Describe the flags used by + --check-sigs. + +2007-06-21 Werner Koch + + * gpgsm.texi (Certificate Management): Changed description of + --gen-key. + +2007-06-19 Werner Koch + + * glossary.texi (Glossary): Describe PSE. + +2007-06-18 Werner Koch + + * gpg-agent.texi (Agent GETINFO): New. + +2007-06-06 Werner Koch + + * Makefile.am (yat2m): Use a plain rule to build it for the sake + of cross-compiling. + + * yat2m.c (finish_page): Init SECT to NULL. + +2007-05-11 Werner Koch + + * gpgsm.texi (--export): Enhanced description. + +2007-05-09 Werner Koch + + * examples/gpgconf.conf: Remove active example line. + + * Makefile.am (online): Distinguish between released and svn manuals. + +2007-05-08 Werner Koch + + * howtos.texi: New. + * howto-create-a-server-cert.texi: New. + * Makefile.am (gnupg_TEXINFOS): Add new files. + + * gnupg.texi: Moved the logo for HTML more to the top. + * Makefile.am (install-html-local): New. + (DVIPS): Redefine to include srcdir. + +2007-05-04 Werner Koch + + * gnupg.texi (Top): Fix typo and a grammar issue. + * Makefile.am (EXTRA_DIST): Add gnupg-logo.png. Suggested by + Bernard Leak. + +2007-04-15 David Shaw + + * gpg.texi (OpenPGP Options): Update the personal-foo-preferences + documentation a bit. + +2007-04-10 Werner Koch + + * gpg.texi (GPG Configuration Options): Document --batch, no-tty, + --yes and --no. + +2007-03-08 Werner Koch + + * gnupg-logo.png, gnupg-logo.eps, gnupg-logo.pdf: New. + * gnupg-badge-openpgp.eps, gnupg-badge-openpgp.eps + * gnupg-badge-openpgp.jpg: Removed. + * gnupg.texi: Use new logo. + +2007-03-07 Werner Koch + + * tools.texi (applygnupgdefaults): New. + +2007-03-06 Werner Koch + + * examples/gpgconf.conf: New. + +2007-03-04 David Shaw + + * gpg.texi (GPG Esoteric Options): Document + --allow-multiple-messages. + +2007-02-26 Werner Koch + + * gpg.texi (GPG Configuration): Document envvar LANGUAGE. + (GPG Configuration Options): Document show-primary-uid-only. + +2007-02-18 Werner Koch + + * gpg.texi (GPG Esoteric Options): No card reader options for gpg2. + +2007-02-14 Werner Koch + + * gpg-agent.texi (Agent Options): Doc --pinentry-touch-file. + +2007-02-05 Werner Koch + + * debugging.texi (Common Problems): Tell how to export a private + key without a certificate. + +2007-01-30 Werner Koch + + * com-certs.pem: Added the current root certifcates of D-Trust and + S-Trust. + +2007-01-18 David Shaw + + * gpg.texi, specify-user-id.texi: Only some of the mentions of + exclamation marks have an example. Give examples to the rest. + +2007-01-17 David Shaw + + * gpg.texi (GPG Configuration Options): Make http_proxy option + documentation match reality. + (BUGS): Warn about hibernate/safe-sleep/etc writing main RAM to + disk, despite locking. + +2006-12-08 Werner Koch + + * gnupg.texi (direntry): Rename gpg to gpg2. + +2006-12-04 Werner Koch + + * gpgv.texi: New. + * tools.texi: Include new file. + +2006-12-02 David Shaw + + * gpg.texi (GPG Esoteric Options): Document --passphrase-repeat. + +2006-11-14 Werner Koch + + * gpgsm.texi (GPGSM EXPORT): Document changes. + +2006-11-11 Werner Koch + + * gnupg.texi (Top): Move gpg-agent part before gpg. + +2006-11-05 David Shaw + + * gpg.texi: Reference to --s2k-count in --s2k-mode. + +2006-10-30 Werner Koch + + * faq.raw: Minor corrections. + +2006-10-12 Werner Koch + + * Makefile.am (man_MANS): Do not install gnupg.7 due to a conflict + with gpg1. + +2006-10-12 David Shaw + + * gpg.texi: Document --s2k-count. + +2006-09-25 Werner Koch + + * gpg.texi (GPG Examples): Add markup to all options. This is + required to have the double dashs printed correclty. + +2006-09-22 Werner Koch + + * instguide.texi (Installation): New. + * assuan.texi (Assuan): Removed. Use the libassuan manual instead. + * gnupg.texi: Reflect these changes. + + * gpg.texi: Make some parts depend on the "gpgone" set + command. This allows us to use the same source for gpg1 and gpg2. + + * yat2m.c (parse_file): Better parsing of @ifset and ifclear. + (main): Allow definition of "-D gpgone". + (parse_file): Allow macro definitions. + (proc_texi_cmd): Expand macros. + (proc_texi_buffer): Process commands terminated by the closing + brace of the enclosing command. + +2006-09-20 Werner Koch + + * texi.css: New. Note that the current vesion of makeinfo has a + bug while copying the @import directive. A pacth has been send to + upstream. + +2006-09-19 Werner Koch + + * gpg.texi: Some restructuring. + + * Makefile.am (online): New target. + +2006-09-18 Werner Koch + + * com-certs.pem: New. + +2006-09-13 Werner Koch + + * gpg.texi (GPG Esoteric Options): Fixed typo in + --require-cross-certification and made it the default. + +2006-09-11 Werner Koch + + * HACKING: Cleaned up. + +2006-09-08 Werner Koch + + * yat2m.c (parse_file): Ignore @node lines immediately. + (proc_texi_cmd): No special @end ifset processing anymore. + + * specify-user-id.texi: New. Factored out of gpg.texi and ../README. + +2006-09-07 Werner Koch + + * scdaemon.texi (Scdaemon Configuration): New. + + * examples/scd-event: Event handler for sdaemon. + * examples/: New directory + +2006-08-22 Werner Koch + + * yat2m.c (parse_file): Added code to skip a line after @mansect. + + * gnupg7.texi: New. + +2006-08-21 Werner Koch + + * Makefile.am: Added other doc files from gpg 1.4. + +2006-08-17 Werner Koch + + * Makefile.am: Added rules to build man pages. + + * yat2m.c: New. + +2006-02-14 Werner Koch + + * gpgsm.texi (GPGSM Configuration): New section. + +2005-11-14 Werner Koch + + * qualified.txt: Added real information. + +2005-11-13 Werner Koch + + * qualified.txt: New. + * Makefile.am (dist_pkgdata_DATA): New. + +2005-08-16 Werner Koch + + * gpg-agent.texi (Agent Options): Note default file name for + --write-env-file. + +2005-06-03 Werner Koch + + * debugging.texi (Architecture Details): New section, mostly empty. + * gnupg-card-architecture.fig: New. + * Makefile.am: Rules to build png and eps versions. + + * gpg-agent.texi (Agent UPDATESTARTUPTTY): New. + +2005-05-17 Werner Koch + + * gpg-agent.texi (Agent Options): Removed --disable-pth. + +2005-04-27 Werner Koch + + * tools.texi (symcryptrun): Added. + + * scdaemon.texi: Removed OpenSC specific options. + +2005-04-20 Werner Koch + + * gpg-agent.texi (Agent Configuration): New section. + +2005-02-24 Werner Koch + + * tools.texi (gpg-connect-agent): New. + +2005-02-14 Werner Koch + + * gpgsm.texi (Certificate Management): Document --import. + +2005-01-27 Moritz Schulte + + * gpg-agent.texi: Document ssh-agent emulation layer. + +2005-01-04 Werner Koch + + * gnupg.texi: Updated to use @copying. + +2004-12-22 Werner Koch + + * gnupg.texi: Reordered. + * contrib.texi: Updated. + +2004-12-21 Werner Koch + + * tools.texi (gpg-preset-passphrase): New section. + + * gnupg-badge-openpgp.eps, gnupg-badge-openpgp.jpg: New + * gnupg.texi: Add a logo. + * sysnotes.texi: New. + +2004-11-05 Werner Koch + + * debugging.texi (Common Problems): Curses pinentry problem. + +2004-10-22 Werner Koch + + * tools.texi (Helper Tools): Document gpgsm-gencert.sh. + +2004-10-05 Werner Koch + + * gpg-agent.texi (Invoking GPG-AGENT): Tell that GPG_TTY needs to + be set in all cases. + +2004-09-30 Werner Koch + + * gpg.texi: New. + * gnupg.texi: Include gpg.texi + + * tools.texi: Add a few @command markups. + * gpgsm.texi: Ditto + * gpg-agent.texi: Ditto. + * scdaemon.texi: Ditto. + +2004-09-30 Marcus Brinkmann + + * tools.texi (Changing options): Add documentation for gpgconf. + + * contrib.texi (Contributors): Add two missing periods. + +2004-09-29 Werner Koch + + * gpgsm.texi (Configuration Options): Add --log-file. + + * gpg-agent.texi (Invoking GPG-AGENT): Add a few words about the + expected pinentry filename. + + Changed license of the manual stuff to GPL. + + * gnupg.texi (Top): New menu item Helper Tools. + + * tools.texi (Helper Tools): New. + * Makefile.am (gnupg_TEXINFOS): Add tools.texi. + +2004-08-05 Werner Koch + + * scdaemon.texi (Card applications): New section. + +2004-06-22 Werner Koch + + * glossary.texi: New. + +2004-06-18 Werner Koch + + * debugging.texi: New. + * gnupg.texi: Include it. + +2004-05-11 Werner Koch + + * gpgsm.texi (Esoteric Options): Add --debug-allow-core-dump. + +2004-05-03 Werner Koch + + * gpg-agent.texi (Agent Options): Add --allow-mark-trusted. + +2004-02-03 Werner Koch + + * contrib.texi (Contributors): Updated from the gpg 1.2.3 thanks + list. + * gpgsm.texi, gpg-agent.texi, scdaemon.texi: Language cleanups. + +2003-12-01 Werner Koch + + * gpgsm.texi (Certificate Options): Add --{enable,disable}-ocsp. + +2003-11-17 Werner Koch + + * scdaemon.texi (Scdaemon Options): Added --allow-admin and + --deny-admin. + +2003-10-27 Werner Koch + + * gpg-agent.texi (Agent GET_CONFIRMATION): New. + +2002-12-04 Werner Koch + + * gpg-agent.texi (Agent Signals): New. + +2002-12-03 Werner Koch + + * gpgsm.texi (Operational Commands): Add --passwd and + --call-protect-tool. + * gpg-agent.texi (Agent PASSWD): New + +2002-11-13 Werner Koch + + * gpg-agent.texi (Invoking GPG-AGENT): Tell about GPG_TTY. + +2002-11-12 Werner Koch + + * gpgsm.texi (Operational Commands): Add --call-dirmngr. + +2002-09-25 Werner Koch + + * gpg-agent.texi (Agent Options): Add --keep-tty and --keep-display. + +2002-09-12 Werner Koch + + * gpg-agent.texi (Invoking GPG-AGENT): Explained how to start only + one instance. + +2002-08-28 Werner Koch + + * gpg-agent.texi (Agent Options): Explained more options. + * scdaemon.texi (Scdaemon Options): Ditto. + +2002-08-09 Werner Koch + + * Makefile.am (gnupg_TEXINFOS): Include contrib.texi. + +2002-08-06 Werner Koch + + * gpgsm.texi: Added more options. + +2002-07-26 Werner Koch + + * assuan.texi: New. + * gpgsm.texi, scdaemon.texi, gpg-agent.texi: Documented the Assuan + protocol used. + +2002-07-22 Werner Koch + + * gnupg.texi, scdaemon.texi, gpg-agent.texi: New. + * contrib.texi, gpl.texi, fdl.texi: New. + * gpgsm.texi: Made this an include file for gnupg.texi. + * Makefile.am: Build gnupg.info instead of gpgsm.info. + +2002-06-04 Werner Koch + + * gpgsm.texi (Invocation): Described the various debug flags. + +2002-05-14 Werner Koch + + * Makefile.am, gpgsm.texi: New. + + Copyright 2002, 2004, 2005, 2006, 2007, 2008, 2010 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. + +Local Variables: +buffer-read-only: t +End: diff --git a/doc/DCO b/doc/DCO new file mode 100644 index 0000000..bae7bff --- /dev/null +++ b/doc/DCO @@ -0,0 +1,29 @@ +GnuPG Developer's Certificate of Origin. Version 1.0 +===================================================== + +By making a contribution to the GnuPG project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the free software license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the + best of my knowledge, is covered under an appropriate free + software license and I have the right under that license to + submit that work with modifications, whether created in whole + or in part by me, under the same free software license + (unless I am permitted to submit under a different license), + as indicated in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including + all personal information I submit with it, including my + sign-off) is maintained indefinitely and may be redistributed + consistent with this project or the free software license(s) + involved. + +Signed-off-by: [Your name and mail address] diff --git a/doc/DETAILS b/doc/DETAILS new file mode 100644 index 0000000..568500e --- /dev/null +++ b/doc/DETAILS @@ -0,0 +1,1442 @@ +# doc/DETAILS -*- org -*- +#+TITLE: GnuPG Details +# Globally disable superscripts and subscripts: +#+OPTIONS: ^:{} +# + +# Note: This file uses org-mode; it should be easy to read as plain +# text but be aware of some markup peculiarities: Verbatim code is +# enclosed in #+begin-example, #+end-example blocks or marked by a +# colon as the first non-white-space character, words bracketed with +# equal signs indicate a monospace font, and the usual /italics/, +# *bold*, and _underline_ conventions are recognized. + +This is the DETAILS file for GnuPG which specifies some internals and +parts of the external API for GPG and GPGSM. + +* Format of the colon listings + + The format is a based on colon separated record, each recods starts + with a tag string and extends to the end of the line. Here is an + example: +#+begin_example +$ gpg --with-colons --list-keys \ + --with-fingerprint --with-fingerprint wk@gnupg.org +pub:f:1024:17:6C7EE1B8621CC013:899817715:1055898235::m:::scESC: +fpr:::::::::ECAF7590EB3443B5C7CF3ACB6C7EE1B8621CC013: +uid:f::::::::Werner Koch : +uid:f::::::::Werner Koch : +sub:f:1536:16:06AD222CADF6A6E1:919537416:1036177416:::::e: +fpr:::::::::CF8BCC4B18DE08FCD8A1615906AD222CADF6A6E1: +sub:r:1536:20:5CE086B5B5A18FF4:899817788:1025961788:::::esc: +fpr:::::::::AB059359A3B81F410FCFF97F5CE086B5B5A18FF4: +#+end_example + +Note that new version of GnuPG or the use of certain options may add +new fields to the output. Parsers should not assume a limit on the +number of fields per line. Some fields are not yet used or only used +with certain record types; parsers should ignore fields they are not +aware of. New versions of GnuPG or the use of certain options may add +new types of records as well. Parsers should ignore any record whose +type they do not recognize for forward-compatibility. + +The double =--with-fingerprint= prints the fingerprint for the subkeys +too. Old versions of gpg used a slightly different format and required +the use of the option =--fixed-list-mode= to conform to the format +described here. + + +** Description of the fields +*** Field 1 - Type of record + + - pub :: Public key + - crt :: X.509 certificate + - crs :: X.509 certificate and private key available + - sub :: Subkey (secondary key) + - sec :: Secret key + - ssb :: Secret subkey (secondary key) + - uid :: User id + - uat :: User attribute (same as user id except for field 10). + - sig :: Signature + - rev :: Revocation signature + - fpr :: Fingerprint (fingerprint is in field 10) + - pkd :: Public key data [*] + - grp :: Keygrip + - rvk :: Revocation key + - tfs :: TOFU statistics [*] + - tru :: Trust database information [*] + - spk :: Signature subpacket [*] + - cfg :: Configuration data [*] + + Records marked with an asterisk are described at [[*Special%20field%20formats][*Special fields]]. + +*** Field 2 - Validity + + This is a letter describing the computed validity of a key. + Currently this is a single letter, but be prepared that additional + information may follow in some future versions. Note that GnuPG < + 2.1 does not set this field for secret key listings. + + - o :: Unknown (this key is new to the system) + - i :: The key is invalid (e.g. due to a missing self-signature) + - d :: The key has been disabled + (deprecated - use the 'D' in field 12 instead) + - r :: The key has been revoked + - e :: The key has expired + - - :: Unknown validity (i.e. no value assigned) + - q :: Undefined validity. '-' and 'q' may safely be treated as + the same value for most purposes + - n :: The key is not valid + - m :: The key is marginal valid. + - f :: The key is fully valid + - u :: The key is ultimately valid. This often means that the + secret key is available, but any key may be marked as + ultimately valid. + - w :: The key has a well known private part. + - s :: The key has special validity. This means that it might be + self-signed and expected to be used in the STEED system. + + If the validity information is given for a UID or UAT record, it + describes the validity calculated based on this user ID. If given + for a key record it describes the validity taken from the best + rated user ID. + + For X.509 certificates a 'u' is used for a trusted root + certificate (i.e. for the trust anchor) and an 'f' for all other + valid certificates. + +*** Field 3 - Key length + + The length of key in bits. + +*** Field 4 - Public key algorithm + + The values here are those from the OpenPGP specs or if they are + greather than 255 the algorithm ids as used by Libgcrypt. + +*** Field 5 - KeyID + + This is the 64 bit keyid as specified by OpenPGP and the last 64 + bit of the SHA-1 fingerprint of an X.509 certifciate. + +*** Field 6 - Creation date + + The creation date of the key is given in UTC. For UID and UAT + records, this is used for the self-signature date. Note that the + date is usually printed in seconds since epoch, however, we are + migrating to an ISO 8601 format (e.g. "19660205T091500"). This is + currently only relevant for X.509. A simple way to detect the new + format is to scan for the 'T'. Note that old versions of gpg + without using the =--fixed-list-mode= option used a "yyyy-mm-tt" + format. + +*** Field 7 - Expiration date + + Key or UID/UAT expiration date or empty if it does not expire. + +*** Field 8 - Certificate S/N, UID hash, trust signature info + + Used for serial number in crt records. For UID and UAT records, + this is a hash of the user ID contents used to represent that + exact user ID. For trust signatures, this is the trust depth + separated by the trust value by a space. + +*** Field 9 - Ownertrust + + This is only used on primary keys. This is a single letter, but + be prepared that additional information may follow in future + versions. For trust signatures with a regular expression, this is + the regular expression value, quoted as in field 10. + +*** Field 10 - User-ID + The value is quoted like a C string to avoid control characters + (the colon is quoted =\x3a=). For a "pub" record this field is + not used on --fixed-list-mode. A UAT record puts the attribute + subpacket count here, a space, and then the total attribute + subpacket size. In gpgsm the issuer name comes here. A FPR + record stores the fingerprint here. The fingerprint of a + revocation key is stored here. +*** Field 11 - Signature class + + Signature class as per RFC-4880. This is a 2 digit hexnumber + followed by either the letter 'x' for an exportable signature or + the letter 'l' for a local-only signature. The class byte of an + revocation key is also given here, 'x' and 'l' is used the same + way. This field if not used for X.509. + +*** Field 12 - Key capabilities + + The defined capabilities are: + + - e :: Encrypt + - s :: Sign + - c :: Certify + - a :: Authentication + - ? :: Unknown capability + + A key may have any combination of them in any order. In addition + to these letters, the primary key has uppercase versions of the + letters to denote the _usable_ capabilities of the entire key, and + a potential letter 'D' to indicate a disabled key. + +*** Field 13 - Issuer certificate fingerprint or other info + + Used in FPR records for S/MIME keys to store the fingerprint of + the issuer certificate. This is useful to build the certificate + path based on certificates stored in the local key database it is + only filled if the issuer certificate is available. The root has + been reached if this is the same string as the fingerprint. The + advantage of using this value is that it is guaranteed to have + been been build by the same lookup algorithm as gpgsm uses. + + For "uid" records this field lists the preferences in the same way + gpg's --edit-key menu does. + + For "sig" records, this is the fingerprint of the key that issued + the signature. Note that this is only filled in if the signature + verified correctly. Note also that for various technical reasons, + this fingerprint is only available if --no-sig-cache is used. + +*** Field 14 - Flag field + + Flag field used in the --edit menu output + +*** Field 15 - S/N of a token + + Used in sec/ssb to print the serial number of a token (internal + protect mode 1002) or a '#' if that key is a simple stub (internal + protect mode 1001). If the option --with-secret is used and a + secret key is available for the public key, a '+' indicates this. + +*** Field 16 - Hash algorithm + + For sig records, this is the used hash algorithm. For example: + 2 = SHA-1, 8 = SHA-256. + +*** Field 17 - Curve name + + For pub, sub, sec, and ssb records this field is used for the ECC + curve name. + +** Special fields + +*** PKD - Public key data + + If field 1 has the tag "pkd", a listing looks like this: +#+begin_example +pkd:0:1024:B665B1435F4C2 .... FF26ABB: + ! ! !-- the value + ! !------ for information number of bits in the value + !--------- index (eg. DSA goes from 0 to 3: p,q,g,y) +#+end_example + +*** TFS - TOFU statistics + + This field may follows a UID record to convey information about + the TOFU database. The information is similar to a TOFU_STATS + status line. + + - Field 2 :: tfs record version (must be 1) + - Field 3 :: validity - A number with validity code. + - Field 4 :: signcount - The number of signatures seen. + - Field 5 :: encrcount - The number of encryptions done. + - Field 6 :: policy - A string with the policy + - Field 7 :: signture-first-seen - a timestamp or 0 if not known. + - Field 8 :: signature-most-recent-seen - a timestamp or 0 if not known. + - Field 9 :: encryption-first-done - a timestamp or 0 if not known. + - Field 10 :: encryption-most-recent-done - a timestamp or 0 if not known. + +*** TRU - Trust database information + Example for a "tru" trust base record: +#+begin_example + tru:o:0:1166697654:1:3:1:5 +#+end_example + + - Field 2 :: Reason for staleness of trust. If this field is + empty, then the trustdb is not stale. This field may + have multiple flags in it: + + - o :: Trustdb is old + - t :: Trustdb was built with a different trust model + than the one we are using now. + + - Field 3 :: Trust model + + - 0 :: Classic trust model, as used in PGP 2.x. + - 1 :: PGP trust model, as used in PGP 6 and later. + This is the same as the classic trust model, + except for the addition of trust signatures. + + GnuPG before version 1.4 used the classic trust model + by default. GnuPG 1.4 and later uses the PGP trust + model by default. + + - Field 4 :: Date trustdb was created in seconds since Epoch. + - Field 5 :: Date trustdb will expire in seconds since Epoch. + - Field 6 :: Number of marginally trusted users to introduce a new + key signer (gpg's option --marginals-needed). + - Field 7 :: Number of completely trusted users to introduce a new + key signer. (gpg's option --completes-needed) + + - Field 8 :: Maximum depth of a certification chain. (gpg's option + --max-cert-depth) + +*** SPK - Signature subpacket records + + - Field 2 :: Subpacket number as per RFC-4880 and later. + - Field 3 :: Flags in hex. Currently the only two bits assigned + are 1, to indicate that the subpacket came from the + hashed part of the signature, and 2, to indicate the + subpacket was marked critical. + - Field 4 :: Length of the subpacket. Note that this is the + length of the subpacket, and not the length of field + 5 below. Due to the need for %-encoding, the length + of field 5 may be up to 3x this value. + - Field 5 :: The subpacket data. Printable ASCII is shown as + ASCII, but other values are rendered as %XX where XX + is the hex value for the byte. + +*** CFG - Configuration data + + --list-config outputs information about the GnuPG configuration + for the benefit of frontends or other programs that call GnuPG. + There are several list-config items, all colon delimited like the + rest of the --with-colons output. The first field is always "cfg" + to indicate configuration information. The second field is one of + (with examples): + + - version :: The third field contains the version of GnuPG. + + : cfg:version:1.3.5 + + - pubkey :: The third field contains the public key algorithms + this version of GnuPG supports, separated by + semicolons. The algorithm numbers are as specified in + RFC-4880. Note that in contrast to the --status-fd + interface these are _not_ the Libgcrypt identifiers. + Using =pubkeyname= prints names instead of numbers. + + : cfg:pubkey:1;2;3;16;17 + + - cipher :: The third field contains the symmetric ciphers this + version of GnuPG supports, separated by semicolons. + The cipher numbers are as specified in RFC-4880. + Using =ciphername= prints names instead of numbers. + + : cfg:cipher:2;3;4;7;8;9;10 + + - digest :: The third field contains the digest (hash) algorithms + this version of GnuPG supports, separated by + semicolons. The digest numbers are as specified in + RFC-4880. Using =digestname= prints names instead of + numbers. + + : cfg:digest:1;2;3;8;9;10 + + - compress :: The third field contains the compression algorithms + this version of GnuPG supports, separated by + semicolons. The algorithm numbers are as specified + in RFC-4880. + + : cfg:compress:0;1;2;3 + + - group :: The third field contains the name of the group, and the + fourth field contains the values that the group expands + to, separated by semicolons. + + For example, a group of: + : group mynames = paige 0x12345678 joe patti + would result in: + : cfg:group:mynames:patti;joe;0x12345678;paige + + - curve :: The third field contains the curve names this version + of GnuPG supports, separated by semicolons. Using + =curveoid= prints OIDs instead of numbers. + + : cfg:curve:ed25519;nistp256;nistp384;nistp521 + + +* Format of the --status-fd output + + Every line is prefixed with "[GNUPG:] ", followed by a keyword with + the type of the status line and some arguments depending on the type + (maybe none); an application should always be willing to ignore + unknown keywords that may be emitted by future versions of GnuPG. + Also, new versions of GnuPG may add arguments to existing keywords. + Any additional arguments should be ignored for forward-compatibility. + +** General status codes +*** NEWSIG [] + Is issued right before a signature verification starts. This is + useful to define a context for parsing ERROR status messages. + arguments are currently defined. If SIGNERS_UID is given and is + not "-" this is the percent escape value of the OpenPGP Signer's + User ID signature sub-packet. + +*** GOODSIG + The signature with the keyid is good. For each signature only one + of the codes GOODSIG, BADSIG, EXPSIG, EXPKEYSIG, REVKEYSIG or + ERRSIG will be emitted. In the past they were used as a marker + for a new signature; new code should use the NEWSIG status + instead. The username is the primary one encoded in UTF-8 and %XX + escaped. The fingerprint may be used instead of the long keyid if + it is available. This is the case with CMS and might eventually + also be available for OpenPGP. + +*** EXPSIG + The signature with the keyid is good, but the signature is + expired. The username is the primary one encoded in UTF-8 and %XX + escaped. The fingerprint may be used instead of the long keyid if + it is available. This is the case with CMS and might eventually + also be available for OpenPGP. + +*** EXPKEYSIG + The signature with the keyid is good, but the signature was made + by an expired key. The username is the primary one encoded in + UTF-8 and %XX escaped. The fingerprint may be used instead of the + long keyid if it is available. This is the case with CMS and + might eventually also be available for OpenPGP. + +*** REVKEYSIG + The signature with the keyid is good, but the signature was made + by a revoked key. The username is the primary one encoded in UTF-8 + and %XX escaped. The fingerprint may be used instead of the long + keyid if it is available. This is the case with CMS and might + eventually also beñ available for OpenPGP. + +*** BADSIG + The signature with the keyid has not been verified okay. The + username is the primary one encoded in UTF-8 and %XX escaped. The + fingerprint may be used instead of the long keyid if it is + available. This is the case with CMS and might eventually also be + available for OpenPGP. + +*** ERRSIG