From: Daniel Kahn Gillmor Date: Sat, 24 Dec 2016 20:39:04 +0000 (+0000) Subject: Import gnupg2_2.1.17.orig.tar.bz2 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=gnupg2.git;a=commitdiff_plain;h=05a815d61965daf3a01ffb3b50fdb8aa7c7df81e Import gnupg2_2.1.17.orig.tar.bz2 [dgit import orig gnupg2_2.1.17.orig.tar.bz2] --- 05a815d61965daf3a01ffb3b50fdb8aa7c7df81e 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 0000000..f2978f3 Binary files /dev/null and b/build-aux/speedo/w32/gnupg-logo-150x57.bmp differ diff --git a/build-aux/speedo/w32/gnupg-logo-164x314.bmp b/build-aux/speedo/w32/gnupg-logo-164x314.bmp new file mode 100644 index 0000000..bf270ac Binary files /dev/null and b/build-aux/speedo/w32/gnupg-logo-164x314.bmp differ diff --git a/build-aux/speedo/w32/inst-options.ini b/build-aux/speedo/w32/inst-options.ini new file mode 100644 index 0000000..8697e89 --- /dev/null +++ b/build-aux/speedo/w32/inst-options.ini @@ -0,0 +1,46 @@ +[Settings] +NumFields=5 + +; The number of the fields here is known in w32inst.nsi. +; The tags must be "[Field N]" with N=1..NumFields + +[Field 1] +Type=Label +Left=0 +Right=-1 +Top=0 +Bottom=20 + +[Field 2] +Type=Checkbox +Left=0 +Right=-1 +Top=30 +Bottom=40 +;Text=Start Menu +State=1 + +[Field 3] +Type=Checkbox +Left=0 +Right=-1 +Top=50 +Bottom=60 +;Text=Desktop +State=0 + +[Field 4] +Type=Checkbox +Left=0 +Right=-1 +Top=70 +Bottom=80 +;Text=Quick Launch Bar +State=0 + +[Field 5] +Type=Label +Left=0 +Right=-1 +Top=90 +Bottom=130 diff --git a/build-aux/speedo/w32/inst.nsi b/build-aux/speedo/w32/inst.nsi new file mode 100644 index 0000000..164e26b --- /dev/null +++ b/build-aux/speedo/w32/inst.nsi @@ -0,0 +1,1613 @@ +# inst.nsi - Installer for GnuPG on Windows. -*- coding: latin-1; -*- +# Copyright (C) 2005, 2014 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 . + +# 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 0000000..4c4bae0 Binary files /dev/null and b/common/gnupg.ico differ 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