[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#767706: unblock: debsig-verify/0.13



Hi!

On Thu, 2014-11-20 at 22:30:41 +0000, Jonathan Wiltshire wrote:
> Control: tag -1 moreinfo
> 
> On Sun, Nov 02, 2014 at 01:23:13AM +0100, Guillem Jover wrote:
> > Please unblock or reduce age-days for package debsig-verify so that it
> > can migrate before the freeze.
> > 
> > It fixes the code so that it now checks for errors returned by relevant
> > library and system calls, either directly or by switching to the more
> > tested code from libdpkg. This is particularly important given that
> > debsig-verify is one of the pieces in the packaging toolchain that
> > might be exposed to untrusted binary .deb packages. In addition this
> > version adds a new functional testsuite, which is run during the build.
> 
> Sorry you didn't get an answer to this sooner. To be honest, the size of
> the debdiff is rather off-putting.

Hmm, right, that's partly due to many files having moved around. The
debdiff should be equivalent to «git diff --shortstat 0.10..0.13»:

  52 files changed, 2260 insertions(+), 1701 deletions(-)

but with «git diff -C --shortstat 0.10..0.13» it becomes:

  37 files changed, 924 insertions(+), 389 deletions(-)

which is not *that* bad, but it's it's not small either.

> How bad would it be if this *wasn't* in Jessie?

I was actually expecting either a reduction of age-days or an unblock
before the freeze, or for it to not be accepted afterwards. The changed
code has been like the one in Jessie (more or less) for ages, so I don't
think it would be the end of the world if it continued that way for some
more years, although I think it would be better to have the new version.
But I can pretty much understand you being very much uncomfortable
accepting this amount of changes at this stage of the freeze.

I'm attaching the condensed git diff, in case you'd feel like taking a
look, otherwise just ignore it and go ahead closing this request.

Thanks,
Guillem
 .gitignore                                         |  19 ++
 ChangeLog                                          |  84 -------
 Makefile                                           |  61 -----
 Makefile.am                                        |  65 ++++++
 README                                             |  70 ++++++
 autogen                                            |   5 +
 configure.ac                                       |  64 +++++
 debian/.cvsignore                                  |   6 -
 debian/changelog                                   |  66 ++++++
 debian/control                                     |   7 +-
 debian/copyright                                   |   1 +
 debian/rules                                       |  62 ++++-
 {docs => doc}/TODO                                 |   0
 {docs => doc}/debsig-verify.1.in                   |  19 +-
 {docs => doc}/policy-syntax.txt                    |   0
 {docs => doc}/policy.dtd                           |   0
 get-version                                        |  41 ++++
 ar-parse.c => src/ar-parse.c                       |  61 +++--
 debsig-verify.c => src/debsig-verify.c             | 258 +++++++++++++--------
 debsig.h => src/debsig.h                           |  38 +--
 gpg-parse.c => src/gpg-parse.c                     | 140 +++++++----
 misc.c => src/misc.c                               |   9 +-
 xml-parse.c => src/xml-parse.c                     |  58 +++--
 test/.gitignore                                    |   4 +
 test/Makefile.am                                   |  41 ++++
 test/atlocal.in                                    |  68 ++++++
 {testing => test}/debs/sigtest1_1.0-1_all.deb      | Bin
 {testing => test}/debs/sigtest2_2.0-1_all.deb      | Bin
 test/debsig-cmd.at                                 |  11 +
 test/debsig-sig.at                                 |  36 +++
 .../keyrings/7CD73F641E04EC2D/debian-debsig.gpg    | Bin
 test/keyrings/FAD46790DE88C7E2/pubring.gpg         | Bin 0 -> 1245 bytes
 test/keyrings/FAD46790DE88C7E2/secring.gpg         | Bin 0 -> 2547 bytes
 .../policies/7CD73F641E04EC2D/generic.pol          |   0
 .../policies/7CD73F641E04EC2D/potato-release.pol   |   0
 .../policies/FAD46790DE88C7E2}/generic.pol         |   8 +-
 test/testsuite.at                                  |  11 +
 37 files changed, 924 insertions(+), 389 deletions(-)

diff --git a/.gitignore b/.gitignore
index fa38d3d..6688d50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,22 @@
+# Inherited ignores
 *.o
+*.log
+*.trs
+*~
+.deps/
+.dirstamp
+Makefile
+Makefile.in
+
+# Top dir ignores
+autom4te.cache/
+build-aux/
+build-tree/
+INSTALL
+configure
+config.*
+aclocal.m4
+stamp-h1
+
 debsig-verify
 debsig-verify.1
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index 8f347fe..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,84 +0,0 @@
-Fri Apr 27 13:53:32 EDT 2001  Ben Collins <bcollins@debian.org>
-
-  * debsig.h: Redefine ds_fail_printf with exit values, and macros. Also, add
-    __attributes__ to ds_printf so gcc will warn us on misuse
-  * ar-parse.c: Convert to new ds_fail_printf
-  * debsig-verify.c: ditto
-  * gpg-parse.c: ditto
-  * xml-parse.c: ditto
-  * docs/policy-syntax.txt: Update terms for handling policy selection
-
-Thu Mar  8 10:50:14 EST 2001  Ben Collins <bcollins@debian.org>
-
-  * gpg-parse.c (gpgVerify): Make sure we add NULL to the end of execl call.
-    Also add some better debug output.
-  * debsig-verify.c (main): Extra check to be sure policy files end in .pol.
-    Also add some better debug output.
-
-Tue Dec 12 17:39:55 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * Add a -d (debug) command line option.
-  * debsig-verify.1: Document it
-  * debsig.h: Add a DS_LEV define for it
-  * debsig-verify.c: Some better formatting, and use the DEBUG level for
-    some.
-  * .c: use the DEBUG level for lots of the function info
-
-Mon Dec 11 14:35:49 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * debsig-verify.c: main(): Better formatting for the
-    Selection/Verification verbose output.
-  * gpg-parse.c: getKeyID()/getSigKeyID(): Extra verbose output, plus
-    formatting.
-
-Mon Dec 11 11:18:56 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * xml-parse.c: increment depthPtr at start of function to get rid of the
-    goto
-  * debsig-verify.c: checkIsDeb(): new function, functionality moved from
-    main().
-
-Mon Dec 11 10:47:49 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * debsig-verify.c: outputUsage(): new function
-  * debsig-verify.c: main(): Use it. Also, handle argv[0] better
-    (prog_name).
-  * gpg-parse.c: gpgVerify(): do not pass --always-trust to gpg.
-
-Fri Dec  8 09:22:05 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * debsig.h:ds_fail_printf: new macro to print DS_LEV_ERR message and
-    exit(1). Used throughout now.
-  * xml-parse.c: Convert PARSE_ERROR macro to parse_error using proper
-    stdarg format.
-
-Thu Dec  7 21:28:40 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * xml-parse.c:clear_policy(): Used to free the memory used by the policy
-    struct. Call it after a parse error, and before start of parse.
-  * debsig-verify.c:main(): call clear_policy() if we fail, even though
-    parsing succeeded.
-
-Thu Dec  7 16:18:56 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * docs/debsig-verify.1.in: Move to here, and generate at build so we get
-    the right paths in the file.
-
-Thu Dec  7 13:30:43 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * docs/debsig-verify.1: new file, man page.
-
-Thu Dec  7 10:50:17 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * Added a --use-policy option, which takes the shortname of a policy
-    that was listed with --list-policies. Allows the user to specify the
-    policy they want to use.
-
-Thu Dec  7 02:24:50 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * debsig-verify.c: Added --list-policies option.
-
-Mon Dec  4 19:17:46 EST 2000  Ben Collins <bcollins@debian.org>
-
-  * Added parsing to pull out the keyID from a full name. E.g. "Name
-    <email@foo>" -> 28219193292JSAJALS
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 402a302..0000000
--- a/Makefile
+++ /dev/null
@@ -1,61 +0,0 @@
-CC = gcc
-CFLAGS = -Wall -g -O2
-
-#TESTING=1
-
-ifndef TESTING
-DEBSIG_KEYRINGS_DIR=/usr/share/debsig/keyrings
-DEBSIG_POLICIES_DIR=/etc/debsig/policies
-else
-DEBSIG_KEYRINGS_DIR=$(shell pwd)/testing/keyrings
-DEBSIG_POLICIES_DIR=$(shell pwd)/testing/policies
-endif
-
-PROGRAM = debsig-verify
-OBJS = xml-parse.o ar-parse.o gpg-parse.o debsig-verify.o misc.o
-
-MK_CPPFLAGS = \
-	-DLIBDPKG_VOLATILE_API=1 \
-	-DDEBSIG_POLICIES_DIR=\"$(DEBSIG_POLICIES_DIR)\" \
-	-DDEBSIG_KEYRINGS_DIR=\"$(DEBSIG_KEYRINGS_DIR)\"
-MK_CFLAGS = \
-	$(shell getconf LFS_CFLAGS) \
-	$(shell pkg-config --cflags libdpkg)
-MK_LDFLAGS = \
-	$(shell getconf LFS_LDFLAGS) \
-	$(shell pkg-config --libs libdpkg) \
-	-lxmlparse
-
-MANPAGES = debsig-verify.1
-
-all: $(PROGRAM) $(MANPAGES)
-
-$(PROGRAM): $(OBJS)
-	$(CC) $(MK_CFLAGS) $(CFLAGS) $(OBJS) $(MK_LDFLAGS) $(LDFLAGS) -o $@
-
-install: all
-	install -d -m755 $(DESTDIR)/usr/bin
-	install -m755 $(PROGRAM) $(DESTDIR)/usr/bin/$(PROGRAM)
-	install -d -m755 $(DESTDIR)$(DEBSIG_POLICIES_DIR)
-	install -d -m755 $(DESTDIR)$(DEBSIG_KEYRINGS_DIR)
-	for mpage in $(MANPAGES); do \
-		num=`echo $$mpage | sed 's,.*\.,,'`; \
-		install -d -m755 $(DESTDIR)/usr/share/man/man$$num; \
-		install $$mpage $(DESTDIR)/usr/share/man/man$$num/$$mpage; \
-	done
-
-check:
-	# Do not ship this in the tarball or repository.
-	ln -s /usr/share/keyrings/debian-keyring.gpg testing/keyrings/7CD73F641E04EC2D/
-	# XXX: Do some actual testing here.
-
-clean:
-	rm -f debsig-verify $(OBJS) $(MANPAGES)
-	rm -f testing/keyrings/7CD73F641E04EC2D/debian-keyring.gpg
-
-%.o: %.c debsig.h
-	$(CC) $(MK_CPPFLAGS) $(CPPFLAGS) $(MK_CFLAGS) $(CFLAGS) -c $< -o $@
-
-%.1: docs/%.1.in
-	sed -e 's,@POLICIES_DIR@,$(DEBSIG_POLICIES_DIR),g' \
-		-e 's,@KEYRINGS_DIR@,$(DEBSIG_KEYRINGS_DIR),g' < $< > $@
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..a97813f
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,65 @@
+SUBDIRS = . test
+
+DEBSIG_KEYRINGS_DIR = $(datadir)/debsig/keyrings
+DEBSIG_POLICIES_DIR = $(sysconfdir)/debsig/policies
+
+ACLOCAL_AMFLAGS = -I m4
+
+AM_CPPFLAGS = \
+	-I$(top_builddir) \
+	-DLIBDPKG_VOLATILE_API=1 \
+	-DDEBSIG_POLICIES_DIR=\"$(DEBSIG_POLICIES_DIR)\" \
+	-DDEBSIG_KEYRINGS_DIR=\"$(DEBSIG_KEYRINGS_DIR)\"
+AM_CFLAGS = \
+	$(LIBDPKG_CFLAGS) \
+	$(nil)
+LDADD = \
+	$(LIBDPKG_LIBS) \
+	-lxmlparse
+
+
+bin_PROGRAMS = src/debsig-verify
+
+src_debsig_verify_SOURCES = \
+	src/ar-parse.c \
+	src/debsig.h \
+	src/debsig-verify.c \
+	src/gpg-parse.c \
+	src/misc.c \
+	src/xml-parse.c \
+	$(nil)
+
+EXTRA_DIST = \
+	autogen \
+	get-version \
+	doc/TODO \
+	doc/debsig-verify.1.in \
+	doc/policy-syntax.txt \
+	doc/policy.dtd \
+	debian/changelog \
+	debian/compat \
+	debian/control \
+	debian/copyright \
+	debian/lintian-overrides \
+	debian/rules \
+	debian/source/format \
+	$(nil)
+
+do_man_subst = $(AM_V_GEN) \
+	sed -e 's,@POLICIES_DIR@,$(DEBSIG_POLICIES_DIR),g' \
+	    -e 's,@KEYRINGS_DIR@,$(DEBSIG_KEYRINGS_DIR),g'
+
+doc/%.1: doc/%.1.in
+	$(MKDIR_P) doc
+	$(do_man_subst) < $< > $@
+
+man_MANS = \
+	doc/debsig-verify.1 \
+	$(nil)
+
+install-data-local:
+	$(MKDIR_P) $(DESTDIR)$(DEBSIG_POLICIES_DIR)
+	$(MKDIR_P) $(DESTDIR)$(DEBSIG_KEYRINGS_DIR)
+
+clean-local:
+	$(RM) doc/debsig-verify.1
diff --git a/README b/README
new file mode 100644
index 0000000..305f11a
--- /dev/null
+++ b/README
@@ -0,0 +1,70 @@
+debsig-verify - Debian package signature verification tool
+
+This tool inspects and verifies binary package digital signatures based
+on predetermined policies, complementing repository signatures or allowing
+to verify the authenticity of a package even after download when detached
+from a repository.
+
+
+Releases
+--------
+
+The current legacy, stable and development releases can be found at:
+
+  <http://ftp.debian.org/debian/pool/main/d/debsig-verify/>
+
+For older releases check:
+
+  <http://snapshot.debian.org/package/debsig-verify/>
+
+
+Mailing List
+------------
+
+The subscription interface and web archives can be found at:
+
+  <https://lists.debian.org/debian-dpkg/>
+
+The mailing list address is (no subscription required to post):
+
+  debian-dpkg@lists.debian.org
+
+
+Source Repository
+-----------------
+
+  <https://anonscm.debian.org/cgit/dpkg/debsig-verify.git>
+  <git://anonscm.debian.org/dpkg/debsig-verify.git>
+
+
+Building from git source
+------------------------
+
+To prepare the debsig-verify source tree from git before starting the build
+process some required software needs to be installed:
+
+  GNU autoconf >= 2.60
+  GNU automake >= 1.11
+
+After installing the needed software, and running the following command on
+the git tree:
+
+  $ ./autogen
+
+the source should be roughly equivalent to the distributed tar source.
+
+
+Building from source
+--------------------
+
+The minimum software required to configure and build debsig-verify from a
+tarball is:
+
+  C89 compiler
+  GNU make
+  pkg-config
+  libdpkg-dev
+  libxmltok1-dev
+
+The build process is done by running the usual «./configure; make check».
+To see all available configuration options please run «./configure --help».
diff --git a/autogen b/autogen
new file mode 100755
index 0000000..dce49c4
--- /dev/null
+++ b/autogen
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+mkdir -p m4
+autoreconf -f -i
+rm -rf autom4te.cache
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..af34f52
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,64 @@
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.60])
+AC_INIT([debsig-verify], m4_esyscmd([./get-version]), [debian-dpkg@lists.debian.org])
+AC_CONFIG_SRCDIR([src/debsig-verify.c])
+AC_CONFIG_TESTDIR([test])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_AUX_DIR([build-aux])
+
+CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter"
+CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+CFLAGS="$CFLAGS -Wmissing-declarations"
+CFLAGS="$CFLAGS -Wmissing-format-attribute"
+CFLAGS="$CFLAGS -Wformat-security"
+CFLAGS="$CFLAGS -Wpointer-arith"
+CFLAGS="$CFLAGS -Wlogical-op"
+CFLAGS="$CFLAGS -Wvla"
+CFLAGS="$CFLAGS -Winit-self"
+CFLAGS="$CFLAGS -Wwrite-strings"
+CFLAGS="$CFLAGS -Wcast-align"
+CFLAGS="$CFLAGS -Wshadow"
+CFLAGS="$CFLAGS -Wdeclaration-after-statement"
+CFLAGS="$CFLAGS -Wnested-externs"
+CFLAGS="$CFLAGS -Wbad-function-cast"
+CFLAGS="$CFLAGS -Wstrict-prototypes"
+CFLAGS="$CFLAGS -Wmissing-prototypes"
+CFLAGS="$CFLAGS -Wold-style-definition"
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_SYS_LARGEFILE
+
+AM_INIT_AUTOMAKE([1.11 foreign nostdinc subdir-objects no-dist-gzip dist-xz])
+AM_SILENT_RULES([yes])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_MKDIR_P
+AM_MISSING_PROG([AUTOM4TE], [autom4te]) dnl Needed by autotest
+
+# Checks for libraries.
+AC_CHECK_LIB([xmlparse], [XML_ParserCreate])
+PKG_CHECK_MODULES([LIBDPKG], [libdpkg >= 1.17.14])
+
+# Checks for header files.
+AC_CHECK_HEADERS([stdlib.h string.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_FUNC_OBSTACK
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_STRNLEN
+AC_CHECK_FUNCS([memset strchr strerror strstr])
+
+AC_CONFIG_FILES([
+	Makefile
+	test/Makefile
+	test/atlocal
+])
+AC_CONFIG_HEADERS([config.h])
+AC_OUTPUT
diff --git a/debian/.cvsignore b/debian/.cvsignore
deleted file mode 100644
index 6239631..0000000
--- a/debian/.cvsignore
+++ /dev/null
@@ -1,6 +0,0 @@
-build-stamp
-tmp
-postinst.debhelper
-prerm.debhelper
-substvars
-files
diff --git a/debian/changelog b/debian/changelog
index 8967e8d..0c1d9ff 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,69 @@
+debsig-verify (0.13) unstable; urgency=medium
+
+  * Disable all current GnuPG warnings, as these do not concern us, because
+    we only use gpg for verification purposes, so we should not be handling
+    sensitive material anyway. This fixes failures in the testsuite on
+    GNU/Hurd due to unexpected output in stderr.
+
+ -- Guillem Jover <guillem@debian.org>  Tue, 28 Oct 2014 18:03:29 +0100
+
+debsig-verify (0.12) unstable; urgency=medium
+
+  * Merge the testsuite execution into the debian/rules build-arch target,
+    and use a build stamp file so that we do not invoke it from a binary
+    target. The latter is going to be run as root possibly via fakeroot,
+    and as GnuPG is set-uid-root on non-Linux systems, it fails there.
+  * Mark targets as .PHONY in debian/rules.
+  * Explicitly Build-Depend on gnupg for the testsuite.
+
+ -- Guillem Jover <guillem@debian.org>  Tue, 28 Oct 2014 06:24:28 +0100
+
+debsig-verify (0.11) unstable; urgency=medium
+
+  * Update Vcs-Browser git URL to the new cgit scheme.
+  * Add a README file.
+  * Autoconfiscate build system.
+  * Add more warning flags to the default compiler flags.
+  * Do not use continuation lines in string literals.
+  * Reformat and reflow --help output.
+  * Add a --root option to use an alternative root directory.
+    Thanks to Michael Vogt <mvo@ubuntu.com>. Closes: #758525
+  * Add new --policies-dir and --keyrings-dir options.
+  * Add new --help option.
+  * Do not print --version and --help on stderr and make them exit 0. And
+    replace usage error output with a new function that gives a hint to the
+    user to use --help instead.
+  * Add long options for quiet, verbose and debug.
+  * Use DS_LEV_ERR instead of DS_FAIL_INTERNAL as ds_printf() level argument.
+  * Use more of libdpkg instead of ad-hoc code, to reduce code duplication,
+    switch to more tested code, and so that the error return codes are
+    checked and acted upon. Closes: #758615
+    - Switch to use subproc module instead of fork() and waitpid().
+    - Switch from xmalloc to m_malloc().
+    - Use ohshit()/ohshite() instead of ds_fail_printf(DS_FAIL_INTERNAL, ...).
+    - Use m_dup2() instead of raw dup2().
+    - Use fdio API instead of ad-hoc file copying.
+    - Use str_match_end() instead of ad-hoc code, which also fixes a warning
+      due to a signed vs unsigned comparison.
+  * Remove useless return statements.
+  * Use a temporary GNUPGHOME instead of using the users's default.
+    Based on a patch by Michael Vogt <mvo@ubuntu.com>. Closes: #758826
+  * Error out if the GnuPG pipe failed on close.
+  * Explicitly check strcmp() return value instead of handling it as a bool.
+  * Switch originID from global to function scoped variable.
+    Thanks to Michael Vogt <mvo@ubuntu.com>.
+  * Switch deb and deb_fd from global to a function scoped struct.
+  * Change len type to size_t to fix a signed vs unsigned comparison warning.
+  * Make private functions static.
+  * Make private constant string variables static const.
+  * Add new autotest functional testsuite.
+  * Add test cases for signature checks.
+    Based on a patch by Michael Vogt <mvo@ubuntu.com>.
+  * Update copyright holders and years.
+  * Bump Standard-Version to 3.9.6 (no changed needed).
+
+ -- Guillem Jover <guillem@debian.org>  Tue, 28 Oct 2014 04:01:53 +0100
+
 debsig-verify (0.10) unstable; urgency=low
 
   * Add exit status codes to the man page.
diff --git a/debian/control b/debian/control
index 52302ab..b2bb5cb 100644
--- a/debian/control
+++ b/debian/control
@@ -3,10 +3,11 @@ Section: admin
 Priority: optional
 Maintainer: Dpkg Developers <debian-dpkg@lists.debian.org>
 Uploaders: Guillem Jover <guillem@debian.org>
-Vcs-Browser: http://anonscm.debian.org/gitweb/?p=dpkg/debsig-verify.git
+Vcs-Browser: https://anonscm.debian.org/cgit/dpkg/debsig-verify.git
 Vcs-Git: git://anonscm.debian.org/dpkg/debsig-verify.git
-Build-Depends: debhelper (>= 9), pkg-config, libdpkg-dev, libxmltok1-dev
-Standards-Version: 3.9.5
+Build-Depends: debhelper (>= 9), pkg-config, libdpkg-dev (>= 1.17.14),
+ libxmltok1-dev, gnupg
+Standards-Version: 3.9.6
 
 Package: debsig-verify
 Architecture: any
diff --git a/debian/copyright b/debian/copyright
index c64cb67..837e6eb 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -4,6 +4,7 @@ Upstream-Name: debsig-verify
 Files: *
 Copyright:
  Copyright © 2001 Ben Collins <bcollins@debian.org>
+ Copyright © 2009, 2014 Guillem Jover <guillem@debian.org>
 License: GPL-2+
  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
diff --git a/debian/rules b/debian/rules
index 1f6dfc7..94a0455 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,35 +5,79 @@ sourcedep_libdpkg_dev := \
 	        --showformat '$${source:Package} (= $${source:Version})' \
 	        --show libdpkg-dev)
 
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE))
+	confflags += --build=$(DEB_HOST_GNU_TYPE)
+else
+	confflags += --build=$(DEB_BUILD_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE)
+endif
+
+# Create configure script if necessary, automake handles rebuilding it.
+configure:
+	dh_testdir
+
+	./autogen
+
+# Configure the build tree
+build-tree/config.status: configure
+	dh_testdir
+
+	install -d build-tree
+	cd build-tree && ../configure $(confflags) \
+		$(shell dpkg-buildflags --export=configure) \
+		--prefix=/usr \
+		--mandir=\$${datadir}/man \
+		--sysconfdir=/etc \
+		--disable-silent-rules
+
 build: build-arch build-indep
 
 build-indep:
 
-build-arch:
+# XXX: We need to use a build stamp because the test suite uses GnuPG, which
+# is Set-Uid-Root on non-Linux systems, which breaks it when using also
+# fakeroot.
+build-arch: build-tree/build-arch-stamp
+build-tree/build-arch-stamp: build-tree/config.status
 	dh_testdir
-	$(MAKE) $(shell dpkg-buildflags --export=cmdline)
+
+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+	$(MAKE) -C build-tree VERBOSE=1 check
+else
+	$(MAKE) -C build-tree
+endif
+	touch $@
+
+.PHONY: build build-indep build-arch
 
 clean:
 	dh_testdir
-	$(MAKE) clean
+	[ ! -f Makefile ] ||  $(MAKE) distclean
+	rm -rf build-tree
 	dh_clean
 
-install: build
+install-arch: build-arch
 	dh_testdir
 	dh_testroot
 	dh_prep
 	dh_installdirs
 
-	$(MAKE) DESTDIR="$(CURDIR)/debian/debsig-verify" install
+	$(MAKE) -C build-tree DESTDIR="$(CURDIR)/debian/debsig-verify" install
+
+.PHONY: install-arch clean
 
 binary-indep:
 
-binary-arch: build install
+binary-arch: install-arch
 	dh_testdir
 	dh_testroot
 	dh_installchangelogs
-	dh_installdocs docs/policy-syntax.txt docs/policy.dtd
-	dh_installexamples testing/policies/*/*.pol
+	dh_installdocs doc/policy-syntax.txt doc/policy.dtd
+	dh_installexamples test/policies/*/*.pol
 	dh_installman
 	dh_installdeb
 	dh_lintian
@@ -47,3 +91,5 @@ binary-arch: build install
 	dh_builddeb
 
 binary: binary-indep binary-arch
+
+.PHONY: binary binary-indep binary-arch
diff --git a/docs/TODO b/doc/TODO
similarity index 100%
rename from docs/TODO
rename to doc/TODO
diff --git a/docs/debsig-verify.1.in b/doc/debsig-verify.1.in
similarity index 91%
rename from docs/debsig-verify.1.in
rename to doc/debsig-verify.1.in
index 833422c..3645298 100644
--- a/docs/debsig-verify.1.in
+++ b/doc/debsig-verify.1.in
@@ -1,4 +1,5 @@
 .\" Copyright © 2000 Ben Collins <bcollins@debian.org>
+.\" Copyright © 2014 Guillem Jover <guillem@debian.org>
 .\"
 .\" Covered under the GPL v2
 .\"
@@ -58,19 +59,22 @@ constrained. If these rules fail, the program exits with a non-zero
 status. If they pass, then it exits with a zero status.
 .SH OPTIONS
 .TP
-.BR \-q
+.BR \-q ", " \-\-quiet
 Causes the program to send no output, other than fatal errors. This is
 useful when being called from another program, where you rely on the exit
 value only.
 .TP
-.BR \-v
+.BR \-v ", " \-\-verbose
 Causes the program to send more output on execution, so as to follow the
 steps it is taking while trying to verify the \fIdeb\fR.
 .TP
-.BR \-d
+.BR \-d ", " \-\-debug
 Outputs even more info than the \fB\-v\fR option. This is mainly for
 debugging.
 .TP
+.BR \-\-help
+Outputs the usage information for the program.
+.TP
 .BR \-\-version
 Outputs the version information for the program. This includes the policy
 format version. This option does not require any other arguments.
@@ -90,6 +94,15 @@ just a file, and not a full path. You cannot specifiy arbitrary policies.
 This option is useful if more than one policy applies to potentially
 verifying the \fIdeb\fR. The program will then use this policy, and only
 this policy, to try and verify the \fIdeb\fR.
+.TP
+.BR \-\-policies\-dir " \fIdirectory\fP"
+Use a different directory when looking up for policies.
+.TP
+.BR \-\-keyrings\-dir " \fIdirectory\fP"
+Use a different directory when looking up for keyrings.
+.TP
+.BR \-\-root " \fIdirectory\fP"
+Use a different root directory when looking up for policies and keyrings.
 .SH EXIT STATUS
 .TP
 .B 0
diff --git a/docs/policy-syntax.txt b/doc/policy-syntax.txt
similarity index 100%
rename from docs/policy-syntax.txt
rename to doc/policy-syntax.txt
diff --git a/docs/policy.dtd b/doc/policy.dtd
similarity index 100%
rename from docs/policy.dtd
rename to doc/policy.dtd
diff --git a/get-version b/get-version
new file mode 100755
index 0000000..fd7b184
--- /dev/null
+++ b/get-version
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# get-version
+#
+# Copyright © 2009 Guillem Jover <guillem@debian.org>
+#
+# 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 <https://www.gnu.org/licenses/>.
+#
+
+if [ -f .dist-version ]; then
+  # Get the version from the file distributed in the tarball.
+  version=$(cat .dist-version)
+elif [ -d .git ]; then
+  # Get the version from the git repository. Since tags can't contain
+  # tildes, we use underscore instead. Reverse that switch here.
+  version=$(git describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/_/~/g')
+
+  # Check if we are on a dirty checkout.
+  git update-index --refresh -q >/dev/null
+  dirty=$(git diff-index --name-only HEAD 2>/dev/null)
+  if [ -n "$dirty" ]; then
+    version="$version-dirty"
+  fi
+else
+  echo "error: cannot get project version." 1>&2
+  exit 1
+fi
+
+# Use printf to avoid the trailing new line that m4_esyscmd would not handle.
+printf "$version"
diff --git a/ar-parse.c b/src/ar-parse.c
similarity index 65%
rename from ar-parse.c
rename to src/ar-parse.c
index 2283d25..47792ca 100644
--- a/ar-parse.c
+++ b/src/ar-parse.c
@@ -2,6 +2,7 @@
  * debsig-verify - Debian package signature verification tool
  *
  * Copyright © 2000 Ben Collins <bcollins@debian.org>
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,30 +22,41 @@
  * processes ar style archives (the format of a .deb package)
  */
 
+#include <config.h>
+
+#include <sys/types.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <assert.h>
 #include <errno.h>
 #include <ar.h>
 
+#include <dpkg/dpkg.h>
 #include <dpkg/ar.h>
+#include <dpkg/error.h>
+#include <dpkg/buffer.h>
+#include <dpkg/fdio.h>
 
 #include "debsig.h"
 
 /* This function takes a member name as an argument. It then goes through
  * the archive trying to find it. If it does, it returns the size of the
- * member's data, and leaves the deb_fs file pointer at the start of that
+ * member's data, and leaves the deb_fd file pointer at the start of that
  * data. Yes, we may have a zero length member in here somewhere, but
  * nothing important is going to be zero length anyway, so we treat it as
  * "non-existant".  */
 off_t
-findMember(const char *name)
+findMember(struct deb_archive *deb, const char *name)
 {
+    struct dpkg_error err;
     char magic[SARMAG+1];
     struct ar_hdr arh;
     off_t mem_len;
-    int len = strlen(name);
+    ssize_t r;
+    size_t len = strlen(name);
 
     if (len > sizeof(arh.ar_name)) {
 	ds_printf(DS_LEV_DEBUG, "findMember: '%s' is too long to be an archive member name",
@@ -53,34 +65,40 @@ findMember(const char *name)
     }
 
     /* This shouldn't happen, but... */
-    if (deb_fs == NULL)
-	ds_fail_printf(DS_FAIL_INTERNAL, "findMember: called while deb_fs == NULL");
+    if (deb->fd < 0)
+	ohshit("findMember: called while deb_fd < 0");
+
+    if (lseek(deb->fd, 0, SEEK_SET) < 0)
+	ohshit("findMember: cannot rewind package");
 
-    rewind(deb_fs);
+    r = fd_read(deb->fd, magic, SARMAG);
+    if (r < 0)
+	ohshite("findMember: failure to read package");
+    if (r != SARMAG)
+	ohshit("findMember: unexpected end of package");
 
-    if (!fgets(magic,sizeof(magic),deb_fs))
-	ds_fail_printf(DS_FAIL_INTERNAL, "findMember: failure to read package (%s)",
-		  strerror(errno));
+    magic[SARMAG] = '\0';
 
     /* We will fail in main() with this one */
-    if (strcmp(magic,ARMAG)) {
+    if (strcmp(magic, ARMAG) != 0) {
 	ds_printf(DS_LEV_DEBUG, "findMember: archive has bad magic");
 	return 0;
     }
 
-    while(!feof(deb_fs)) {
-	if (fread(&arh, 1, sizeof(arh),deb_fs) != sizeof(arh)) {
-	    if (ferror(deb_fs))
-		ds_fail_printf(DS_FAIL_INTERNAL, "findMember: error while parsing archive header (%s)",
-			  strerror(errno));
+    for (;;) {
+	r = fd_read(deb->fd, &arh, sizeof(arh));
+	if (r == 0)
 	    return 0;
-	}
+	if (r < 0)
+	    ohshite("findMember: error while parsing archive header");
+	if (r != sizeof(arh))
+	    ohshit("findMember: unexpected end of package");
 
 	if (dpkg_ar_member_is_illegal(&arh))
-	    ds_fail_printf(DS_FAIL_INTERNAL, "findMember: archive appears to be corrupt, fmag incorrect");
+	    ohshit("findMember: archive appears to be corrupt, fmag incorrect");
 
 	dpkg_ar_normalize_name(&arh);
-	mem_len = dpkg_ar_member_get_size(deb, &arh);
+	mem_len = dpkg_ar_member_get_size(deb->name, &arh);
 
 	/*
 	 * If all looks well, then we return the length of the member, and
@@ -96,10 +114,9 @@ findMember(const char *name)
 	    strnlen(arh.ar_name, sizeof(arh.ar_name)) == len)
 	    return mem_len;
 
-	/* fseek to the start of the next member, and try again */
-	if (fseek(deb_fs, mem_len + (mem_len & 1), SEEK_CUR) == -1 && ferror(deb_fs))
-	    ds_fail_printf(DS_FAIL_INTERNAL,
-			   "findMember: error during file seek (%s)", strerror(errno));
+	/* Skip to the start of the next member, and try again. */
+	if (fd_skip(deb->fd, mem_len + (mem_len & 1), &err) < 0)
+	    ohshit("findMember: error while skiping member data: %s", err.str);
     }
 
     /* well, nothing found, so let's pass on the bad news */
diff --git a/debsig-verify.c b/src/debsig-verify.c
similarity index 63%
rename from debsig-verify.c
rename to src/debsig-verify.c
index a09f534..f7e8b19 100644
--- a/debsig-verify.c
+++ b/src/debsig-verify.c
@@ -2,6 +2,7 @@
  * debsig-verify - Debian package signature verification tool
  *
  * Copyright © 2000 Ben Collins <bcollins@debian.org>
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,30 +22,41 @@
  * main routines
  */
 
+#include <config.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <fcntl.h>
 #include <dirent.h>
 
 #include <dpkg/dpkg.h>
+#include <dpkg/string.h>
+#include <dpkg/buffer.h>
 
 #include "debsig.h"
 
-char originID[2048];
+const char *rootdir = "";
 
-char *deb = NULL;
-FILE *deb_fs = NULL;
+const char *policies_dir = DEBSIG_POLICIES_DIR;
+const char *keyrings_dir = DEBSIG_KEYRINGS_DIR;
 
 #define CTAR(x) "control.tar" # x
 #define DTAR(x) "data.tar" # x
-char *ver_magic_member = "debian-binary";
-char *ver_ctrl_members[] = { CTAR(), CTAR(.gz), CTAR(.xz), 0 };
-char *ver_data_members[] = { DTAR(), DTAR(.gz), DTAR(.xz), DTAR(.bz2), DTAR(.lzma), 0 };
-
-static int checkSelRules(struct group *grp, const char *deb) {
+static const char ver_magic_member[] = "debian-binary";
+static const char *ver_ctrl_members[] = {
+	CTAR(), CTAR(.gz), CTAR(.xz), NULL
+};
+static const char *ver_data_members[] = {
+	DTAR(), DTAR(.gz), DTAR(.xz), DTAR(.bz2), DTAR(.lzma), NULL
+};
+
+static int
+checkSelRules(struct deb_archive *deb, const char *originID, struct group *grp)
+{
     int opt_count = 0;
     struct match *mtc;
     int len;
@@ -56,9 +68,9 @@ static int checkSelRules(struct group *grp, const char *deb) {
         /* If we have an ID for this match, check to make sure it exists, and
          * matches the signature we are about to check.  */
         if (mtc->id) {
-            char *m_id = getKeyID(mtc);
+            char *m_id = getKeyID(originID, mtc);
             char *d_id = getSigKeyID(deb, mtc->name);
-            if (m_id == NULL || d_id == NULL || strcmp(m_id, d_id))
+            if (m_id == NULL || d_id == NULL || strcmp(m_id, d_id) != 0)
                 return 0;
         }
 
@@ -67,7 +79,7 @@ static int checkSelRules(struct group *grp, const char *deb) {
 	 * specified, don't we?
 	 */
 
-        len = checkSigExist(mtc->name);
+        len = checkSigExist(deb, mtc->name);
 
         /* If the member exists and we reject it, fail now. Also, if it
          * doesn't exist, and we require it, fail as well. */
@@ -92,23 +104,10 @@ static int checkSelRules(struct group *grp, const char *deb) {
     return 1;
 }
 
-static off_t
-passthrough(FILE *in, FILE *out, off_t len)
+static int
+verifyGroupRules(struct deb_archive *deb, const char *originID, struct group *grp)
 {
-    char buf[2048];
-    int t;
-
-    while (len > 0) {
-        t = fread(buf, 1, sizeof(buf), in);
-        fwrite(buf, 1, (t > len) ? len : t, out);
-        len -= t;
-    }
-
-    return len;
-}
-
-static int verifyGroupRules(struct group *grp, const char *deb) {
-    FILE *fp;
+    struct dpkg_error err;
     char tmp_sig[32] = {'\0'}, tmp_data[32] = {'\0'};
     int opt_count = 0, t, i, fd;
     struct match *mtc;
@@ -122,7 +121,7 @@ static int verifyGroupRules(struct group *grp, const char *deb) {
 
     /* Go ahead and write out our data to a temp file */
     strncpy(tmp_data, "/tmp/debsig-data.XXXXXX", sizeof(tmp_data));
-    if ((fd = mkstemp(tmp_data)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
+    if ((fd = mkstemp(tmp_data)) == -1) {
 	ds_printf(DS_LEV_ERR, "error creating temp file %s: %s\n",
 		  tmp_data, strerror(errno));
 	if (fd != -1) {
@@ -134,29 +133,38 @@ static int verifyGroupRules(struct group *grp, const char *deb) {
 
     /* Now, let's find all the members we need to check and cat them into a
      * single temp file. This is what we pass to gpg.  */
-    if (!(len = findMember(ver_magic_member)))
+    if (!(len = findMember(deb, ver_magic_member)))
         goto fail_and_close;
-    len = passthrough(deb_fs, fp, len);
+    len = fd_fd_copy(deb->fd, fd, len, &err);
+    if (len < 0)
+	ohshit("verifyGroupRules: cannot copy to temp file: %s", err.str);
 
     for (i = 0; ver_ctrl_members[i]; i++) {
-	if (!(len = findMember(ver_ctrl_members[i])))
+	len = findMember(deb, ver_ctrl_members[i]);
+	if (!len)
 	    continue;
-	len = passthrough(deb_fs, fp, len);
+	len = fd_fd_copy(deb->fd, fd, len, &err);
+	if (len < 0)
+	    ohshit("verifyGroupRules: cannot copy to temp file: %s", err.str);
 	break;
     }
     if (!ver_ctrl_members[i])
 	goto fail_and_close;
 
     for (i = 0; ver_data_members[i]; i++) {
-	if (!(len = findMember(ver_data_members[i])))
+	len = findMember(deb, ver_data_members[i]);
+	if (!len)
 	    continue;
-	len = passthrough(deb_fs, fp, len);
+	len = fd_fd_copy(deb->fd, fd, len, &err);
+	if (len < 0)
+	    ohshit("verifyGroupRules: cannot copy to temp file: %s", err.str);
 	break;
     }
     if (!ver_data_members[i])
 	goto fail_and_close;
 
-    fclose(fp);
+    if (close(fd))
+        ohshite("error closing temp file %s", tmp_data);
     fd = -1;
 
     for (mtc = grp->matches; mtc; mtc = mtc->next) {
@@ -166,14 +174,14 @@ static int verifyGroupRules(struct group *grp, const char *deb) {
 	/* If we have an ID for this match, check to make sure it exists, and
 	 * matches the signature we are about to check.  */
 	if (mtc->id) {
-	    char *m_id = getKeyID(mtc);
+	    char *m_id = getKeyID(originID, mtc);
 	    char *d_id = getSigKeyID(deb, mtc->name);
-	    if (m_id == NULL || d_id == NULL || strcmp(m_id, d_id))
+	    if (m_id == NULL || d_id == NULL || strcmp(m_id, d_id) != 0)
 		goto fail_and_close;
 	}
 
-	/* This will also position deb_fs to the start of the member */
-	len = checkSigExist(mtc->name);
+	/* This will also position deb->fd to the start of the member. */
+	len = checkSigExist(deb, mtc->name);
 
 	/* If the member exists and we reject it, die now. Also, if it
 	 * doesn't exist, and we require it, die as well. */
@@ -187,17 +195,21 @@ static int verifyGroupRules(struct group *grp, const char *deb) {
 
 	/* let's get our temp file */
 	strncpy(tmp_sig, "/tmp/debsig-sig.XXXXXX", sizeof(tmp_sig));
-	if ((fd = mkstemp(tmp_sig)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
+	if ((fd = mkstemp(tmp_sig)) == -1) {
 	    ds_printf(DS_LEV_ERR, "error creating temp file %s: %s\n",
 		      tmp_sig, strerror(errno));
 	    goto fail_and_close;
 	}
 
-	len = passthrough(deb_fs, fp, len);
-	fclose(fp);
+	len = fd_fd_copy(deb->fd, fd, len, &err);
+	if (len < 0)
+	    ohshit("verifyGroupRules: cannot copy to temp file: %s", err.str);
+
+	if (close(fd) < 0)
+	    ohshit("error closing temp file %s", tmp_sig);
 
 	/* Now, let's check with gpg on this one */
-	t = gpgVerify(tmp_data, mtc, tmp_sig);
+	t = gpgVerify(originID, mtc, tmp_data, tmp_sig);
 
 	fd = -1;
 	unlink(tmp_sig);
@@ -232,17 +244,19 @@ fail_and_close:
     return 0;
 }
 
-static int checkIsDeb(void) {
+static int
+checkIsDeb(struct deb_archive *deb)
+{
     int i;
     const char *member;
 
-    if (!findMember(ver_magic_member)) {
+    if (!findMember(deb, ver_magic_member)) {
        ds_printf(DS_LEV_VER, "Missing archive magic member %s", ver_magic_member);
        return 0;
     }
 
     for (i = 0; (member = ver_ctrl_members[i]); i++)
-        if (findMember(member))
+        if (findMember(deb, member))
             break;
     if (!member) {
         ds_printf(DS_LEV_VER, "Missing archive control member, checked:");
@@ -252,7 +266,7 @@ static int checkIsDeb(void) {
     }
 
     for (i = 0; (member = ver_data_members[i]); i++)
-        if (findMember(member))
+        if (findMember(deb, member))
             break;
     if (!member) {
         ds_printf(DS_LEV_VER, "Missing archive data member, checked:");
@@ -265,47 +279,56 @@ static int checkIsDeb(void) {
 }
 
 static void outputVersion(void) {
-    fprintf(stderr, "\
-Debsig Program Version - "VERSION"\n\
-  Signature Version - "SIG_VERSION"\n\
-  Signature Namespace - "DEBSIG_NAMESPACE"\n\
-  Policies Directory - "DEBSIG_POLICIES_DIR"\n\
-  Keyrings Directory - "DEBSIG_KEYRINGS_DIR"\n");
-    return;
+    printf(
+"Debsig Program Version - "VERSION"\n"
+"  Signature Version - "SIG_VERSION"\n"
+"  Signature Namespace - "DEBSIG_NAMESPACE"\n"
+"  Policies Directory - "DEBSIG_POLICIES_DIR"\n"
+"  Keyrings Directory - "DEBSIG_KEYRINGS_DIR"\n");
+}
+
+static void
+outputBadUsage(void)
+{
+    ohshit("Use --help for program usage information.");
 }
 
 static void outputUsage(void) {
-        fprintf(stderr, "\
-Usage: %s [ options ] <deb>\n\n\
-   -q                  Quiet, only output fatal errors\n\
-   -v                  Verbose output (mainly debug)\n\
-   -d                  Debug output as well\n\
-   --version           Output version info, and exit\n\
-   --list-policies     Only list policies that can be used to\n\
-                       validate this sig. This runs through\n\
-                       'Selection' block of the policies only.\n\
-   --use-policy <name> Used in conjunction with the above\n\
-                       option. This allows you to specify the\n\
-                       short name of the policy you wish to try.\n",
-	dpkg_get_progname());
-        exit(1);
+    printf("Usage: %s [<option>...] <deb>\n\n", dpkg_get_progname());
+
+    printf(
+"Options:\n"
+"  -q, --quiet              Quiet, only output fatal errors.\n"
+"  -v, --verbose            Verbose output (mainly debug).\n"
+"  -d, --debug              Debug output as well.\n"
+"      --list-policies      Only list policies that can be used to validate\n"
+"                             this sig. Only runs through 'Selection' block.\n"
+"      --use-policy <name>  Specify the short policy name to use.\n"
+"      --policies-dir <dir> Use an alternative policies directory.\n"
+"      --keyrings-dir <dir> Use an alternative keyrings directory.\n"
+"      --root <dir>         Use an alternative root directory for policy lookup.\n"
+"      --help               Output usage info, and exit.\n"
+"      --version            Output version info, and exit.\n"
+);
 }
 
-void
+static void
 ds_catch_fatal_error(void)
 {
     pop_error_context(ehflag_bombout);
     exit(DS_FAIL_INTERNAL);
 }
 
-void
+static void
 ds_print_fatal_error(const char *emsg, const void *data)
 {
-    ds_printf(DS_FAIL_INTERNAL, "%s", emsg);
+    ds_printf(DS_LEV_ERR, "%s", emsg);
 }
 
 int main(int argc, char *argv[]) {
+    struct deb_archive deb = { .name = NULL, .fd = -1, };
     struct policy *pol = NULL;
+    char originID[2048];
     char buf[8192], pol_file[8192], *tmpID, *force_file = NULL;
     DIR *pd = NULL;
     struct dirent *pd_ent;
@@ -316,63 +339,93 @@ int main(int argc, char *argv[]) {
 
     push_error_context_func(ds_catch_fatal_error, ds_print_fatal_error, NULL);
 
-    if (argc < 2)
-	outputUsage();
+    if (argc < 2) {
+	ds_printf(DS_LEV_ERR, "missing <deb> filename argument");
+	outputBadUsage();
+    }
 
     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
-	if (!strcmp(argv[i], "-q"))
-	    ds_debug_level = DS_LEV_ERR;
-	else if (!strcmp(argv[i], "-v"))
+	if (strcmp(argv[i], "-v") == 0|| strcmp(argv[i], "--verbose") == 0)
 	    ds_debug_level = DS_LEV_VER;
-	else if (!strcmp(argv[i], "-d"))
+	else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0)
+	    ds_debug_level = DS_LEV_ERR;
+	else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0)
 	    ds_debug_level = DS_LEV_DEBUG;
-	else if (!strcmp(argv[i], "--version")) {
-	    outputVersion();
+	else if (strcmp(argv[i], "--version") == 0) {
 	    /* Make sure we exit non-zero if there are any more args. This
 	     * makes sure someone doesn't do something stupid like pass
 	     * --version and a .deb, and expect it to return a validation
 	     * exit status.  */
-	    if (argc > 2)
+	    if (argc > 2) {
+		ds_printf(DS_LEV_ERR, "--version accepts no arguments");
 		exit(1);
-	    else
-		exit(0);
-	} else if (!strcmp(argv[i], "--list-policies")) {
+	    }
+
+	    outputVersion();
+	    exit(0);
+	} else if (strcmp(argv[i], "--help") == 0) {
+	    outputUsage();
+	    exit(0);
+	} else if (strcmp(argv[i], "--list-policies") == 0) {
 	    /* Just create a list of policies we can use */
 	    list_only = 1;
 	    ds_printf(DS_LEV_ALWAYS, "Listing usable policies");
-	} else if (!strcmp(argv[i], "--use-policy")) {
+	} else if (strcmp(argv[i], "--use-policy") == 0) {
 	    /* We take one arg */
 	    force_file = argv[++i];
 	    if (i == argc || force_file[0] == '-') {
 		ds_printf(DS_LEV_ERR, "--use-policy requires an argument");
+		outputBadUsage();
+	    }
+	} else if (strcmp(argv[i], "--policies-dir") == 0) {
+	    policies_dir = argv[++i];
+	    if (i == argc || policies_dir[0] == '-') {
+		ds_printf(DS_LEV_ERR, "--policies-dir requires an argument");
 		outputUsage();
 	    }
-	} else
+	} else if (strcmp(argv[i], "--keyrings-dir") == 0) {
+	    keyrings_dir = argv[++i];
+	    if (i == argc || keyrings_dir[0] == '-') {
+		ds_printf(DS_LEV_ERR, "--keyrings-dir requires an argument");
+		outputUsage();
+	    }
+	} else if (strcmp(argv[i], "--root") == 0) {
+	    rootdir = argv[++i];
+	    if (i == argc || rootdir[0] == '-') {
+		ds_printf(DS_LEV_ERR, "--root requires an argument");
+		outputBadUsage();
+	    }
+	} else {
+	    ds_printf(DS_LEV_ERR, "unknown argument");
 	    outputUsage();
+	    exit(1);
+	}
     }
 
-    if (i + 1 != argc) /* There should only be one arg left */
-	outputUsage();
-
-    deb = argv[i];
+    /* There should only be one arg left. */
+    if (i + 1 != argc) {
+	ds_printf(DS_LEV_ERR, "too many arguments");
+	outputBadUsage();
+    }
 
-    if ((deb_fs = fopen(deb, "r")) == NULL)
-	ds_fail_printf(DS_FAIL_INTERNAL, "could not open %s (%s)", deb, strerror(errno));
+    deb.name = argv[i];
+    deb.fd = open(deb.name, O_RDONLY);
+    if (deb.fd < 0)
+	ohshite("could not open %s", deb.name);
 
     if (!list_only)
-	ds_printf(DS_LEV_VER, "Starting verification for: %s", deb);
+	ds_printf(DS_LEV_VER, "Starting verification for: %s", deb.name);
 
-    if (!checkIsDeb())
-	ds_fail_printf(DS_FAIL_INTERNAL, "%s does not appear to be a deb format package", deb);
+    if (!checkIsDeb(&deb))
+	ohshit("%s does not appear to be a deb format package", deb.name);
 
-    if ((tmpID = getSigKeyID(deb, "origin")) == NULL)
+    if ((tmpID = getSigKeyID(&deb, "origin")) == NULL)
 	ds_fail_printf(DS_FAIL_NOSIGS, "Origin Signature check failed. This deb might not be signed.\n");
 
     strncpy(originID, tmpID, sizeof(originID));
 
     /* Now we have an ID, let's check the policy to use */
-
-    snprintf(buf, sizeof(buf) - 1, DEBSIG_POLICIES_DIR_FMT, originID);
+    snprintf(buf, sizeof(buf) - 1, "%s%s/%s", rootdir, policies_dir, originID);
     if ((pd = opendir(buf)) == NULL)
 	ds_fail_printf(DS_FAIL_UNKNOWN_ORIGIN,
 		       "Could not open Origin dir %s: %s\n", buf, strerror(errno));
@@ -383,12 +436,11 @@ int main(int argc, char *argv[]) {
 	ds_printf(DS_LEV_ALWAYS, "  Policies in: %s", buf);
 
     while ((pd_ent = readdir(pd)) != NULL && (pol == NULL || list_only)) {
-	char *ext = strstr(pd_ent->d_name, ".pol");
 	/* Make sure we have the right name format */
-	if (ext == NULL || (ext - pd_ent->d_name) + 4 != strlen(pd_ent->d_name))
+	if (!str_match_end(pd_ent->d_name, ".pol"))
 	    continue;
 
-	if (force_file != NULL && strcmp(pd_ent->d_name, force_file))
+	if (force_file != NULL && strcmp(pd_ent->d_name, force_file) != 0)
 	    continue;
 
 	/* Now try to parse the file */
@@ -401,7 +453,7 @@ int main(int argc, char *argv[]) {
 	/* Now let's see if this policy's selection is useful for this .deb  */
 	ds_printf(DS_LEV_VER, "    Checking Selection group(s).");
 	for (grp = pol->sels; grp != NULL; grp = grp->next) {
-	    if (!checkSelRules(grp, deb)) {
+	    if (!checkSelRules(&deb, originID, grp)) {
 		clear_policy();
 		ds_printf(DS_LEV_VER, "    Selection group failed checks.");
 		pol = NULL;
@@ -433,9 +485,9 @@ int main(int argc, char *argv[]) {
     ds_printf(DS_LEV_VER, "    Checking Verification group(s).");
 
     for (grp = pol->vers; grp; grp = grp->next) {
-	if (!verifyGroupRules(grp, deb)) {
+	if (!verifyGroupRules(&deb, originID, grp)) {
 	    ds_printf(DS_LEV_VER, "    Verification group failed checks.");
-	    ds_fail_printf(DS_FAIL_BADSIG, "Failed verification for %s.", deb);
+	    ds_fail_printf(DS_FAIL_BADSIG, "Failed verification for %s.", deb.name);
 	}
     }
 
diff --git a/debsig.h b/src/debsig.h
similarity index 75%
rename from debsig.h
rename to src/debsig.h
index 3c492b0..5942993 100644
--- a/debsig.h
+++ b/src/debsig.h
@@ -2,6 +2,7 @@
  * debsig-verify - Debian package signature verification tool
  *
  * Copyright © 2000 Ben Collins <bcollins@debian.org>
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,14 +18,13 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#define DEBSIG_POLICIES_DIR_FMT DEBSIG_POLICIES_DIR"/%s"
-#define DEBSIG_KEYRINGS_FMT DEBSIG_KEYRINGS_DIR"/%s/%s"
-
 #define GPG_PROG "/usr/bin/gpg"
 
 /* This is so ugly, but easy */
-#define GPG_ARGS_FMT "%s %s %s"
-#define GPG_ARGS "--no-options", "--no-default-keyring", "--batch"
+#define GPG_ARGS_FMT "%s %s %s %s %s %s"
+#define GPG_ARGS "--no-options", "--no-default-keyring", "--batch", \
+                 "--no-secmem-warning", "--no-permission-warning", \
+                 "--no-mdc-warning"
 
 #define SIG_MAGIC ":signature packet:"
 #define USER_MAGIC ":user ID packet:"
@@ -33,10 +33,14 @@
 #define REQUIRED_MATCH 2
 #define REJECT_MATCH 3
 
-#define VERSION "0.9"
 #define SIG_VERSION "1.0"
 #define DEBSIG_NAMESPACE "http://www.debian.org/debsig/"SIG_VERSION"/";
 
+struct deb_archive {
+	const char *name;
+	int fd;
+};
+
 struct match {
         struct match *next;
         int type;
@@ -61,11 +65,17 @@ struct policy {
 };
 
 struct policy *parsePolicyFile(const char *filename);
-off_t findMember(const char *name);
-off_t checkSigExist(const char *name);
-char *getKeyID (const struct match *mtc);
-char *getSigKeyID (const char *deb, const char *type);
-int gpgVerify(const char *data, struct match *mtc, const char *sig);
+off_t
+findMember(struct deb_archive *deb, const char *name);
+off_t
+checkSigExist(struct deb_archive *deb, const char *name);
+char *
+getKeyID(const char *originID, const struct match *mtc);
+char *
+getSigKeyID(struct deb_archive *deb, const char *type);
+int
+gpgVerify(const char *originID, struct match *mtc,
+          const char *data, const char *sig);
 void clear_policy(void);
 
 /* Debugging and failures */
@@ -90,6 +100,6 @@ do {						\
 } while(0)
 
 extern int ds_debug_level;
-extern FILE *deb_fs;
-extern char *deb;
-extern char originID[];
+extern const char *rootdir;
+extern const char *policies_dir;
+extern const char *keyrings_dir;
diff --git a/gpg-parse.c b/src/gpg-parse.c
similarity index 55%
rename from gpg-parse.c
rename to src/gpg-parse.c
index bae2181..dcd36b7 100644
--- a/gpg-parse.c
+++ b/src/gpg-parse.c
@@ -2,6 +2,7 @@
  * debsig-verify - Debian package signature verification tool
  *
  * Copyright © 2000 Ben Collins <bcollins@debian.org>
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +22,8 @@
  * routines to parse gpg output
  */
 
+#include <config.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
@@ -29,23 +32,60 @@
 #include <sys/stat.h>
 #include <stdlib.h>
 
+#include <dpkg/dpkg.h>
+#include <dpkg/subproc.h>
+#include <dpkg/buffer.h>
+#include <dpkg/path.h>
+
 #include "debsig.h"
 
 static int gpg_inited = 0;
+static char *gpg_tmpdir;
+
+static void
+cleanup_gpg_tmpdir(void)
+{
+    pid_t pid;
+
+    pid = subproc_fork();
+    if (pid == 0) {
+      execlp("rm", "rm", "-rf", gpg_tmpdir, NULL);
+      ohshite("unable to execute %s (%s)", "rm", "rm -rf");
+    }
+    subproc_reap(pid, "getSigKeyID", SUBPROC_NOCHECK);
+
+    free(gpg_tmpdir);
+    gpg_tmpdir = NULL;
+    gpg_inited = 0;
+}
 
-/* Crazy damn hack to make sure gpg has created ~/.gnupg, else it will
- * fail first time called */
-static void gpg_init(void) {
+/* Ensure that gpg has a writable HOME to put its keyrings */
+static void
+gpg_init(void)
+{
     int rc;
 
     if (gpg_inited) return;
-    rc = system(GPG_PROG" --options /dev/null < /dev/null > /dev/null 2>&1");
+
+    gpg_tmpdir = mkdtemp(path_make_temp_template("debsig-verify"));
+    if (gpg_tmpdir == NULL)
+        ohshite("cannot create temporary directory '%s'", gpg_tmpdir);
+
+    rc = setenv("GNUPGHOME", gpg_tmpdir, 1);
     if (rc < 0)
-        ds_fail_printf(DS_FAIL_INTERNAL, "error writing initializing gpg");
+        ohshite("cannot set environment variable %s to '%s'", "GNUPGHOME",
+                gpg_tmpdir);
+
+    rc = atexit(cleanup_gpg_tmpdir);
+    if (rc != 0)
+       ohshit("cannot set atexit cleanup handler");
+
     gpg_inited = 1;
 }
 
-char *getKeyID (const struct match *mtc) {
+char *
+getKeyID(const char *originID, const struct match *mtc)
+{
     static char buf[2048];
     FILE *ds;
     char *c, *d, *ret = mtc->id;
@@ -55,8 +95,9 @@ char *getKeyID (const struct match *mtc) {
 
     gpg_init();
 
-    snprintf(buf, sizeof(buf) - 1, GPG_PROG" "GPG_ARGS_FMT" --list-packets -q "DEBSIG_KEYRINGS_FMT,
-	     GPG_ARGS, originID, mtc->file);
+    snprintf(buf, sizeof(buf) - 1,
+	     GPG_PROG" "GPG_ARGS_FMT" --list-packets -q %s%s/%s/%s",
+	     GPG_ARGS, rootdir, keyrings_dir, originID, mtc->file);
 
     if ((ds = popen(buf, "r")) == NULL) {
 	perror("gpg");
@@ -65,15 +106,15 @@ char *getKeyID (const struct match *mtc) {
 
     c = fgets(buf, sizeof(buf), ds);
     while (c != NULL) {
-	if (!strncmp(buf, USER_MAGIC, strlen(USER_MAGIC))) {
+	if (strncmp(buf, USER_MAGIC, strlen(USER_MAGIC)) == 0) {
 	    if ((c = strchr(buf, '"')) == NULL) continue;
 	    d = c + 1;
 	    if ((c = strchr(d, '"')) == NULL) continue;
 	    *c = '\0';
-	    if (!strcmp(d, mtc->id)) {
+	    if (strcmp(d, mtc->id) == 0) {
 		c = fgets(buf, sizeof(buf), ds);
 		if (c == NULL) continue;
-		if (!strncmp(buf, SIG_MAGIC, strlen(SIG_MAGIC))) {
+		if (strncmp(buf, SIG_MAGIC, strlen(SIG_MAGIC)) == 0) {
 		    if ((c = strchr(buf, '\n')) != NULL)
 			*c = '\0';
 		    d = strstr(buf, "keyid");
@@ -87,7 +128,8 @@ char *getKeyID (const struct match *mtc) {
 	c = fgets(buf, sizeof(buf), ds);
     }
 
-    pclose(ds);
+    if (pclose(ds) < 0)
+	ohshite("getKeyID: closing GnuPG pipe");
 
     if (ret == NULL)
 	ds_printf(DS_LEV_DEBUG, "        getKeyID: failed for %s", mtc->id);
@@ -97,12 +139,15 @@ char *getKeyID (const struct match *mtc) {
     return ret;
 }
 
-char *getSigKeyID (const char *deb, const char *type) {
+char *
+getSigKeyID(struct deb_archive *deb, const char *type)
+{
     static char buf[2048];
-    int pread[2], pwrite[2], t;
-    off_t len = checkSigExist(type);
+    struct dpkg_error err;
+    int pread[2], pwrite[2];
+    off_t len = checkSigExist(deb, type);
     pid_t pid;
-    FILE *ds_read, *ds_write;
+    FILE *ds_read;
     char *c, *ret = NULL;
 
     if (!len)
@@ -112,42 +157,39 @@ char *getSigKeyID (const char *deb, const char *type) {
 
     /* Fork for gpg, keeping a nice pipe to read/write from.  */
     if (pipe(pread) < 0)
-        ds_fail_printf(DS_FAIL_INTERNAL, "error creating a pipe");
+        ohshite("error creating a pipe");
     if (pipe(pwrite) < 0)
-        ds_fail_printf(DS_FAIL_INTERNAL, "error creating a pipe");
+        ohshite("error creating a pipe");
     /* I like file streams, so sue me :P */
-    if ((ds_read = fdopen(pread[0], "r")) == NULL ||
-	 (ds_write = fdopen(pwrite[1], "w")) == NULL)
-	ds_fail_printf(DS_FAIL_INTERNAL, "error opening file stream for gpg");
+    if ((ds_read = fdopen(pread[0], "r")) == NULL)
+	ohshite("error opening file stream for gpg");
 
-    if (!(pid = fork())) {
+    pid = subproc_fork();
+    if (pid == 0) {
 	/* Here we go */
-	dup2(pread[1],1); close(pread[0]); close(pread[1]);
-	dup2(pwrite[0],0); close(pwrite[0]); close(pwrite[1]);
+	m_dup2(pread[1], 1);
+	close(pread[0]);
+	close(pread[1]);
+	m_dup2(pwrite[0], 0);
+	close(pwrite[0]);
+	close(pwrite[1]);
 	execl(GPG_PROG, "gpg", GPG_ARGS, "--list-packets", "-q", "-", NULL);
 	exit(1);
     }
     close(pread[1]); close(pwrite[0]);
 
     /* First, let's feed gpg our signature. Don't forget, our call to
-     * checkSigExist() above positioned the deb_fs file pointer already.  */
-    t = fread(buf, 1, sizeof(buf), deb_fs);
-    while(len > 0) {
-	if (t > len)
-	    fwrite(buf, 1, len, ds_write);
-	else
-	    fwrite(buf, 1, t, ds_write);
-	len -= t;
-	t = fread(buf, 1, sizeof(buf), deb_fs);
-    }
-    if (ferror(ds_write))
-	ds_fail_printf(DS_FAIL_INTERNAL, "error writing to gpg");
-    fclose(ds_write);
+     * checkSigExist() above positioned the deb->fd file pointer already.  */
+    if (fd_fd_copy(deb->fd, pwrite[1], len, &err) < 0)
+	ohshit("getSigKeyID: error reading signature (%s)", err.str);
+
+    if (close(pwrite[1]) < 0)
+	ohshite("getSigKeyID: error closing gpg write pipe");
 
     /* Now, let's see what gpg has to say about all this */
     c = fgets(buf, sizeof(buf), ds_read);
     while (c != NULL) {
-	if (!strncmp(buf, SIG_MAGIC, strlen(SIG_MAGIC))) {
+	if (strncmp(buf, SIG_MAGIC, strlen(SIG_MAGIC)) == 0) {
 	    if ((c = strchr(buf, '\n')) != NULL)
 		*c = '\0';
 	    /* This is the only line we care about */
@@ -160,10 +202,11 @@ char *getSigKeyID (const char *deb, const char *type) {
 	c = fgets(buf, sizeof(buf), ds_read);
     }
     if (ferror(ds_read))
-	ds_fail_printf(DS_FAIL_INTERNAL, "error reading from gpg");
+	ohshit("error reading from gpg");
     fclose(ds_read);
 
-    waitpid(pid, NULL, 0);
+    subproc_reap(pid, "getSigKeyID", SUBPROC_NOCHECK);
+
     if (ret == NULL)
 	ds_printf(DS_LEV_DEBUG, "        getSigKeyID: failed for %s", type);
     else
@@ -172,21 +215,26 @@ char *getSigKeyID (const char *deb, const char *type) {
     return ret;
 }
 
-int gpgVerify(const char *data, struct match *mtc, const char *sig) {
+int
+gpgVerify(const char *originID, struct match *mtc,
+          const char *data, const char *sig)
+{
     char keyring[8192];
-    int status;
     pid_t pid;
+    int rc;
     struct stat st;
 
     gpg_init();
 
-    snprintf(keyring, sizeof(keyring) - 1, DEBSIG_KEYRINGS_FMT, originID, mtc->file);
+    snprintf(keyring, sizeof(keyring) - 1, "%s%s/%s/%s",
+             rootdir, keyrings_dir, originID, mtc->file);
     if (stat(keyring, &st)) {
 	ds_printf(DS_LEV_DEBUG, "gpgVerify: could not stat %s", keyring);
 	return 0;
     }
 
-    if (!(pid = fork())) {
+    pid = subproc_fork();
+    if (pid == 0) {
 	if (DS_LEV_DEBUG < ds_debug_level) {
 	    close(0); close(1); close(2);
 	}
@@ -195,8 +243,8 @@ int gpgVerify(const char *data, struct match *mtc, const char *sig) {
 	exit(1);
     }
 
-    waitpid(pid, &status, 0);
-    if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+    rc = subproc_reap(pid, "gpgVerify", SUBPROC_RETERROR | SUBPROC_RETSIGNO);
+    if (rc != 0) {
 	ds_printf(DS_LEV_DEBUG, "gpgVerify: gpg exited abnormally or with non-zero exit status");
 	return 0;
     }
diff --git a/misc.c b/src/misc.c
similarity index 88%
rename from misc.c
rename to src/misc.c
index 01a2a2c..44c5f45 100644
--- a/misc.c
+++ b/src/misc.c
@@ -2,6 +2,7 @@
  * debsig-verify - Debian package signature verification tool
  *
  * Copyright © 2000 Ben Collins <bcollins@debian.org>
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +22,8 @@
  * miscellaneous functions
  */
 
+#include <config.h>
+
 #include <stdio.h>
 #include <stdarg.h>
 
@@ -38,12 +41,10 @@ void ds_printf(int level, const char *fmt, ...) {
 	(void) vprintf (buf, ap);
 	va_end(ap);
     }
-
-    return;
 }
 
 off_t
-checkSigExist(const char *name)
+checkSigExist(struct deb_archive *deb, const char *name)
 {
     char buf[16];
 
@@ -54,5 +55,5 @@ checkSigExist(const char *name)
 
     snprintf(buf, sizeof(buf) - 1, "_gpg%s", name);
 
-    return findMember(buf);
+    return findMember(deb, buf);
 }
diff --git a/xml-parse.c b/src/xml-parse.c
similarity index 86%
rename from xml-parse.c
rename to src/xml-parse.c
index f459d30..17fd90a 100644
--- a/xml-parse.c
+++ b/src/xml-parse.c
@@ -2,6 +2,7 @@
  * debsig-verify - Debian package signature verification tool
  *
  * Copyright © 2000 Ben Collins <bcollins@debian.org>
+ * Copyright © 2014 Guillem Jover <guillem@debian.org>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +22,8 @@
  * provides the XML parsing code for policy files, via expat (xmltok)
  */
 
+#include <config.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -29,11 +32,12 @@
 #include <sys/stat.h>
 #include <obstack.h>
 
+#include <dpkg/dpkg.h>
 #include <xmltok/xmlparse.h>
 
 #include "debsig.h"
 
-#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_alloc m_malloc
 #define obstack_chunk_free free
 
 static int parse_err_cnt;
@@ -49,13 +53,6 @@ static int deb_obs_init = 0;
     ds_printf(DS_LEV_DEBUG , "%d: " fmt , XML_GetCurrentLineNumber(parser) , ## args); \
 }
 
-static void *xmalloc(size_t size) {
-    register void *p = malloc(size);
-    if (p == NULL)
-	ds_fail_printf(DS_FAIL_INTERNAL, "out of memory");
-    return p;
-}
-
 static void startElement(void *userData, const char *name, const char **atts) {
     int i, depth;
     int *depthPtr = userData;
@@ -64,28 +61,28 @@ static void startElement(void *userData, const char *name, const char **atts) {
     depth = *depthPtr;
     *depthPtr = depth + 1;
 
-    if (!strcmp(name,"Policy")) {
+    if (strcmp(name, "Policy") == 0) {
 	if (depth != 0)
 	    parse_error("policy parse error: 'Policy' found at wrong level");
 
 	for (i = 0; atts[i]; i += 2) {
-	    if (!strcmp(atts[i], "xmlns")) {
-		if (strcmp(atts[i + 1], DEBSIG_NAMESPACE))
+	    if (strcmp(atts[i], "xmlns") == 0) {
+		if (strcmp(atts[i + 1], DEBSIG_NAMESPACE) != 0)
 		    parse_error("policy name space != " DEBSIG_NAMESPACE);
 	    } else
 		parse_error("Policy element contains unknown attribute '%s'",
 			     atts[i]);
 	}
-    } else if (!strcmp(name,"Origin")) {
+    } else if (strcmp(name, "Origin") == 0) {
 	if (depth != 1)
 	    parse_error("policy parse error: 'Origin' found at wrong level");
 	
 	for (i = 0; atts[i]; i += 2) {
-	    if (!strcmp(atts[i], "id"))
+	    if (strcmp(atts[i], "id") == 0)
 		ret.id = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));
-	    else if (!strcmp(atts[i], "Name"))
+	    else if (strcmp(atts[i], "Name") == 0)
 		ret.name = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));
-	    else if (!strcmp(atts[i], "Description"))
+	    else if (strcmp(atts[i], "Description") == 0)
 		ret.description = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));
 	    else
 		parse_error("Origin element contains unknown attribute '%s'",
@@ -94,7 +91,8 @@ static void startElement(void *userData, const char *name, const char **atts) {
 
 	if (ret.id == NULL || ret.name == NULL)
 	    parse_error("Origin element missing Name or ID attribute");
-    } else if (!strcmp(name,"Selection") || !strcmp(name,"Verification")) {
+    } else if (strcmp(name, "Selection") == 0 ||
+	       strcmp(name, "Verification") == 0) {
 	struct group *g = NULL;
 	if (depth != 1)
 	    parse_error("policy parse error: 'Selection/Verification' found at wrong level");
@@ -102,10 +100,10 @@ static void startElement(void *userData, const char *name, const char **atts) {
 	/* create a new entry, make it the current */
 	cur_grp = (struct group *)obstack_alloc(&deb_obs, sizeof(struct group));
 	if (cur_grp == NULL)
-	    ds_fail_printf(DS_FAIL_INTERNAL, "out of memory");
+	    ohshit("out of memory");
 	memset(cur_grp, 0, sizeof(struct group));
 
-	if (!strcmp(name,"Selection")) {
+	if (strcmp(name, "Selection") == 0) {
 	    if (ret.sels == NULL)
 		ret.sels = cur_grp;
 	    else
@@ -123,7 +121,7 @@ static void startElement(void *userData, const char *name, const char **atts) {
 	}
 
 	for (i = 0; atts[i]; i += 2) {
-	    if (!strcmp(atts[i], "MinOptional")) {
+	    if (strcmp(atts[i], "MinOptional") == 0) {
 		int t; const char *c = atts[i+1];
 		for (t = 0; c[t]; t++) {
 		    if (!isdigit(c[t]))
@@ -135,8 +133,9 @@ static void startElement(void *userData, const char *name, const char **atts) {
 			     atts[i]);
 	    }
 	}
-    } else if (!strcmp(name,"Required") || !strcmp(name,"Reject") ||
-	       !strcmp(name,"Optional")) {
+    } else if (strcmp(name, "Required") == 0 ||
+	       strcmp(name, "Reject") == 0||
+	       strcmp(name, "Optional") == 0) {
 	struct match *m = NULL, *cur_m = NULL;
 	if (depth != 2)
 	    parse_error("policy parse error: Match element found at wrong level");
@@ -150,7 +149,7 @@ static void startElement(void *userData, const char *name, const char **atts) {
         /* create a new entry, make it the current */
         cur_m = (struct match *)obstack_alloc(&deb_obs, sizeof(struct match));
         if (cur_m == NULL)
-            ds_fail_printf(DS_FAIL_INTERNAL, "out of memory");
+            ohshit("out of memory");
         memset(cur_m, 0, sizeof(struct match));
 
 	if (cur_grp->matches == NULL)
@@ -163,13 +162,13 @@ static void startElement(void *userData, const char *name, const char **atts) {
 
 	/* Set the attributes first, so we can sanity check the type after */
         for (i = 0; atts[i]; i += 2) {
-            if (!strcmp(atts[i], "Type")) {
+            if (strcmp(atts[i], "Type") == 0) {
                 cur_m->name = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));
-	    } else if (!strcmp(atts[i], "File")) {
+	    } else if (strcmp(atts[i], "File") == 0) {
 		cur_m->file = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));;
-	    } else if (!strcmp(atts[i], "id")) {
+	    } else if (strcmp(atts[i], "id") == 0) {
 		cur_m->id = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));;
-	    } else if (!strcmp(atts[i], "Expiry")) {
+	    } else if (strcmp(atts[i], "Expiry") == 0) {
 		int t; const char *c = atts[i+1];
 		for (t = 0; c[t]; t++) {
 		    if (!isdigit(c[t]))
@@ -183,11 +182,11 @@ static void startElement(void *userData, const char *name, const char **atts) {
         }
 
 
-        if (!strcmp(name,"Required")) {
+        if (strcmp(name, "Required") == 0) {
 	    cur_m->type = REQUIRED_MATCH;
 	    if (cur_m->name == NULL || cur_m->file == NULL)
 		parse_error("Required must have a Type and File attribute");
-	} else if (!strcmp(name,"Optional")) {
+	} else if (strcmp(name, "Optional") == 0) {
 	    cur_m->type = OPTIONAL_MATCH;
 	    if (cur_m->name == NULL || cur_m->file == NULL)
 		parse_error("Optional must have a Type and File attribute");
@@ -203,7 +202,7 @@ static void endElement(void *userData, const char *name) {
     int *depthPtr = userData;
     *depthPtr -= 1;
 
-    if (!strcmp(name,"Selection") || !strcmp(name,"Verification")) {
+    if (strcmp(name, "Selection") == 0 || strcmp(name, "Verification") == 0) {
 	struct match *m; int i = 0;
 	/* sanity check this block */
 	for (m = cur_grp->matches; m; m = m->next) {
@@ -225,7 +224,6 @@ void clear_policy(void) {
 	deb_obs_init = 0;
     }
     memset(&ret, '\0', sizeof(struct policy));
-    return;
 }
 
 struct policy *parsePolicyFile(const char *filename) {
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..eeb2f13
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,4 @@
+atconfig
+atlocal
+package.m4
+testsuite
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..cd12a2c
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,41 @@
+## Process this file with automake to produce Makefile.in
+
+PACKAGE_M4 = $(srcdir)/package.m4
+EXTRA_DIST = $(PACKAGE_M4)
+
+TESTSUITE = $(srcdir)/testsuite
+EXTRA_DIST += $(TESTSUITE)
+
+TESTSUITE_AT = testsuite.at
+TESTSUITE_AT += debsig-cmd.at
+TESTSUITE_AT += debsig-sig.at
+EXTRA_DIST += $(TESTSUITE_AT)
+
+EXTRA_DIST += policies/FAD46790DE88C7E2
+EXTRA_DIST += keyrings/FAD46790DE88C7E2
+
+DISTCLEANFILES = atconfig
+
+$(PACKAGE_M4): $(top_srcdir)/configure.ac
+	{ \
+	  echo '# Signature of the current package.'; \
+	  echo 'm4_define([AT_PACKAGE_NAME],      [@PACKAGE_NAME@])'; \
+	  echo 'm4_define([AT_PACKAGE_TARNAME],   [@PACKAGE_TARNAME@])'; \
+	  echo 'm4_define([AT_PACKAGE_VERSION],   [@PACKAGE_VERSION@])'; \
+	  echo 'm4_define([AT_PACKAGE_STRING],    [@PACKAGE_STRING@])'; \
+	  echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
+	} >$@
+
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): $(PACKAGE_M4) $(TESTSUITE_AT)
+	$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+	mv $@.tmp $@
+
+check-local: atconfig atlocal $(TESTSUITE)
+	$(SHELL) $(TESTSUITE) $(TESTSUITEFLAGS)
+
+installcheck-local: atconfig atlocal $(TESTSUITE)
+	$(SHELL) $(TESTSUITE) $(TESTSUITEFLAGS) AUTOTEST_PATH='$(bindir)'
+
+clean-local:
+	test ! -f $(TESTSUITE) || $(SHELL) $(TESTSUITE) --clean
diff --git a/test/atlocal.in b/test/atlocal.in
new file mode 100644
index 0000000..d416bb9
--- /dev/null
+++ b/test/atlocal.in
@@ -0,0 +1,68 @@
+# Global shell definitions for the autotest test suite
+
+PATH="@abs_top_builddir@/src:$PATH"
+export PATH
+
+# Setup a sane environment
+LC_ALL=C
+export LC_ALL
+
+TZ=UTC
+export TZ
+
+# Define helper variables and functions
+TESTDATA="@abs_top_srcdir@/test"
+TESTPOLICIES="$TESTDATA/policies"
+TESTKEYRINGS="$TESTDATA/keyrings"
+TESTKEYID="FAD46790DE88C7E2"
+TESTGNUPG="gnupg"
+
+DEBSIG="debsig-verify -v -d --policies-dir $TESTPOLICIES --keyrings-dir $TESTKEYRINGS"
+
+debsig_setup_gnupg ()
+{
+  mkdir -p $TESTGNUPG
+  chmod go-rwx $TESTGNUPG
+  cp -a $TESTDATA/keyrings/$TESTKEYID/* $TESTGNUPG/
+}
+
+debsig_make_deb ()
+{
+  local debname="$1"
+  local debversion="$2"
+  local debdir="${debname}_${debversion}"
+  local debpkg="${debdir}.deb"
+
+  # Make a .deb package.
+  mkdir -p $debdir/DEBIAN
+  cat > $debdir/DEBIAN/control <<EOF
+Package: $debname
+Version: $debversion
+Architecture: all
+Maintainer: Dpkg Developers <debian-dpkg@lists.debian.org>
+Description: Signature Test Package
+EOF
+  mkdir -p $debdir/usr/share/doc/
+  echo "Debsig testing deb" > $debdir/usr/share/doc/README
+  dpkg-deb -b "$debdir" "$debpkg"
+}
+
+debsig_make_sig_bad ()
+{
+  local debpkg="$1_$2.deb"
+
+  # Add a bogus signature to a .deb package.
+  debsig_setup_gnupg
+  gpg --home $TESTGNUPG  --detach-sig >_gpgorigin <"$debpkg"
+  ar q "$debpkg" _gpgorigin
+}
+
+debsig_make_sig ()
+{
+  local debpkg="$1_$2.deb"
+
+  # Add signature to a .deb package.
+  debsig_setup_gnupg
+  ar p "$debpkg" | gpg --home $TESTGNUPG --detach-sig >_gpgorigin
+  ar q "$debpkg" _gpgorigin
+}
diff --git a/testing/debs/sigtest1_1.0-1_all.deb b/test/debs/sigtest1_1.0-1_all.deb
similarity index 100%
rename from testing/debs/sigtest1_1.0-1_all.deb
rename to test/debs/sigtest1_1.0-1_all.deb
diff --git a/testing/debs/sigtest2_2.0-1_all.deb b/test/debs/sigtest2_2.0-1_all.deb
similarity index 100%
rename from testing/debs/sigtest2_2.0-1_all.deb
rename to test/debs/sigtest2_2.0-1_all.deb
diff --git a/test/debsig-cmd.at b/test/debsig-cmd.at
new file mode 100644
index 0000000..ec3312a
--- /dev/null
+++ b/test/debsig-cmd.at
@@ -0,0 +1,11 @@
+AT_BANNER([Command-line options])
+
+AT_SETUP([debsig-verify --version])
+AT_KEYWORDS([debsig-verify command-line])
+AT_CHECK([debsig-verify --version], [], [ignore])
+AT_CLEANUP()
+
+AT_SETUP([debsig-verify --help])
+AT_KEYWORDS([debsig-verify command-line])
+AT_CHECK([debsig-verify --help], [], [ignore])
+AT_CLEANUP()
diff --git a/test/debsig-sig.at b/test/debsig-sig.at
new file mode 100644
index 0000000..a46c34d
--- /dev/null
+++ b/test/debsig-sig.at
@@ -0,0 +1,36 @@
+AT_BANNER([Binary .deb packages])
+
+AT_SETUP([deb does not validate, no signature])
+AT_KEYWORDS([debsig-verify deb])
+DEBSIG_MAKE_DEB([debraw], [1.0])
+AT_CHECK([$DEBSIG debraw_1.0.deb], [10], [ignore])
+AT_CLEANUP()
+
+AT_SETUP([deb does not validate, signed, no policy dir])
+AT_KEYWORDS([debsig-verify deb])
+DEBSIG_MAKE_DEB([debsig], [1.0])
+DEBSIG_MAKE_SIG([debsig], [1.0])
+AT_CHECK([$DEBSIG --policies-dir "nonexistent" debsig_1.0.deb], [11], [ignore])
+AT_CLEANUP()
+
+AT_SETUP([deb does not validate, signed, no policy file])
+AT_KEYWORDS([debsig-verify deb])
+DEBSIG_MAKE_DEB([debsig], [1.0])
+DEBSIG_MAKE_SIG([debsig], [1.0])
+AT_CHECK([mkdir -p policies/$TESTKEYID
+$DEBSIG --policies-dir "policies" debsig_1.0.deb], [12], [ignore])
+AT_CLEANUP()
+
+AT_SETUP([deb does not validate, bogus signature])
+AT_KEYWORDS([debsig-verify deb])
+DEBSIG_MAKE_DEB([debsig], [1.0])
+DEBSIG_MAKE_SIG_BAD([debsig], [1.0])
+AT_CHECK([$DEBSIG debsig_1.0.deb], [13], [ignore], [ignore])
+AT_CLEANUP()
+
+AT_SETUP([deb does validate])
+AT_KEYWORDS([debsig-verify deb])
+DEBSIG_MAKE_DEB([debsig], [1.0])
+DEBSIG_MAKE_SIG([debsig], [1.0])
+AT_CHECK([$DEBSIG debsig_1.0.deb], [], [ignore], [ignore])
+AT_CLEANUP()
diff --git a/testing/keyrings/7CD73F641E04EC2D/debian-debsig.gpg b/test/keyrings/7CD73F641E04EC2D/debian-debsig.gpg
similarity index 100%
rename from testing/keyrings/7CD73F641E04EC2D/debian-debsig.gpg
rename to test/keyrings/7CD73F641E04EC2D/debian-debsig.gpg
diff --git a/test/keyrings/FAD46790DE88C7E2/pubring.gpg b/test/keyrings/FAD46790DE88C7E2/pubring.gpg
new file mode 100644
index 0000000..149108f
Binary files /dev/null and b/test/keyrings/FAD46790DE88C7E2/pubring.gpg differ
diff --git a/test/keyrings/FAD46790DE88C7E2/secring.gpg b/test/keyrings/FAD46790DE88C7E2/secring.gpg
new file mode 100644
index 0000000..6bb40a6
Binary files /dev/null and b/test/keyrings/FAD46790DE88C7E2/secring.gpg differ
diff --git a/testing/policies/7CD73F641E04EC2D/generic.pol b/test/policies/7CD73F641E04EC2D/generic.pol
similarity index 100%
copy from testing/policies/7CD73F641E04EC2D/generic.pol
copy to test/policies/7CD73F641E04EC2D/generic.pol
diff --git a/testing/policies/7CD73F641E04EC2D/potato-release.pol b/test/policies/7CD73F641E04EC2D/potato-release.pol
similarity index 100%
rename from testing/policies/7CD73F641E04EC2D/potato-release.pol
rename to test/policies/7CD73F641E04EC2D/potato-release.pol
diff --git a/testing/policies/7CD73F641E04EC2D/generic.pol b/test/policies/FAD46790DE88C7E2/generic.pol
similarity index 65%
rename from testing/policies/7CD73F641E04EC2D/generic.pol
rename to test/policies/FAD46790DE88C7E2/generic.pol
index 5edff09..998e813 100644
--- a/testing/policies/7CD73F641E04EC2D/generic.pol
+++ b/test/policies/FAD46790DE88C7E2/generic.pol
@@ -4,21 +4,19 @@
 
   <!-- This is mainly a sanity check, since our filename is that of the ID
        anyway. -->
-  <Origin Name="Debian" id="7CD73F641E04EC2D" Description="Debian GNU Project"/>
+  <Origin Name="Debsig" id="FAD46790DE88C7E2" Description="Debsig testing"/>
 
   <!-- This is required to match in order for this policy to be used. We
        reject the release Type, since we want a different rule set for
        that. -->
   <Selection>
-    <Required Type="origin" File="debian-debsig.gpg" id="7CD73F641E04EC2D"/>
-    <Reject Type="release"/>
+    <Required Type="origin" File="pubring.gpg" id="FAD46790DE88C7E2"/>
   </Selection>
 
   <!-- Once we decide to use this policy, this must pass in order to verify
        the package. -->
   <Verification MinOptional="0">
-    <Required Type="origin" File="debian-debsig.gpg" id="7CD73F641E04EC2D"/>
-    <Optional Type="maint" File="debian-keyring.gpg"/>
+    <Required Type="origin" File="pubring.gpg" id="FAD46790DE88C7E2"/>
   </Verification>
 
 </Policy>
diff --git a/test/testsuite.at b/test/testsuite.at
new file mode 100644
index 0000000..aad6e77
--- /dev/null
+++ b/test/testsuite.at
@@ -0,0 +1,11 @@
+AT_INIT()
+AT_COLOR_TESTS()
+
+AT_TESTED([debsig-verify])
+
+m4_define([DEBSIG_MAKE_DEB], [debsig_make_deb "$1" "$2"])
+m4_define([DEBSIG_MAKE_SIG], [debsig_make_sig "$1" "$2"])
+m4_define([DEBSIG_MAKE_SIG_BAD], [debsig_make_sig_bad "$1" "$2"])
+
+m4_include([debsig-cmd.at])
+m4_include([debsig-sig.at])

Reply to: