summaryrefslogtreecommitdiffstats
path: root/gpglib
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /gpglib
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'gpglib')
-rw-r--r--gpglib/.gitignore6
-rw-r--r--gpglib/Makefile.am45
-rw-r--r--gpglib/README.html357
-rw-r--r--gpglib/checksign.c75
-rw-r--r--gpglib/configure.in120
-rw-r--r--gpglib/delete.c77
-rw-r--r--gpglib/export.c64
-rw-r--r--gpglib/fork.c359
-rw-r--r--gpglib/genkey.c209
-rw-r--r--gpglib/gpg.c2235
-rw-r--r--gpglib/gpglib.h239
-rw-r--r--gpglib/import.c70
-rw-r--r--gpglib/libgpg.c104
-rw-r--r--gpglib/list.c574
-rw-r--r--gpglib/mimegpg.c189
-rw-r--r--gpglib/mimegpg.sgml152
-rw-r--r--gpglib/mimegpgfork.c449
-rw-r--r--gpglib/mimegpgfork.h84
-rw-r--r--gpglib/mimegpgheader.c393
-rw-r--r--gpglib/mimegpgheader.h54
-rw-r--r--gpglib/mimegpgstack.c72
-rw-r--r--gpglib/mimegpgstack.h32
-rw-r--r--gpglib/options.c41
-rw-r--r--gpglib/rfc2045.c115
-rw-r--r--gpglib/sign.c109
-rw-r--r--gpglib/tempname.c77
-rw-r--r--gpglib/tempname.h23
-rw-r--r--gpglib/testgpg.c184
-rw-r--r--gpglib/webgpg.in39
29 files changed, 6547 insertions, 0 deletions
diff --git a/gpglib/.gitignore b/gpglib/.gitignore
new file mode 100644
index 0000000..75404e5
--- /dev/null
+++ b/gpglib/.gitignore
@@ -0,0 +1,6 @@
+/gpg.h
+/mimegpg
+/mimegpg.1
+/mimegpg.html
+/testgpg
+/webgpg
diff --git a/gpglib/Makefile.am b/gpglib/Makefile.am
new file mode 100644
index 0000000..3ea6fbd
--- /dev/null
+++ b/gpglib/Makefile.am
@@ -0,0 +1,45 @@
+#
+# Copyright 2001-2007 Double Precision, Inc. See COPYING for
+# distribution information.
+#
+
+
+CLEANFILES=gpg.h
+
+noinst_SCRIPTS=webgpg
+
+noinst_LTLIBRARIES=libgpg.la
+noinst_PROGRAMS=testgpg mimegpg
+
+libgpg_la_SOURCES=checksign.c delete.c export.c fork.c genkey.c gpg.c gpglib.h\
+ import.c libgpg.c list.c mimegpgfork.c mimegpgfork.h mimegpgheader.c \
+ mimegpgheader.h mimegpgstack.c mimegpgstack.h options.c rfc2045.c \
+ sign.c tempname.c tempname.h
+
+testgpg_SOURCES=testgpg.c
+testgpg_LDADD=libgpg.la ../unicode/libunicode.la ../numlib/libnumlib.la
+testgpg_DEPENDENCIES=$(testgpg_LDADD)
+testgpg_LDFLAGS=-static
+
+mimegpg_SOURCES=mimegpg.c
+mimegpg_LDADD=libgpg.la ../rfc2045/librfc2045.la ../rfc822/librfc822.la \
+ ../rfc822/libencode.la ../numlib/libnumlib.la \
+ ../unicode/libunicode.la
+mimegpg_DEPENDENCIES=libgpg.la ../rfc2045/librfc2045.la ../rfc822/librfc822.la\
+ ../rfc822/libencode.la ../numlib/libnumlib.la \
+ ../unicode/libunicode.la
+BUILT_SOURCES=gpg.h mimegpg.html mimegpg.1
+EXTRA_DIST=mimegpg.html mimegpg.1 README.html
+
+if HAVE_SGML
+
+mimegpg.html: mimegpg.sgml ../docbook/sgml2html
+ ../docbook/sgml2html mimegpg.sgml mimegpg.html
+
+mimegpg.1: mimegpg.sgml ../docbook/sgml2man
+ ../docbook/sgml2man mimegpg.sgml mimegpg.1
+
+endif
+
+gpg.h: config.status
+ echo '#define GPG "@GPG@"' >gpg.h
diff --git a/gpglib/README.html b/gpglib/README.html
new file mode 100644
index 0000000..a28d1a8
--- /dev/null
+++ b/gpglib/README.html
@@ -0,0 +1,357 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>GnuPG support in SqWebMail</title>
+ <meta name="MSSmartTagsPreventParsing" content="TRUE" />
+</head>
+
+<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B"
+alink="#FF0000" xml:lang="en" lang="en">
+<!-- Copyright 1998 - 2001 Double Precision, Inc. See COPYING for -->
+<!-- distribution information. -->
+
+<h1>GnuPG support in <code>SqWebMail</code></h1>
+
+<p>Table of contents:</p>
+<ul>
+ <li><a href="#intro">Introduction</a><br />
+ <br />
+ </li>
+ <li><a href="#require">Requirements</a><br />
+ <br />
+ </li>
+ <li><a href="#implementation">Implementation</a><br />
+ <br />
+ </li>
+ <li><a href="#interop">Interoperability</a><br />
+ <br />
+ </li>
+ <li><a href="#passphrase">Using passphrase-protected private keys</a><br />
+ <br />
+ </li>
+ <li><a href="#bugs">Bugs</a><br />
+ <br />
+ </li>
+ <li><a href="#random">Important note for Linux and other systems that use
+ the <code>/dev/random</code> device.</a><br />
+ <br />
+ </li>
+</ul>
+
+<h2><a name="intro" id="intro">Introduction</a></h2>
+
+<p>This is experimental beta of GnuPG support in <code>SqWebMail</code>. The
+following functionality is available:</p>
+<ul>
+ <li>Create new keypairs<br />
+ <br />
+ </li>
+ <li>List public/private keys<br />
+ <br />
+ </li>
+ <li>Delete keys<br />
+ <br />
+ </li>
+ <li>Sign keys<br />
+ <br />
+ </li>
+ <li>Export keys (attach them to the current message)<br />
+ <br />
+ </li>
+ <li>Import keys from messages<br />
+ <br />
+ </li>
+ <li>Sign outgoing messages<br />
+ <br />
+ </li>
+ <li>Encrypt outgoing messages<br />
+ <br />
+ </li>
+ <li>Display and verify signed messages<br />
+ <br />
+ </li>
+ <li>Decrypt encrypted messages<br />
+ <br />
+ </li>
+ <li>Reply and forward encrypted messages. Correctly handle
+ multipart/alternative and multipart/related.<br />
+ <br />
+ </li>
+</ul>
+
+<p><code>SqWebMail</code> creates and manages its own keyrings.
+<code>$HOME/.gnupg</code> is not used. This allows GnuPG functions to work
+for virtual accounts that have no home directory.</p>
+
+<h2><a name="require" id="require">Requirements</a></h2>
+
+<p>This implementation requires GnuPG, which is used for all of the heavy
+lifting. No encryption code exists in <code>SqWebMail</code> itself. The
+configuration script searches the current PATH for the <code>gpg</code>
+binary. The final <code>SqWebMail</code> binary gets the hardcoded absolute
+path to the <code>gpg</code> binary. If <code>gpg</code> is not found in the
+current PATH, <code>/usr/bin/gpg</code> will be used. Therefore, if GnuPG is
+locally installed in a non-default location, make sure that <code>gpg</code>
+can be found in the current PATH.</p>
+
+<h2><a name="implementation" id="implementation">Implementation</a></h2>
+
+<p><code><code>SqWebMail</code></code> runs <code>gpg</code> automatically to
+handle all the actual tasks involved in encrypting and decrypting messages.
+<code>make install</code> also installs two more binaries:
+<code>reformime</code> and <code>mimegpg</code>. Those binaries do some other
+things, and nothing really needs to be done about them, or to them, except to
+know that they're there.</p>
+
+<h3>The <code>webgpg</code> script</h3>
+<code>make install</code> automatically installs a shell script,
+<code>webgpg</code>. Encryption/decryption capabilities for all mail accounts
+are disabled by default. The <code>webgpg</code> script takes one argument -
+the complete pathname to a Maildir. <code>webgpg</code> creates a new
+subdirectory, <code>Maildir/gpg</code>, and initializes it. Once that
+subdirectory is created, encryption/decryption in <code>SqWebMail</code> will
+become available for that account. Not every mail account has to have
+encryption/decryption enabled. This functionality can be selectively enabled
+and disabled for individual accounts.
+
+<p>NOTE: <code>webgpg</code> script MUST be executed by the same userid and
+groupid that owns the <code>Maildir</code> directory.</p>
+
+<p>NOTE: The current implementation consists of the bare minimum required to
+get the overall functionality. As a result, a number of shortcuts are taken
+with respect to avoiding some hoops that GnuPG occasionally requires to be
+jumped through. For example, by default GnuPG requires that a public key
+must be signed before it can be used for encryption. <code>SqWebMail</code>
+will automatically provide the "always-trust" option to bypass the check.
+Basically, if you have the public key in your keyring, you can use it.</p>
+
+<h3>Issues with adding or activating encryption for existing accounts</h3>
+
+<p>Obviously, encryption/decryption takes additional server CPU cycles. There
+is no hard and fast rule for how much additional load is needed. Fortunately,
+this is not an "all or none" deal. Encryption support can be activated in a
+controlled manner and phased in gradually, in steps, allowing the impact on
+the web/mail server to be closely monitored, and controlled.</p>
+
+<h3>Issues with adding or activating encryption for new accounts</h3>
+
+<p>The <code>webgpg</code> script needs to be used to initialize encryption
+support for newly-created accounts. Many systems use the
+<code>/etc/skel</code> directory as a template for setting up the initial
+contents of new accounts. In that case, use run "<code>webgpg
+/etc/skel/Maildir</code>", and all new accounts will have encryption
+automatically activated.</p>
+
+<h2><a name="interop" id="interop">Interoperability</a></h2>
+
+<p>As of now, <code>SqWebMail</code>'s encryption/decryption is experimental.
+There's not been much interoperability testing with other encryption-capable
+mail software. It is very much possible that there are some interoperability
+issues that will have to be worked out.</p>
+
+<p>The directory <code>Maildir/gpg</code> does pretty much what
+<code>$HOME/.gnupg</code> does for the command-line <code>gpg</code> tool.
+<code>SqWebMail</code> does not use <code>$HOME/.gnupg</code>, it keeps its
+keyrings and other GnuPG-related stuff in the <code>Maildir/gpg</code>
+directory. That's because <code>SqWebMail</code> can be configured to use
+both physical system accounts and virtual mail accounts, and after
+authenticating, there is no difference whatsoever between the two. Therefore,
+<code>SqWebMail</code> has no concept of the $HOME directory. Its entire
+universe consists of the <code>Maildir</code> directory, and its contents.
+<code>SqWebMail</code> is completely unaware of the existence of
+<code>$HOME/.gnupg</code>, and will never be aware of it. Any keypairs in
+<code>$HOME/.gnupg</code> will have to be imported into
+<code>Maildir/gpg</code>.</p>
+
+<h3>Importing keys</h3>
+
+<p>Since <code>$HOME/.gnupg</code> is not used, any existing keys will have
+to be imported. If shell access is available, the <code>pubring</code> and
+<code>secring</code> files can simply be copied to <code>Maildir/gpg</code>.
+The following procedure can be used to import keys in all other
+circumstances. The following example imports keys from mail account M to
+<code>SqWebMail</code> mail account W.</p>
+<ul>
+ <li>Create a temporary keypair in W.<br />
+ <br />
+ </li>
+ <li>Create a new message, addressed to M, attach the new public key, and
+ send it.<br />
+ <br />
+ </li>
+ <li>In M, take the new public key and import it.<br />
+ <br />
+ </li>
+ <li>Export M's secret key. With GnuPG, use the following command:
+ <pre>gpg --export-secret-key --armor [fingerprint]</pre>
+ <p>The output from this command looks something like this:</p>
+ <pre>-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.0.4 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
++M3OSONvootZCzKXP6VT29Vf+lZLQkjSyuY28PpfflaFKi8YpeCEKo4dDhynxCbV
+NAYk2I6wXguwl4MOT0ebkEWM1WS9lTsto7cCzz0ovSg3xe82PozA/4s6E5UUgl1B
+
+...
+RQj/UASJxoPEEDHAhfZ0FYPsDFbo/P///4nDiTQk6Pz///+J2I1l+FteXcOJ9lWJ
+RSBQ/3Uc/3UY/3UU/3UQ/3UM/3UI6GsAAACDxBz/dSxqBf91KGoAicP/dST8McCJ
+-----END PGP PRIVATE KEY BLOCK-----</pre>
+ <br />
+ <br />
+ </li>
+ <li>Wrap this inside a MIME-formatted mail message. The end result should
+ look something like this:
+ <pre>Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="xxx"
+
+--xxx
+Content-Type: text/plain
+
+Here's the key
+
+--xxx
+Content-Type: application/pgp-keys
+
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.0.4 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
++M3OSONvootZCzKXP6VT29Vf+lZLQkjSyuY28PpfflaFKi8YpeCEKo4dDhynxCbV
+NAYk2I6wXguwl4MOT0ebkEWM1WS9lTsto7cCzz0ovSg3xe82PozA/4s6E5UUgl1B
+
+...
+RQj/UASJxoPEEDHAhfZ0FYPsDFbo/P///4nDiTQk6Pz///+J2I1l+FteXcOJ9lWJ
+RSBQ/3Uc/3UY/3UU/3UQ/3UM/3UI6GsAAACDxBz/dSxqBf91KGoAicP/dST8McCJ
+-----END PGP PRIVATE KEY BLOCK-----
+
+--xxx</pre>
+ <br />
+ <br />
+ </li>
+ <li>Encrypt the whole message using W's public key. The end result should
+ look something like this:
+ <pre>Mime-version: 1.0
+Content-Type: multipart/encrypted;
+ boundary="yyy";
+ protocol="application/pgp-encrypted"
+
+--yyy
+Content-Type: application/pgp-encrypted
+Content-Transfer-Encoding: 7bit
+
+Version: 1
+
+--yyy
+Content-Type: application/octet-stream
+Content-Transfer-Encoding: 7bit
+
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.0.4 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+hQIOA8nMaZ+WFjCiEAf+IzymijY0K0ELubJnDuTo0OZg6n8EApLTdwXVZ7sjoWvU
+YbEjfvwBXp+wc7X4lvw3S/AGYe4/mmshgTnRjuDPKsN3zHViYGdGF0UaB40sjUp3
+
+...
+U9Qi2kvxRvV0b8nBN1qeFUSSRF8Ncwug1sr/rFVNNWA0yavb5WDFrqRE6WFJU3z3
+p0vPPRbdWCOTP7WGOygpHdRMLQSCxG4m+L9uG3roG5Uu7A==
+=GutI
+-----END PGP MESSAGE-----
+
+--yyy--</pre>
+ <br />
+ </li>
+ <li>Mail this message to W. Repeat the procedure with M's public key
+ (<code>gpg --export-key --armor). If you're handy with composing MIME
+ messages, both keys can be sent in a single encrypted message. <br />
+ <br />
+ </code></li>
+ <li>In <code>SqWebMail</code>, you should be able to decrypt the message
+ containing the keys, and import them.</li>
+</ul>
+
+<h2><a name="passphrase" id="passphrase">Using passphrase-protected private
+keys</a></h2>
+
+<p>SqWebMail can handle passphrase-protected keys only when SSL is being
+used. This is an artificial restriction that prevents passphrases from going
+over the network, in the clear. When SSL is used, SqWebMail will
+automatically prompt for a passphrase. Leave the passphrase field empty if
+the secret key is not protected by a passphrase.</p>
+
+<h2><a name="bugs" id="bugs">Bugs</a></h2>
+<ol>
+ <li>Although <code>SqWebMail</code> contains mappings for many charsets,
+ GnuPG versions prior to 1.0.6 know only about <code>iso-8859-1</code> and
+ <code>iso-8859-2</code>. All messages displayed by <code>SqWebMail</code>
+ from GnuPG 1.0.5 or earlier will come out in <code>iso-8859-1</code>. <br
+ />
+ <br />
+ </li>
+ <li>Currently, there are no command line options in GnuPG for removing key
+ signatures (is that even possible?). You can sign keys, but can't
+ "unsign" them.<br />
+ <br />
+ </li>
+ <li>There has not been any/much interoperability testing with other
+ GnuPG/PGP mail software. It is entirely possible that
+ <code>SqWebMail</code>'s implementation is currently not interoperable,
+ and may require some tinkering as time goes by.<br />
+ <br />
+ </li>
+ <li>GnuPG will always blather "Warning: secret key NOT protected" when
+ using keys not protected by a passphrase. This whinge is harmless, and
+ can be ignored.<br />
+ <br />
+ </li>
+ <li>The error message from GnuPG for a missing passphrase is not very
+ helpful: "Unable to get tty input". This is because SqWebMail runs GnuPG
+ with the --no-tty flag, and provides a passphrase separately, when it's
+ entered. When SqWebMail does not provide a passphrase, GnuPG complains
+ that tty input is disabled.<br />
+ <br />
+ </li>
+</ol>
+
+<h2><a name="random" id="random">Important note for Linux and other systems
+that use the <code>/dev/random</code> device.</a></h2>
+
+<p>If GnuPG is compiled on a system that has a <code>/dev/random</code>
+device, GnuPG will use the <code>/dev/random</code> device as a source for
+random entropy. The <code>/dev/random</code> device driver in Linux, and in
+many other implementations, creates the entropy pool from system device
+driver background activity. Unfortunately, it's is not a bottomless pit of
+random entropy. When the random device driver entropy pool is empty,
+<code>/dev/random</code> halts until device drivers create more noise.</p>
+
+<p>That means that when GnuPG is used on Linux, GnuPG may pause for an
+excessive period of time, and issue its famous "please do something" prompt.
+Since GnuPG is invoked in automatic mode, there is no other alternative,
+except to wait until more device driver noise becomes available.</p>
+
+<p>This is more likely to happen when GnuPG is used to create new keypairs,
+especially ones that are 1024 bits, or longer. Before deploying, extensive
+testing is recommended to make sure that even an idle system generates
+sufficient background noise so that the additional wait is not excessive.
+There are several possible solutions that can also be used:</p>
+<ol>
+ <li>GnuPG can be optionally configured to use a pseudo-random generator
+ that does not use <code>/dev/random</code>, but instead uses several
+ alternative sources of random junk. Consult GnuPG's documentation for
+ more information.<br />
+ <br />
+ </li>
+ <li>Many systems also have a <code>/dev/urandom</code> device, which
+ automatically generates pseudo-random noise if <code>/dev/random</code>
+ runs out of entropy. This makes it possible to simply remove the
+ <code>/dev/random</code> device and create a link from
+ <code>/dev/random</code> to <code>/dev/urandom</code>.</li>
+</ol>
+
+<p>Although - technically - both approaches can theoretically result in
+slightly less secure keypairs, practically the difference is probably
+academic in nature.</p>
+</body>
+</html>
diff --git a/gpglib/checksign.c b/gpglib/checksign.c
new file mode 100644
index 0000000..a5333ba
--- /dev/null
+++ b/gpglib/checksign.c
@@ -0,0 +1,75 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+
+/*
+** Check signature.
+*/
+
+static int dochecksign(int (*)(const char *, size_t, void *),
+ void *);
+
+int libmail_gpg_checksign(const char *gpgdir, const char *content,
+ const char *signature,
+ int (*dump_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ char *argvec[10];
+ int rc;
+
+ argvec[0]="gpg";
+ argvec[1]="--no-tty";
+ argvec[2]="--verify";
+ argvec[3]=(char *)signature;
+ argvec[4]=(char *)content;
+ argvec[5]=0;
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout,
+ &libmail_gpg_stderr, gpgdir, argvec) < 0)
+ rc= -1;
+ else
+ {
+ int rc2;
+
+ rc=dochecksign(dump_func, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc2)
+ rc=rc2;
+ }
+ return (rc);
+}
+
+static int dochecksign(int (*dump_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ int rc=libmail_gpg_write("Y\n", 2, dump_func, NULL, NULL, 0, voidarg);
+ int rc2;
+
+ if (rc == 0)
+ rc=libmail_gpg_read(dump_func, dump_func, NULL, 0, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc == 0)
+ rc=rc2;
+ return (rc);
+}
diff --git a/gpglib/configure.in b/gpglib/configure.in
new file mode 100644
index 0000000..80b00aa
--- /dev/null
+++ b/gpglib/configure.in
@@ -0,0 +1,120 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl
+dnl Copyright 2001-2010 Double Precision, Inc. See COPYING for
+dnl distribution information.
+dnl
+
+AC_INIT(webpgp, 0.10, [courier-users@lists.sourceforge.net])
+
+>confdefs.h # Kill PACKAGE_ macros
+
+AC_CONFIG_SRCDIR(webgpg.in)
+AC_CONFIG_AUX_DIR(../..)
+AM_INIT_AUTOMAKE([foreign no-define])
+LPATH="$PATH:/usr/local/bin"
+
+AM_CONFIG_HEADER(config.h)
+
+dnl Checks for programs.
+AC_PROG_AWK
+AC_PROG_INSTALL
+AC_PROG_CC
+AC_LIBTOOL_DLOPEN
+AM_PROG_LIBTOOL
+AC_PROG_LN_S
+AC_PATH_PROGS(GPG, gpg gpg2, /usr/bin/gpg, $LPATH)
+AC_PATH_PROGS(PERL, perl5 perl, perl, $LPATH)
+
+if test "$PERL" = "perl"
+then
+ AC_MSG_ERROR(Perl is required)
+fi
+
+AC_CACHE_CHECK( [for gpg charset], ac_cv_gpg_charset,
+
+rm -rf conftestdir
+mkdir conftestdir
+
+GNUPGHOME=`pwd`/conftestdir
+export GNUPGHOME
+
+if $GPG --charset utf-8 --list-keys >/dev/null 2>&1
+then
+ ac_cv_gpg_charset=utf-8
+else
+ ac_cv_gpg_charset=iso-8859-1
+fi
+rm -rf conftestdir
+unset GNUPGHOME
+
+)
+
+AC_DEFINE_UNQUOTED(GPG_CHARSET,"$ac_cv_gpg_charset",
+ [ Default gpg output character set ])
+
+VERSION="`$GPG --version | sed '2,$d;s/.* //'`"
+
+if test "$VERSION" = ""
+then
+ AC_MSG_WARN(Unable to determine gpg version)
+else
+ has_cert_check_level=1
+
+ case $VERSION in
+ 1.0.4)
+ has_cert_check_level=0
+ ;;
+ 1.0.5)
+ has_cert_check_level=0
+ ;;
+ 1.0.6)
+ has_cert_check_level=0
+ ;;
+ esac
+
+fi
+
+if test "$has_cert_check_level" = 1
+then
+ AC_DEFINE_UNQUOTED(GPG_HAS_CERT_CHECK_LEVEL,1,
+ [ Whether gpg --sign-key asks for certificate trust level ])
+fi
+
+rm -rf conftempdir
+mkdir conftempdir
+
+if $GPG --homedir conftempdir --list-keys --allow-secret-key-import >/dev/null
+then
+ AC_DEFINE_UNQUOTED(GPG_HAS_ALLOW_SECRET_KEY_IMPORT,1,
+ [ Whether gpg has the --allow-secret-key-import option ])
+fi
+
+rm -rf conftempdir
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(unistd.h sys/wait.h sys/time.h unistd.h fcntl.h)
+AC_HEADER_SYS_WAIT
+AC_HEADER_TIME
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_SYS_LARGEFILE
+
+dnl Checks for library functions.
+AC_TYPE_SIGNAL
+AC_CHECK_FUNCS(gettimeofday)
+
+if test "$GCC" = yes ; then
+ CFLAGS="$CFLAGS -Wall"
+fi
+
+CFLAGS="-I.. -I$srcdir/.. $CFLAGS"
+
+AM_CONDITIONAL(HAVE_SGML, test -d ${srcdir}/../docbook)
+
+AC_OUTPUT(Makefile webgpg)
diff --git a/gpglib/delete.c b/gpglib/delete.c
new file mode 100644
index 0000000..53943c9
--- /dev/null
+++ b/gpglib/delete.c
@@ -0,0 +1,77 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+
+/*
+** Delete a key.
+*/
+
+static int dodeletekey(int (*)(const char *, size_t, void *),
+ void *);
+
+int libmail_gpg_deletekey(const char *gpgdir, int secret,
+ const char *fingerprint,
+ int (*dump_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ char *argvec[8];
+ int rc;
+
+ argvec[0]="gpg";
+ argvec[1]="--command-fd";
+ argvec[2]="0";
+ argvec[3]= secret ? "--delete-secret-key":"--delete-key";
+ argvec[4]="-q";
+ argvec[5]="--no-tty";
+ argvec[6]=(char *)fingerprint;
+ argvec[7]=0;
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL,
+ gpgdir, argvec) < 0)
+ rc= -1;
+ else
+ {
+ int rc2;
+
+ rc=dodeletekey(dump_func, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc2)
+ rc=rc2;
+ }
+ return (rc);
+}
+
+static int dodeletekey(int (*dump_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ int rc=libmail_gpg_write("Y\n", 2, dump_func, NULL, NULL, 0, voidarg);
+ int rc2;
+
+ if (rc == 0)
+ rc=libmail_gpg_read(dump_func, NULL, NULL, 0, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc == 0)
+ rc=rc2;
+ return (rc);
+}
diff --git a/gpglib/export.c b/gpglib/export.c
new file mode 100644
index 0000000..66a4a05
--- /dev/null
+++ b/gpglib/export.c
@@ -0,0 +1,64 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+/*
+** List keys
+*/
+
+int libmail_gpg_exportkey(const char *gpgdir,
+ int secret,
+ const char *fingerprint,
+ int (*out_func)(const char *, size_t, void *),
+ int (*err_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ char *argvec[6];
+ int rc;
+
+ argvec[0]="gpg";
+ argvec[1]="--armor";
+ argvec[2]="--no-tty";
+ argvec[3]= secret ? "--export-secret-keys":"--export";
+ argvec[4]=(char *)fingerprint;
+ argvec[5]=0;
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout,
+ &libmail_gpg_stderr, gpgdir, argvec) < 0)
+ rc= -1;
+ else
+ {
+ int rc2;
+
+ close(libmail_gpg_stdin);
+ libmail_gpg_stdin=-1;
+
+ rc=libmail_gpg_read(out_func, err_func, NULL, 0, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc2)
+ rc=rc2;
+ }
+ return (rc);
+}
diff --git a/gpglib/fork.c b/gpglib/fork.c
new file mode 100644
index 0000000..b44c0ac
--- /dev/null
+++ b/gpglib/fork.c
@@ -0,0 +1,359 @@
+/*
+** Copyright 2001-2006 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include "gpg.h"
+#include "gpglib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+
+/*
+** Helper function: for and run pgp, with the given file descriptors and
+** options.
+*/
+
+pid_t libmail_gpg_fork(int *libmail_gpg_stdin, int *libmail_gpg_stdout,
+ int *libmail_gpg_stderr,
+ const char *gpgdir,
+ char **argvec)
+{
+ int pipein[2], pipeout[2], pipeerr[2];
+ pid_t p;
+ char *s;
+
+ if (libmail_gpg_stdin && pipe(pipein) < 0)
+ return (-1);
+
+ if (libmail_gpg_stdout && pipe(pipeout) < 0)
+ {
+ if (libmail_gpg_stdin)
+ {
+ close(pipein[0]);
+ close(pipein[1]);
+ }
+ return (-1);
+ }
+
+ if (libmail_gpg_stderr && pipe(pipeerr) < 0)
+ {
+ if (libmail_gpg_stdout)
+ {
+ close(pipeout[0]);
+ close(pipeout[1]);
+ }
+
+ if (libmail_gpg_stdin)
+ {
+ close(pipein[0]);
+ close(pipein[1]);
+ }
+ return (-1);
+ }
+
+ signal(SIGCHLD, SIG_DFL);
+ p=libmail_gpg_pid=fork();
+ if (p < 0)
+ {
+ if (libmail_gpg_stderr)
+ {
+ close(pipeerr[0]);
+ close(pipeerr[1]);
+ }
+
+ if (libmail_gpg_stdout)
+ {
+ close(pipeout[0]);
+ close(pipeout[1]);
+ }
+ if (libmail_gpg_stdin)
+ {
+ close(pipein[0]);
+ close(pipein[1]);
+ }
+
+ return (-1);
+ }
+
+ if (p)
+ {
+ signal(SIGPIPE, SIG_IGN);
+
+ if (libmail_gpg_stderr)
+ {
+ close(pipeerr[1]);
+ *libmail_gpg_stderr=pipeerr[0];
+ }
+
+ if (libmail_gpg_stdout)
+ {
+ close(pipeout[1]);
+ *libmail_gpg_stdout=pipeout[0];
+ }
+
+ if (libmail_gpg_stdin)
+ {
+ close(pipein[0]);
+ *libmail_gpg_stdin=pipein[1];
+ }
+ return (0);
+ }
+
+ if (libmail_gpg_stderr)
+ {
+ dup2(pipeerr[1], 2);
+ close(pipeerr[0]);
+ close(pipeerr[1]);
+ }
+ else if (libmail_gpg_stdout)
+ {
+ dup2(pipeout[1], 2);
+ }
+
+ if (libmail_gpg_stdout)
+ {
+ dup2(pipeout[1], 1);
+ close(pipeout[0]);
+ close(pipeout[1]);
+ }
+
+ if (libmail_gpg_stdin)
+ {
+ dup2(pipein[0], 0);
+ close(pipein[0]);
+ close(pipein[1]);
+ }
+
+ if (gpgdir)
+ {
+ s=malloc(sizeof("GNUPGHOME=")+strlen(gpgdir));
+ if (!s)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ strcat(strcpy(s, "GNUPGHOME="), gpgdir);
+ if (putenv(s) < 0)
+ {
+ perror("putenv");
+ exit(1);
+ }
+ }
+
+ {
+ const char *gpg=getenv("GPG");
+ if (!gpg || !*gpg)
+ gpg=GPG;
+
+ execv(gpg, argvec);
+ perror(gpg);
+ }
+ _exit(1);
+ return (0);
+}
+
+int libmail_gpg_write(const char *p, size_t cnt,
+ int (*stdout_func)(const char *, size_t, void *),
+ int (*stderr_func)(const char *, size_t, void *),
+ int (*timeout_func)(void *),
+ unsigned timeout,
+ void *voidarg)
+{
+ char buf[BUFSIZ];
+
+ fd_set fdr, fdw;
+ struct timeval tv;
+
+ if (!timeout_func)
+ timeout=0;
+ while (cnt)
+ {
+ int maxfd=0;
+ int n;
+
+ FD_ZERO(&fdr);
+ FD_ZERO(&fdw);
+
+ FD_SET(libmail_gpg_stdin, &fdw);
+
+ if (libmail_gpg_stdout >= 0)
+ {
+ FD_SET(libmail_gpg_stdout, &fdr);
+ if (libmail_gpg_stdout > maxfd)
+ maxfd=libmail_gpg_stdout;
+ }
+
+ if (libmail_gpg_stderr >= 0)
+ {
+ FD_SET(libmail_gpg_stderr, &fdr);
+ if (libmail_gpg_stderr > maxfd)
+ maxfd=libmail_gpg_stderr;
+ }
+
+ tv.tv_usec=0;
+ tv.tv_sec=timeout;
+ n=select(maxfd+1, &fdr, &fdw, NULL, timeout ? &tv:NULL);
+ if (n == 0)
+ {
+ n=(*timeout_func)(voidarg);
+ if (n)
+ return(n);
+ continue;
+ }
+ if (n < 0)
+ continue;
+
+ if (FD_ISSET(libmail_gpg_stdin, &fdw))
+ {
+ int n=write(libmail_gpg_stdin, p, cnt);
+
+ if (n <= 0)
+ return (-1);
+
+ p += n;
+ cnt -= n;
+ }
+
+ if (libmail_gpg_stdout >= 0 &&
+ FD_ISSET(libmail_gpg_stdout, &fdr))
+ {
+ int n=read(libmail_gpg_stdout, buf, sizeof(buf));
+
+ if (n <= 0)
+ {
+ close(libmail_gpg_stdout);
+ libmail_gpg_stdout= -1;
+ }
+ else if (stdout_func &&
+ (n=(*stdout_func)(buf, n, voidarg)) != 0)
+ return (n);
+ }
+
+ if (libmail_gpg_stderr >= 0 &&
+ FD_ISSET(libmail_gpg_stderr, &fdr))
+ {
+ int n=read(libmail_gpg_stderr, buf, sizeof(buf));
+
+ if (n <= 0)
+ {
+ close(libmail_gpg_stderr);
+ libmail_gpg_stderr= -1;
+ }
+ else if (stderr_func &&
+ (n=(*stderr_func)(buf, n, voidarg)) != 0)
+ return (n);
+ }
+ }
+ return (0);
+}
+
+int libmail_gpg_read(int (*stdout_func)(const char *, size_t, void *),
+ int (*stderr_func)(const char *, size_t, void *),
+ int (*timeout_func)(void *),
+ unsigned timeout,
+ void *voidarg)
+{
+ char buf[BUFSIZ];
+
+ fd_set fdr;
+ struct timeval tv;
+
+ if (libmail_gpg_stdin >= 0)
+ {
+ close(libmail_gpg_stdin);
+ libmail_gpg_stdin= -1;
+ }
+
+ if (!timeout_func)
+ timeout=0;
+
+ while ( libmail_gpg_stdout >= 0 || libmail_gpg_stderr >= 0)
+ {
+ int maxfd=0;
+ int n;
+
+ FD_ZERO(&fdr);
+
+ if (libmail_gpg_stdout >= 0)
+ {
+ FD_SET(libmail_gpg_stdout, &fdr);
+ if (libmail_gpg_stdout > maxfd)
+ maxfd=libmail_gpg_stdout;
+ }
+
+ if (libmail_gpg_stderr >= 0)
+ {
+ FD_SET(libmail_gpg_stderr, &fdr);
+ if (libmail_gpg_stderr > maxfd)
+ maxfd=libmail_gpg_stderr;
+ }
+
+ tv.tv_usec=0;
+ tv.tv_sec=timeout;
+ n=select(maxfd+1, &fdr, NULL, NULL, timeout ? &tv:NULL);
+
+ if (n == 0)
+ {
+ n=(*timeout_func)(voidarg);
+ if (n)
+ return(n);
+ continue;
+ }
+ if (n < 0)
+ continue;
+
+ if (libmail_gpg_stdout >= 0 &&
+ FD_ISSET(libmail_gpg_stdout, &fdr))
+ {
+ int n=read(libmail_gpg_stdout, buf, sizeof(buf));
+
+ if (n <= 0)
+ {
+ close(libmail_gpg_stdout);
+ libmail_gpg_stdout= -1;
+ }
+ else if (stdout_func &&
+ (n=(*stdout_func)(buf, n, voidarg)) != 0)
+ return (n);
+ }
+
+ if (libmail_gpg_stderr >= 0 &&
+ FD_ISSET(libmail_gpg_stderr, &fdr))
+ {
+ int n=read(libmail_gpg_stderr, buf, sizeof(buf));
+
+ if (n <= 0)
+ {
+ close(libmail_gpg_stderr);
+ libmail_gpg_stderr= -1;
+ }
+ else if (stderr_func &&
+ (n=(*stderr_func)(buf, n, voidarg)) != 0)
+ return (n);
+ }
+ }
+ return (0);
+}
diff --git a/gpglib/genkey.c b/gpglib/genkey.c
new file mode 100644
index 0000000..9e72f1c
--- /dev/null
+++ b/gpglib/genkey.c
@@ -0,0 +1,209 @@
+/*
+** Copyright 2001-2011 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+
+/*
+** Generate a new key.
+*/
+
+static int dogenkey(const char *, const char *, const char *,
+ int,
+ int,
+ unsigned,
+ char,
+ const char *passphrase,
+ int (*)(const char *, size_t, void *),
+ int (*)(void *),
+ void *);
+
+int libmail_gpg_genkey(const char *gpgdir,
+ const char *charset,
+ const char *name,
+ const char *addr,
+ const char *comment,
+ int skeylen,
+ int ekeylen,
+ unsigned expire,
+ char expire_unit,
+ const char *passphrase,
+ int (*dump_func)(const char *, size_t, void *),
+ int (*timeout_func)(void *),
+ void *voidarg)
+{
+ char *name_u, *addr_u, *comment_u;
+ char *argvec[4];
+ int rc;
+
+ name_u=libmail_u_convert_toutf8(name, charset, NULL);
+
+ if (!name_u)
+ return (-1);
+
+ addr_u=libmail_u_convert_toutf8(addr, charset, NULL);
+ if (!addr_u)
+ {
+ free(name_u);
+ return (-1);
+ }
+
+ comment_u=libmail_u_convert_toutf8(comment, charset, NULL);
+ if (!comment_u)
+ return (-1);
+
+
+ argvec[0]="gpg";
+ argvec[1]="--gen-key";
+ argvec[2]="--batch";
+ argvec[3]=NULL;
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL,
+ gpgdir, argvec) < 0)
+ rc= -1;
+ else
+ {
+ int rc2;
+
+ rc=dogenkey(name_u, addr_u, comment_u,
+ skeylen, ekeylen, expire, expire_unit,
+ passphrase,
+ dump_func, timeout_func, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc2)
+ rc=rc2;
+ }
+ free(comment_u);
+ free(name_u);
+ free(addr_u);
+ return (rc);
+}
+
+static char *mkcmdbuf(const char *, const char *, const char *,
+ int,
+ int,
+ unsigned,
+ char, const char *);
+
+static int dogenkey(const char *name, const char *addr, const char *comment,
+ int skeylen,
+ int ekeylen,
+ unsigned expire,
+ char expire_unit,
+ const char *passphrase,
+ int (*dump_func)(const char *, size_t, void *),
+ int (*timeout_func)(void *),
+ void *voidarg)
+{
+ char *cmd_buf=mkcmdbuf(name, addr, comment, skeylen, ekeylen,
+ expire, expire_unit, passphrase);
+
+ int rc=libmail_gpg_write(cmd_buf, strlen(cmd_buf), dump_func, NULL,
+ timeout_func, 5, voidarg);
+ int rc2;
+
+ free(cmd_buf);
+ if (rc == 0)
+ rc=libmail_gpg_read(dump_func, NULL, timeout_func, 5, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc == 0)
+ rc=rc2;
+ return (rc);
+}
+
+static char *mkcmdbuf(const char *name, const char *addr, const char *comment,
+ int skeylen,
+ int ekeylen,
+ unsigned expire,
+ char expire_unit,
+ const char *passphrase)
+{
+ static const char genkey_cmd[]=
+ "Key-Type: DSA\n"
+ "Key-Length: %s\n"
+ "Subkey-Type: ELG-E\n"
+ "Subkey-Length: %s\n"
+ "%s%s%s"
+ "%s%s%s"
+ "%s%s%s"
+ "Name-Email: %s\n"
+ "Expire-Date: %s\n"
+ "%%commit\n";
+
+ static const char namereal1[]="Name-Real: ";
+ static const char namereal2[]="\n";
+ static const char comment1[]="Name-Comment: ";
+ static const char comment2[]="\n";
+ static const char passphrase1[]="Passphrase: ";
+ static const char passphrase2[]="\n";
+
+ char skl_buf[NUMBUFSIZE];
+ char kl_buf[NUMBUFSIZE];
+ char exp_buf[NUMBUFSIZE+1];
+
+ char *p;
+
+ libmail_str_size_t(skeylen, skl_buf);
+ libmail_str_size_t(ekeylen, kl_buf);
+ if (expire == 0)
+ {
+ strcpy(exp_buf, "0");
+ }
+ else
+ {
+ char b[2];
+
+ b[0]=expire_unit;
+ b[1]=0;
+
+ strcat(libmail_str_size_t(expire, exp_buf), b);
+ }
+
+ p=malloc(512+strlen(kl_buf)+strlen(skl_buf)+strlen(exp_buf)
+ +strlen(name)+strlen(addr)+strlen(comment)
+ +strlen(passphrase));
+
+ if (!p)
+ return (NULL);
+
+ while (*comment == ' ' || *comment == '\t')
+ ++comment;
+
+ sprintf(p, genkey_cmd, skl_buf, kl_buf,
+
+ *name ? namereal1:"",
+ name,
+ *name ? namereal2:"",
+
+ *comment ? comment1:"",
+ comment,
+ *comment ? comment2:"",
+
+ *passphrase ? passphrase1:"",
+ passphrase,
+ *passphrase ? passphrase2:"",
+
+ addr, exp_buf);
+ return (p);
+}
diff --git a/gpglib/gpg.c b/gpglib/gpg.c
new file mode 100644
index 0000000..b63214c
--- /dev/null
+++ b/gpglib/gpg.c
@@ -0,0 +1,2235 @@
+/*
+** Copyright 2001-2011 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <sys/types.h>
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include "mimegpgheader.h"
+#include "mimegpgstack.h"
+#include "mimegpgfork.h"
+#include "tempname.h"
+#include "gpglib.h"
+#include "rfc822/encode.h"
+#include "rfc2045/rfc2045.h"
+
+static int my_rewind(FILE *fp)
+{
+ if (fflush(fp) || ferror(fp) || fseek(fp, 0L, SEEK_SET))
+ return (-1);
+ clearerr(fp);
+ return (0);
+}
+
+int libmail_gpg_inputfunc_readfp(char *buf, size_t cnt, void *vp)
+{
+ FILE *fp=(FILE *)vp;
+ size_t i;
+ int c;
+
+ if (cnt == 0)
+ return -1;
+
+ --cnt;
+
+ for (i=0; i<cnt; i++)
+ {
+ if ((c=getc(fp)) == EOF)
+ {
+ if (i == 0)
+ return -1;
+ break;
+ }
+ buf[i]=c;
+
+ if (c == '\n')
+ {
+ ++i;
+ break;
+ }
+ }
+ buf[i]=0;
+ return 0;
+}
+
+void libmail_gpg_noexec(int fd)
+{
+#ifdef FD_CLOEXEC
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+}
+
+/*
+** Check if the line just read is a MIME boundary line. Search the
+** current MIME stack for a matching MIME boundary delimiter.
+*/
+
+static struct mimestack *is_boundary(struct mimestack *s, const char *line,
+ int *isclosing)
+{
+ struct mimestack *b;
+
+ if (line[0] != '-' || line[1] != '-' ||
+ (b=libmail_mimestack_search(s, line+2)) == 0)
+ return (NULL);
+
+
+ *isclosing=strncmp(line+2+strlen(b->boundary), "--", 2) == 0;
+ return (b);
+}
+
+static const char *get_boundary(struct mimestack *,
+ const char *,
+ FILE *);
+
+/*
+** Skip until EOF or a MIME boundary delimiter other than a closing MIME
+** boundary delimiter. After returning from bind_boundary we expect to
+** see MIME headers. Copy any intermediate lines to fpout.
+*/
+
+static void find_boundary(struct mimestack **stack, int *iseof,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int doappend)
+{
+ char buf[BUFSIZ];
+
+ for (;;)
+ {
+ int is_closing;
+ struct mimestack *b;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ return;
+ }
+
+ if (!(b=is_boundary(*stack, buf, &is_closing)))
+ {
+ if (output_func)
+ (*output_func)(buf, strlen(buf),
+ output_func_arg);
+
+ while (strchr(buf, '\n') == 0)
+ {
+ if ( (*input_func)(buf, sizeof(buf),
+ input_func_arg))
+ {
+ *iseof=1;
+ return;
+ }
+ if (output_func)
+ (*output_func)(buf, strlen(buf),
+ output_func_arg);
+ }
+ continue;
+ }
+
+ if (output_func)
+ {
+ (*output_func)("--", 2, output_func_arg);
+ (*output_func)(b->boundary, strlen(b->boundary),
+ output_func_arg);
+
+ if (is_closing)
+ (*output_func)("--", 2, output_func_arg);
+
+ (*output_func)("\n", 1, output_func_arg);
+ }
+
+ if (is_closing)
+ {
+ libmail_mimestack_pop_to(stack, b);
+ continue;
+ }
+ break;
+ }
+}
+
+
+/*
+** Read a set of headers.
+*/
+
+static struct header *read_headers(struct mimestack **stack, int *iseof,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int doappend,
+ int *errflag)
+{
+ char buf[BUFSIZ];
+ struct read_header_context rhc;
+ struct header *h;
+
+ *errflag=0;
+ libmail_readheader_init(&rhc);
+
+ while (!*iseof)
+ {
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+
+ if (READ_START_OF_LINE(rhc))
+ {
+ struct mimestack *b;
+ int is_closing;
+
+ if (strcmp(buf, "\n") == 0
+ || strcmp(buf, "\r\n") == 0)
+ break;
+
+ b=is_boundary(*stack, buf, &is_closing);
+
+ if (b)
+ {
+ /*
+ ** Corrupted MIME message. We should NOT
+ ** see a MIME boundary in the middle of the
+ ** headers!
+ **
+ ** Ignore this damage.
+ */
+
+ struct header *p;
+
+ h=libmail_readheader_finish(&rhc);
+
+ for (p=h; p; p=p->next)
+ (*output_func)(p->header,
+ strlen(p->header),
+ output_func_arg);
+
+ (*output_func)("--", 2, output_func_arg);
+ (*output_func)(b->boundary,
+ strlen(b->boundary),
+ output_func_arg);
+
+ if (is_closing)
+ (*output_func)("--", 2,
+ output_func_arg);
+
+ (*output_func)("\n", 1, output_func_arg);
+
+ if (is_closing)
+ {
+ libmail_mimestack_pop_to(stack, b);
+ find_boundary(stack, iseof,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg,
+ doappend);
+ }
+ libmail_header_free(h);
+
+ libmail_readheader_init(&rhc);
+ continue; /* From the top */
+ }
+ }
+ if (libmail_readheader(&rhc, buf) < 0)
+ {
+ libmail_header_free(libmail_readheader_finish(&rhc));
+ *errflag= -1;
+ return NULL;
+ }
+ }
+
+ return (libmail_readheader_finish(&rhc));
+}
+
+/*
+** Here we do actual signing/encoding
+*/
+
+static int encode_header(const char *h)
+{
+ if (strncasecmp(h, "content-", 8) == 0)
+ return (1);
+ return (0);
+}
+
+struct gpg_fork_output_info {
+ void (*output_func)(const char *, size_t, void *);
+ void *output_func_arg;
+ struct gpgmime_forkinfo *gpgptr;
+};
+
+static int gpg_fork_output(const char *p, size_t n, void *dummy)
+{
+ struct gpg_fork_output_info *info=(struct gpg_fork_output_info *)dummy;
+
+ (*info->output_func)(p, n, info->output_func_arg);
+ return 0;
+}
+
+
+static int dogpgencrypt(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack **stack,
+ struct header *h, int *iseof,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int argc,
+ char **argv,
+ int dosign,
+ void (*errhandler)(const char *, void *),
+ void *errhandler_arg)
+{
+ struct header *hp;
+ char buf[BUFSIZ];
+ struct gpgmime_forkinfo gpg;
+ int clos_flag=0;
+ struct mimestack *b=0;
+ int rc;
+ const char *boundary;
+ int need_crlf;
+ struct gpg_fork_output_info gfoi;
+
+ boundary=get_boundary(*stack, "", NULL);
+
+ gfoi.output_func=output_func;
+ gfoi.output_func_arg=output_func_arg;
+
+ if (libmail_gpgmime_forksignencrypt(gpghome, passphrase_fd,
+ (dosign ? GPG_SE_SIGN:0)
+ | GPG_SE_ENCRYPT,
+ argc, argv,
+ &gpg_fork_output, &gfoi,
+ &gpg))
+ {
+ return -1;
+ }
+
+ for (hp=h; hp; hp=hp->next)
+ {
+ if (encode_header(hp->header))
+ continue;
+
+ (*output_func)(hp->header, strlen(hp->header),
+ output_func_arg);
+ }
+
+#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg)
+#define S(s) (*output_func)( s, strlen(s), output_func_arg)
+
+
+ C("Content-Type: multipart/encrypted;\n"
+ " boundary=\"");
+
+ S(boundary);
+
+ C("\";\n"
+ " protocol=\"application/pgp-encrypted\"\n"
+ "\n"
+ "This is a MIME GnuPG-encrypted message. If you see this text, it means\n"
+ "that your E-mail or Usenet software does not support MIME encrypted messages.\n"
+ "The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.\n"
+ "To open this message correctly you will need to install E-mail or Usenet\n"
+ "software that supports modern Internet standards.\n"
+ "\n--");
+
+ S(boundary);
+
+ C("\n"
+ "Content-Type: application/pgp-encrypted\n"
+ "Content-Transfer-Encoding: 7bit\n"
+ "\n"
+ "Version: 1\n"
+ "\n--");
+
+ S(boundary);
+
+ C("\n"
+ "Content-Type: application/octet-stream\n"
+ "Content-Transfer-Encoding: 7bit\n\n");
+
+#undef C
+#undef S
+
+ /* For Eudora compatiblity */
+ libmail_gpgmime_write(&gpg, "Mime-Version: 1.0\r\n", 19);
+
+ for (hp=h; hp; hp=hp->next)
+ {
+ const char *p;
+
+ if (!encode_header(hp->header))
+ continue;
+
+ for (p=hp->header; *p; p++)
+ {
+ if (*p == '\r')
+ continue;
+
+ if (*p == '\n')
+ libmail_gpgmime_write(&gpg, "\r\n", 2);
+ else
+ libmail_gpgmime_write(&gpg, p, 1);
+ }
+ }
+
+ /*
+ ** Chew the content until the next MIME boundary.
+ */
+ need_crlf=1;
+
+ while (!*iseof)
+ {
+ const char *p;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+
+ if (need_crlf)
+ {
+ if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL)
+ break;
+
+ libmail_gpgmime_write(&gpg, "\r\n", 2);
+ }
+
+ need_crlf=0;
+ for (;;)
+ {
+ for (p=buf; *p; p++)
+ {
+ if (*p == '\r')
+ continue;
+ if (*p == '\n')
+ {
+ need_crlf=1;
+ break;
+ }
+
+ libmail_gpgmime_write(&gpg, p, 1);
+ }
+ if (*p == '\n')
+ break;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** This needs some 'splainin. Note that we spit out a newline at
+ ** the BEGINNING of each line, above. This generates the blank
+ ** header->body separator line. Now, if we're NOT doing multiline
+ ** content, we need to follow the last line of the content with a
+ ** newline. If we're already doing multiline content, that extra
+ ** newline (if it exists) is already there.
+ */
+
+ if (!*stack)
+ {
+ libmail_gpgmime_write(&gpg, "\r\n", 2);
+ }
+
+ rc=libmail_gpgmime_finish(&gpg);
+
+ if (rc)
+ {
+ (*errhandler)(libmail_gpgmime_getoutput(&gpg), errhandler_arg);
+ return (-1);
+ }
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(boundary, strlen(boundary), output_func_arg);
+ (*output_func)("--\n", 3, output_func_arg);
+
+ if (*iseof)
+ return 0;
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(b->boundary, strlen(b->boundary), output_func_arg);
+ if (clos_flag)
+ (*output_func)("--", 2, output_func_arg);
+ (*output_func)("\n", 1, output_func_arg);
+
+ if (clos_flag)
+ {
+ libmail_mimestack_pop_to(stack, b);
+ find_boundary(stack, iseof, input_func,
+ input_func_arg, output_func,
+ output_func_arg, 1);
+ }
+
+ return 0;
+}
+
+static int dogpgsign(const char *gpghome, const char *passphrase_fd,
+ struct mimestack **stack, struct header *h, int *iseof,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int argc,
+ char **argv,
+ void (*errhandler)(const char *, void *),
+ void *errhandler_arg)
+{
+ struct header *hp;
+ char buf[8192];
+ struct gpgmime_forkinfo gpg;
+ int clos_flag=0;
+ struct mimestack *b=0;
+ int rc=0;
+ char signed_content_name[TEMPNAMEBUFSIZE];
+ int signed_content;
+ FILE *signed_content_fp;
+ const char *boundary;
+ int need_crlf;
+ struct gpg_fork_output_info gfoi;
+
+ for (hp=h; hp; hp=hp->next)
+ {
+ if (encode_header(hp->header))
+ continue;
+ (*output_func)(hp->header, strlen(hp->header),
+ output_func_arg);
+ }
+
+ signed_content=libmail_tempfile(signed_content_name);
+ if (signed_content < 0 ||
+ (signed_content_fp=fdopen(signed_content, "w+")) == NULL)
+ {
+ if (signed_content >= 0)
+ {
+ close(signed_content);
+ unlink(signed_content_name);
+ }
+ return -1;
+
+ }
+ libmail_gpg_noexec(fileno(signed_content_fp));
+ unlink(signed_content_name); /* UNIX semantics */
+
+ for (hp=h; hp; hp=hp->next)
+ {
+ const char *p;
+
+ if (!encode_header(hp->header))
+ continue;
+
+ for (p=hp->header; *p; p++)
+ {
+ if (*p == '\r')
+ continue;
+
+ if (*p == '\n')
+ putc('\r', signed_content_fp);
+ putc(*p, signed_content_fp);
+ }
+ }
+
+ /*
+ ** Chew the content until the next MIME boundary.
+ */
+ need_crlf=1;
+ while (!*iseof)
+ {
+ const char *p;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+
+ if (need_crlf)
+ {
+ if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL)
+ break;
+
+ fprintf(signed_content_fp, "\r\n");
+ }
+
+ need_crlf=0;
+ for (;;)
+ {
+ for (p=buf; *p; p++)
+ {
+ if (*p == '\r')
+ continue;
+ if (*p == '\n')
+ {
+ need_crlf=1;
+ break;
+ }
+
+ putc(*p, signed_content_fp);
+ }
+ if (*p == '\n')
+ break;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** This needs some 'splainin. Note that we spit out a newline at
+ ** the BEGINNING of each line, above. This generates the blank
+ ** header->body separator line. Now, if we're NOT doing multiline
+ ** content, we need to follow the last line of the content with a
+ ** newline. If we're already doing multiline content, that extra
+ ** newline (if it exists) is already there.
+ */
+
+ if (!*stack)
+ {
+ fprintf(signed_content_fp, "\r\n");
+ }
+
+ if (fflush(signed_content_fp) < 0 || ferror(signed_content_fp))
+ {
+ fclose(signed_content_fp);
+ return (-1);
+ }
+
+ boundary=get_boundary(*stack, "", signed_content_fp);
+
+ if (my_rewind(signed_content_fp) < 0)
+ {
+ fclose(signed_content_fp);
+ return (-1);
+ }
+
+#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg)
+#define S(s) (*output_func)( s, strlen(s), output_func_arg)
+
+ C("Content-Type: multipart/signed;\n"
+ " boundary=\"");
+ S(boundary);
+ C("\";\n"
+ " micalg=pgp-sha1;"
+ " protocol=\"application/pgp-signature\"\n"
+ "\n"
+ "This is a MIME GnuPG-signed message. If you see this text, it means that\n"
+ "your E-mail or Usenet software does not support MIME signed messages.\n"
+ "The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.\n"
+ "To open this message correctly you will need to install E-mail or Usenet\n"
+ "software that supports modern Internet standards.\n"
+ "\n--");
+ S(boundary);
+ C("\n");
+
+
+ gfoi.output_func=output_func;
+ gfoi.output_func_arg=output_func_arg;
+ gfoi.gpgptr= &gpg;
+
+ if (libmail_gpgmime_forksignencrypt(gpghome, passphrase_fd,
+ GPG_SE_SIGN,
+ argc, argv,
+ &gpg_fork_output, &gfoi,
+ &gpg))
+ {
+ fclose(signed_content_fp);
+ return (-1);
+ }
+
+ while (fgets(buf, sizeof(buf), signed_content_fp) != NULL)
+ {
+ char *p;
+ size_t j, k;
+
+ libmail_gpgmime_write(&gpg, buf, strlen(buf));
+
+ p=buf;
+ for (j=k=0; p[j]; j++)
+ if (p[j] != '\r')
+ p[k++]=p[j];
+
+ if (k)
+ (*output_func)(p, k, output_func_arg);
+ }
+
+ C("\n--");
+ S(boundary);
+
+ C("\n"
+ "Content-Type: application/pgp-signature\n"
+ "Content-Transfer-Encoding: 7bit\n\n");
+
+#undef C
+#undef S
+
+ if (libmail_gpgmime_finish(&gpg))
+ rc= -1; /* TODO */
+
+ if (rc)
+ {
+ (*errhandler)(libmail_gpgmime_getoutput(&gpg),
+ errhandler_arg);
+ fclose(signed_content_fp);
+ return -1;
+ }
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(boundary, strlen(boundary), output_func_arg);
+ (*output_func)("--\n", 3, output_func_arg);
+
+ fclose(signed_content_fp);
+ if (*iseof)
+ return 0;
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(b->boundary, strlen(b->boundary), output_func_arg);
+ if (clos_flag)
+ (*output_func)("--", 2, output_func_arg);
+ (*output_func)("\n", 1, output_func_arg);
+
+ if (clos_flag)
+ {
+ libmail_mimestack_pop_to(stack, b);
+ find_boundary(stack, iseof, input_func, input_func_arg,
+ output_func, output_func_arg, 1);
+ }
+ return 0;
+}
+
+static int isgpg(struct mime_header *);
+static int checksign(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack **, int *, struct header *,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*)(const char *, size_t, void *),
+ void *,
+ int, char **,
+ int *);
+static int decrypt(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack **, int *, struct header *,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*)(const char *, size_t, void *),
+ void *,
+ int, char **,
+ int *);
+
+static void print_noncontent_headers(struct header *h,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg)
+{
+ struct header *p;
+
+ for (p=h; p; p=p->next)
+ {
+ if (strncasecmp(p->header, "content-", 8) == 0)
+ continue;
+ (*output_func)(p->header, strlen(p->header), output_func_arg);
+ }
+}
+
+static int dosignencode2(int dosign, int doencode, int dodecode,
+ const char *gpghome,
+ const char *passphrase_fd,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ void (*errhandler_func)(const char *, void *),
+ void *errhandler_arg,
+ int argc, char **argv,
+ int *status)
+{
+ struct mimestack *boundary_stack=0;
+ int iseof=0;
+
+ *status=0;
+
+ while (!iseof)
+ {
+ int errflag;
+
+ static const char ct_s[]="content-type:";
+ struct header *h=read_headers(&boundary_stack, &iseof,
+ input_func, input_func_arg,
+ output_func,
+ output_func_arg,
+ dodecode ? 0:1,
+ &errflag),
+ *hct;
+
+ if (errflag)
+ return 1;
+
+ if (iseof && !h)
+ continue; /* Artifact */
+
+ hct=libmail_header_find(h, ct_s);
+
+ /*
+ ** If this is a multipart MIME section, we can keep on
+ ** truckin'.
+ **
+ */
+
+ if (hct)
+ {
+ struct mime_header *mh=
+ libmail_mimeheader_parse(hct->header+
+ (sizeof(ct_s)-1));
+ const char *bv;
+
+ if (!mh)
+ {
+ libmail_header_free(h);
+ return (-1);
+ }
+
+ if (strcasecmp(mh->header_name, "multipart/x-mimegpg")
+ == 0)
+ {
+ /* Punt */
+
+ char *buf=malloc(strlen(hct->header)+100);
+ const char *p;
+
+ if (!buf)
+ {
+ libmail_mimeheader_free(mh);
+ libmail_header_free(h);
+ return (-1);
+ }
+ strcpy(buf, "Content-Type: multipart/mixed");
+ p=strchr(hct->header, ';');
+ strcat(buf, p ? p:"");
+ free(hct->header);
+ hct->header=buf;
+
+ libmail_mimeheader_free(mh);
+ mh=libmail_mimeheader_parse(hct->header+
+ sizeof(ct_s)-1);
+ if (!mh)
+ {
+ libmail_header_free(h);
+ return (-1);
+ }
+ }
+
+ if (strncasecmp(mh->header_name, "multipart/", 10)==0
+ && (bv=libmail_mimeheader_getattr(mh, "boundary")) != 0
+
+ && (doencode & LIBMAIL_GPG_ENCAPSULATE) == 0
+
+ && !dosign
+ )
+ {
+ struct header *p;
+
+ if (libmail_mimestack_push(&boundary_stack,
+ bv) < 0)
+ {
+ libmail_header_free(h);
+ return (-1);
+ }
+
+ if (dodecode)
+ {
+ int rc;
+
+ if (strcasecmp(mh->header_name,
+ "multipart/signed")==0
+ && (dodecode & LIBMAIL_GPG_CHECKSIGN)
+ && isgpg(mh))
+ {
+ int errflag;
+
+ print_noncontent_headers(h,
+ output_func,
+ output_func_arg
+ );
+ libmail_mimeheader_free(mh);
+ rc=checksign(gpghome,
+ passphrase_fd,
+ &boundary_stack,
+ &iseof,
+ h,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg,
+ argc, argv,
+ &errflag);
+ libmail_header_free(h);
+
+ if (errflag)
+ *status |=
+ LIBMAIL_ERR_VERIFYSIG;
+ if (rc)
+ return -1;
+
+ continue;
+ }
+
+ if (strcasecmp(mh->header_name,
+ "multipart/encrypted")
+ ==0
+ && (dodecode & LIBMAIL_GPG_UNENCRYPT)
+ && isgpg(mh))
+ {
+ int errflag;
+
+ print_noncontent_headers(h,
+ output_func,
+ output_func_arg
+ );
+ libmail_mimeheader_free(mh);
+ rc=decrypt(gpghome,
+ passphrase_fd,
+ &boundary_stack,
+ &iseof,
+ h,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg,
+ argc, argv,
+ &errflag);
+ libmail_header_free(h);
+
+ if (errflag)
+ *status |=
+ LIBMAIL_ERR_DECRYPT;
+ if (rc)
+ return -1;
+ continue;
+ }
+ }
+
+ for (p=h; p; p=p->next)
+ {
+ (*output_func)(p->header,
+ strlen(p->header),
+ output_func_arg);
+ }
+
+ (*output_func)("\n", 1, output_func_arg);
+ libmail_header_free(h);
+ libmail_mimeheader_free(mh);
+
+ find_boundary(&boundary_stack, &iseof,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg, dodecode ? 0:1);
+ continue;
+ }
+ libmail_mimeheader_free(mh);
+ }
+
+ if (dodecode)
+ {
+ struct header *p;
+ int is_message_rfc822=0;
+
+ for (p=h; p; p=p->next)
+ {
+ (*output_func)(p->header,
+ strlen(p->header),
+ output_func_arg);
+ }
+ (*output_func)("\n", 1, output_func_arg);
+
+ /*
+ ** If this is a message/rfc822 attachment, we can
+ ** resume reading the next set of headers.
+ */
+
+ hct=libmail_header_find(h, ct_s);
+ if (hct)
+ {
+ struct mime_header *mh=
+ libmail_mimeheader_parse(hct->header+
+ (sizeof(ct_s)
+ -1));
+ if (!mh)
+ {
+ libmail_header_free(h);
+ return (-1);
+ }
+
+ if (strcasecmp(mh->header_name,
+ "message/rfc822") == 0)
+ is_message_rfc822=1;
+ libmail_mimeheader_free(mh);
+ }
+ libmail_header_free(h);
+
+ if (!is_message_rfc822)
+ find_boundary(&boundary_stack, &iseof,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg, 0);
+ continue;
+ }
+
+ if (doencode ?
+ dogpgencrypt(gpghome,
+ passphrase_fd,
+ &boundary_stack, h, &iseof,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg,
+ argc, argv, dosign,
+ errhandler_func, errhandler_arg)
+ :
+ dogpgsign(gpghome,
+ passphrase_fd,
+ &boundary_stack, h, &iseof,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg,
+ argc, argv,
+ errhandler_func, errhandler_arg))
+ {
+ libmail_header_free(h);
+ return 1;
+ }
+
+ libmail_header_free(h);
+ }
+
+ return (0);
+}
+
+
+static int isgpg(struct mime_header *mh)
+{
+ const char *attr;
+
+ attr=libmail_mimeheader_getattr(mh, "protocol");
+
+ if (!attr)
+ return (0);
+
+ if (strcasecmp(attr, "application/pgp-encrypted") == 0)
+ return (1);
+
+ if (strcasecmp(attr, "application/pgp-signature") == 0)
+ {
+ return (1);
+ }
+ return (0);
+}
+
+static int nybble(char c)
+{
+ static const char x[]="0123456789ABCDEFabcdef";
+
+ const char *p=strchr(x, c);
+ int n;
+
+ if (!p) p=x;
+
+ n= p-x;
+ if (n >= 16)
+ n -= 6;
+ return (n);
+}
+
+/*
+** Check signature
+*/
+
+static int dochecksign(const char *, const char *,
+ struct mimestack *,
+ FILE *,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ const char *,
+ const char *,
+ int, char **, int *);
+
+static int checksign(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack **stack, int *iseof,
+ struct header *h,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int argc, char **argv, int *errptr)
+{
+ char buf[BUFSIZ];
+ struct header *h2;
+
+ char signed_content[TEMPNAMEBUFSIZE];
+ char signature[TEMPNAMEBUFSIZE];
+ int signed_file, signature_file;
+ FILE *signed_file_fp, *signature_file_fp;
+ int clos_flag;
+ int need_nl, check_boundary;
+ struct mimestack *b=0;
+ struct mime_header *mh;
+ int qpdecode=0;
+ int errflag;
+
+ *errptr=0;
+
+ signed_file=libmail_tempfile(signed_content);
+
+ if (signed_file < 0 || (signed_file_fp=fdopen(signed_file, "w+")) == 0)
+ {
+ if (signed_file > 0)
+ {
+ close(signed_file);
+ unlink(signed_content);
+ }
+ return -1;
+ }
+ libmail_gpg_noexec(fileno(signed_file_fp));
+
+ find_boundary(stack, iseof, input_func,
+ input_func_arg, NULL, NULL, 0);
+ if (*iseof)
+ return 0;
+
+ need_nl=0;
+ check_boundary=1;
+
+ while (!*iseof)
+ {
+ const char *p;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ continue;
+ }
+
+ if (check_boundary
+ && (b=is_boundary(*stack, buf, &clos_flag)) != 0)
+ break;
+ if (need_nl)
+ fprintf(signed_file_fp, "\r\n");
+
+ for (p=buf; *p && *p != '\n'; p++)
+ putc(*p, signed_file_fp);
+ need_nl=check_boundary= *p != 0;
+ }
+
+ if (my_rewind(signed_file_fp) < 0)
+ {
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ return -1;
+ }
+
+ if (clos_flag)
+ {
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ if (b)
+ libmail_mimestack_pop_to(stack, b);
+ find_boundary(stack, iseof, input_func, input_func_arg,
+ output_func, output_func_arg, 1);
+ return 0;
+ }
+
+ h=read_headers(stack, iseof, input_func, input_func_arg,
+ output_func, output_func_arg, 0, &errflag);
+
+ if (errflag)
+ {
+ fclose(signed_file_fp);
+ unlink(signed_content);
+
+ return (-1);
+ }
+
+ if (!h || !(h2=libmail_header_find(h, "content-type:")))
+ {
+ if (h)
+ libmail_header_free(h);
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ if (!*iseof)
+ find_boundary(stack, iseof, input_func, input_func_arg,
+ output_func, output_func_arg, 1);
+ return 0;
+ }
+
+ mh=libmail_mimeheader_parse(h2->header+sizeof("content-type:")-1);
+
+ if (!mh)
+ {
+ libmail_header_free(h);
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ return (-1);
+ }
+
+ if (strcasecmp(mh->header_name, "application/pgp-signature"))
+ {
+ libmail_mimeheader_free(mh);
+ libmail_header_free(h);
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ if (!*iseof)
+ find_boundary(stack, iseof, input_func, input_func_arg,
+ output_func, output_func_arg, 1);
+ return (0);
+ }
+ libmail_mimeheader_free(mh);
+
+ /*
+ ** In rare instances, the signature is qp-encoded.
+ */
+
+ if ((h2=libmail_header_find(h, "content-transfer-encoding:")) != NULL)
+ {
+ mh=libmail_mimeheader_parse
+ (h2->header+sizeof("content-transfer-encoding:")-1);
+
+ if (!mh)
+ {
+ libmail_header_free(h);
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ return -1;
+ }
+
+ if (strcasecmp(mh->header_name,
+ "quoted-printable") == 0)
+ qpdecode=1;
+ libmail_mimeheader_free(mh);
+ }
+ libmail_header_free(h);
+
+ signature_file=libmail_tempfile(signature);
+
+ if (signature_file < 0
+ || (signature_file_fp=fdopen(signature_file, "w+")) == 0)
+ {
+ if (signature_file > 0)
+ {
+ close(signature_file);
+ unlink(signature);
+ }
+ unlink(signed_content);
+ return (-1);
+ }
+
+ while (!*iseof)
+ {
+ const char *p;
+
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ continue;
+ }
+
+ if ((b=is_boundary(*stack, buf, &clos_flag)) != 0)
+ break;
+
+ for (p=buf; *p; p++)
+ {
+ int n;
+
+ if (!qpdecode)
+ {
+ putc(*p, signature_file_fp);
+ continue;
+ }
+
+ if (*p == '=' && p[1] == '\n')
+ break;
+
+ if (*p == '=' && p[1] && p[2])
+ {
+ n=nybble(p[1]) * 16 + nybble(p[2]);
+ if ( (char)n )
+ {
+ putc((char)n, signature_file_fp);
+ p += 2;
+ }
+ p += 2;
+ continue;
+ }
+ putc(*p, signature_file_fp);
+
+ /* If some spits out qp-lines > BUFSIZ, they deserve
+ ** this crap.
+ */
+ }
+ }
+
+ fflush(signature_file_fp);
+ if (ferror(signature_file_fp))
+ {
+ unlink(signature);
+ fclose(signed_file_fp);
+ unlink(signed_content);
+ return -1;
+ }
+ if (fclose(signature_file_fp))
+ {
+ unlink(signature);
+ unlink(signed_content);
+ return -1;
+ }
+
+ errflag=dochecksign(gpghome,
+ passphrase_fd,
+ *stack, signed_file_fp,
+ output_func, output_func_arg, signed_content,
+ signature,
+ argc, argv, errptr);
+
+ fclose(signed_file_fp);
+ unlink(signature);
+ unlink(signed_content);
+
+ if (errflag)
+ return -1;
+
+ while (!clos_flag)
+ {
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+ if (!(b=is_boundary(*stack, buf, &clos_flag)))
+ clos_flag=0;
+ }
+ if (b)
+ libmail_mimestack_pop_to(stack, b);
+
+ return 0;
+}
+
+static const char *newboundary()
+{
+ static char buffer[256];
+ static unsigned counter=0;
+ time_t t;
+ char hostnamebuf[256];
+
+ time(&t);
+ hostnamebuf[sizeof(hostnamebuf)-1]=0;
+ if (gethostname(hostnamebuf, sizeof(hostnamebuf)-1) < 0)
+ hostnamebuf[0]=0;
+
+ sprintf(buffer, "=_%-1.30s-%u-%u-%04u",
+ hostnamebuf, (unsigned)getpid(),
+ (unsigned)t, ++counter);
+ return (buffer);
+}
+
+static int good_boundary(const char *boundary,
+ struct mimestack *m, const char *errmsg, FILE *fp)
+{
+ int dummy;
+ int l=strlen(boundary);
+ const char *p;
+ char buf[BUFSIZ];
+
+ if (is_boundary(m, boundary, &dummy))
+ return (0);
+
+ for (p=errmsg; *p; )
+ {
+ if (*p == '-' && p[1] == '-' && strncasecmp(p+2, boundary, l)
+ == 0)
+ return (0);
+
+ while (*p)
+ if (*p++ == '\n')
+ break;
+ }
+
+ if (fp)
+ {
+ if (my_rewind(fp) < 0)
+ return 0;
+
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ if (buf[0] == '-' && buf[1] == '-' &&
+ strncasecmp(buf+2, boundary, l) == 0)
+ return (0);
+ }
+ }
+ return (1);
+}
+
+static const char *get_boundary(struct mimestack *m,
+ const char *errmsg,
+ FILE *fp)
+{
+ const char *p;
+
+ do
+ {
+ p=newboundary();
+ } while (!good_boundary(p, m, errmsg, fp));
+ return (p);
+}
+
+static const char *encoding_str(const char *p)
+{
+ while (*p)
+ {
+ if (*p <= 0 || *p >= 0x7F)
+ return ("8bit");
+ ++p;
+ }
+ return ("7bit");
+}
+
+
+static int copyfp(FILE *t,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int stripcr)
+{
+ char buf[BUFSIZ];
+ int rc=0;
+
+ while ((rc=fread(buf, 1, sizeof(buf), t)) > 0)
+ {
+ if (stripcr)
+ {
+ int i, j;
+
+ for (i=j=0; i<rc; ++i)
+ {
+ if (buf[i] != '\r')
+ buf[j++]=buf[i];
+ }
+ rc=j;
+ }
+ (*output_func)(buf, rc, output_func_arg);
+ }
+
+ return rc;
+}
+
+static void open_result_multipart(void (*)(const char *, size_t, void *),
+ void *,
+ int, const char *, const char *,
+ const char *);
+
+static int dochecksign(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack *stack,
+ FILE *content_fp,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ const char *content_filename,
+ const char *signature_filename,
+ int argc,
+ char **argv, int *errptr)
+{
+ struct gpgmime_forkinfo gpg;
+ int i;
+ const char *new_boundary;
+ const char *output;
+
+ if (libmail_gpgmime_forkchecksign(gpghome, passphrase_fd,
+ content_filename,
+ signature_filename,
+ argc, argv,
+ &gpg))
+ {
+ return -1;
+ }
+
+ *errptr=i=libmail_gpgmime_finish(&gpg);
+
+ output=libmail_gpgmime_getoutput(&gpg);
+
+ new_boundary=get_boundary(stack, output, content_fp);
+
+ open_result_multipart(output_func, output_func_arg,
+ i, new_boundary, output,
+ libmail_gpgmime_getcharset(&gpg));
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(new_boundary, strlen(new_boundary), output_func_arg);
+ (*output_func)("\n", 1, output_func_arg);
+
+ if (my_rewind(content_fp) < 0)
+ {
+ return -1;
+ }
+
+ if (copyfp(content_fp, output_func, output_func_arg, 1))
+ return -1;
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(new_boundary, strlen(new_boundary), output_func_arg);
+ (*output_func)("--\n", 3, output_func_arg);
+ return 0;
+}
+
+static void open_result_multipart(void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int rc,
+ const char *new_boundary,
+ const char *err_str,
+ const char *err_charset)
+{
+#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg)
+#define S(s) (*output_func)( s, strlen(s), output_func_arg)
+
+ char n[10];
+ const char *p;
+
+ sprintf(n, "%d", rc);
+
+ C("Content-Type: multipart/x-mimegpg; xpgpstatus=");
+
+ S(n);
+
+ C("; boundary=\"");
+
+ S(new_boundary);
+
+ C("\"\n"
+ "\nThis is a MIME GnuPG-processed message. If you see this text, it means\n"
+ "that your E-mail or Usenetsoftware does not support MIME-formatted messages.\n\n"
+ "--");
+
+ S(new_boundary);
+ C("\nContent-Type: text/x-gpg-output; charset=");
+ S(err_charset);
+ C("\nContent-Transfer-Encoding: ");
+
+ p=encoding_str(err_str);
+ S(p);
+ C("\n\n");
+ S(err_str);
+#undef C
+#undef S
+}
+
+static void close_mime(struct mimestack **stack, int *iseof,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg)
+{
+ char buf[BUFSIZ];
+ int is_closing;
+ struct mimestack *b;
+
+ for (;;)
+ {
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+
+ (*output_func)(buf, strlen(buf), output_func_arg);
+ if (!(b=is_boundary(*stack, buf, &is_closing)))
+ continue;
+ if (!is_closing)
+ continue;
+
+ libmail_mimestack_pop_to(stack, b);
+ break;
+ }
+}
+
+static int dodecrypt(const char *, const char *,
+ struct mimestack **, int *,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ FILE *, int, char **, const char *,
+ void (*)(const char *,
+ size_t,
+ void *),
+ void *,
+ int *);
+
+static void write_temp_fp(const char *p, size_t n, void *vp)
+{
+ if (fwrite(p, n, 1, (FILE *)vp) != 1)
+ ; /* ignored */
+}
+
+static int decrypt(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack **stack, int *iseof,
+ struct header *h,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int argc, char **argv,
+ int *errptr)
+{
+ struct header *p, *q;
+ char temp_file[TEMPNAMEBUFSIZE];
+ int temp_fd;
+ FILE *temp_fp;
+ struct mime_header *mh;
+ int flag;
+ int errflag;
+
+ *errptr=0;
+ temp_fd=libmail_tempfile(temp_file);
+ if (temp_fd < 0 || (temp_fp=fdopen(temp_fd, "w+")) == 0)
+ {
+ if (temp_fd >= 0)
+ close(temp_fd);
+ return -1;
+ }
+
+ for (p=h; p; p=p->next)
+ {
+ fprintf(temp_fp, "%s", p->header);
+ }
+ putc('\n', temp_fp);
+
+ find_boundary(stack, iseof, input_func, input_func_arg,
+ write_temp_fp, temp_fp, 0);
+ if (*iseof)
+ {
+ fclose(temp_fp);
+ unlink(temp_file);
+ return (0);
+ }
+
+ p=read_headers(stack, iseof, input_func, input_func_arg, write_temp_fp,
+ temp_fp, 0, &errflag);
+
+ if (*iseof || errflag)
+ {
+ libmail_header_free(p);
+ fclose(temp_fp);
+ unlink(temp_file);
+
+ if (errflag)
+ return -1;
+ return 0;
+ }
+
+ q=libmail_header_find(p, "content-type:");
+
+ flag=0;
+
+ if (q)
+ {
+ mh=libmail_mimeheader_parse(q->header+13);
+ if (!mh)
+ {
+ libmail_header_free(p);
+ fclose(temp_fp);
+ unlink(temp_file);
+ return -1;
+ }
+
+ if (strcasecmp(mh->header_name, "application/pgp-encrypted")
+ == 0)
+ flag=1;
+ libmail_mimeheader_free(mh);
+ }
+
+ for (q=p; q; q=q->next)
+ {
+ fprintf(temp_fp, "%s", q->header);
+ }
+ libmail_header_free(p);
+ putc('\n', temp_fp);
+
+ p=read_headers(stack, iseof, input_func, input_func_arg,
+ write_temp_fp, temp_fp, 0,
+ &errflag);
+
+ if (*iseof || errflag)
+ {
+ libmail_header_free(p);
+ fclose(temp_fp);
+ unlink(temp_file);
+
+ if (errflag)
+ return -1;
+
+ return 0;
+ }
+
+ q=libmail_header_find(p, "version:");
+
+ if (flag)
+ {
+ if (!q || atoi(p->header + 8) != 1)
+ flag=0;
+ }
+ for (q=p; q; q=q->next)
+ {
+ fprintf(temp_fp, "%s", q->header);
+ }
+ libmail_header_free(p);
+ putc('\n', temp_fp);
+
+ find_boundary(stack, iseof, input_func, input_func_arg,
+ write_temp_fp, temp_fp, 0);
+
+ if (*iseof)
+ {
+ fclose(temp_fp);
+ unlink(temp_file);
+ return 0;
+ }
+
+ p=read_headers(stack, iseof, input_func, input_func_arg, write_temp_fp,
+ temp_fp, 0, &errflag);
+
+ if (*iseof || errflag)
+ {
+ libmail_header_free(p);
+ fclose(temp_fp);
+ unlink(temp_file);
+
+ if (errflag)
+ return -1;
+
+ return 0;
+ }
+
+ q=libmail_header_find(p, "content-type:");
+
+ if (q && flag)
+ {
+ flag=0;
+ mh=libmail_mimeheader_parse(q->header+13);
+ if (!mh)
+ {
+ libmail_header_free(p);
+ fclose(temp_fp);
+ unlink(temp_file);
+ return -1;
+ }
+
+ if (strcasecmp(mh->header_name, "application/octet-stream")
+ == 0)
+ flag=1;
+ libmail_mimeheader_free(mh);
+
+ q=libmail_header_find(p, "content-transfer-encoding:");
+ if (q && flag)
+ {
+ flag=0;
+ mh=libmail_mimeheader_parse(strchr(q->header, ':')+1);
+ if (!mh)
+ {
+ libmail_header_free(p);
+ fclose(temp_fp);
+ unlink(temp_file);
+ return -1;
+ }
+
+ if (strcasecmp(mh->header_name, "7bit") == 0 ||
+ strcasecmp(mh->header_name, "8bit") == 0)
+ flag=1;
+ libmail_mimeheader_free(mh);
+ }
+ }
+
+ for (q=p; q; q=q->next)
+ {
+ fprintf(temp_fp, "%s", q->header);
+ }
+ libmail_header_free(p);
+ putc('\n', temp_fp);
+
+ if (fflush(temp_fp) || ferror(temp_fp) || my_rewind(temp_fp) < 0)
+ {
+ fclose(temp_fp);
+ unlink(temp_file);
+ return -1;
+ }
+
+ if (!flag)
+ {
+ int c=copyfp(temp_fp, output_func, output_func_arg, 0);
+
+ fclose(temp_fp);
+ unlink(temp_file);
+ close_mime(stack, iseof, input_func, input_func_arg,
+ output_func, output_func_arg);
+ return (c);
+ }
+
+ fclose(temp_fp);
+ if ((temp_fp=fopen(temp_file, "w+")) == NULL)
+ {
+ unlink(temp_file);
+ return (-1);
+ }
+ libmail_gpg_noexec(fileno(temp_fp));
+ errflag=dodecrypt(gpghome, passphrase_fd,
+ stack, iseof, input_func, input_func_arg,
+ temp_fp, argc, argv, temp_file,
+ output_func, output_func_arg, errptr);
+ fclose(temp_fp);
+ unlink(temp_file);
+ return errflag;
+}
+
+static int dumpdecrypt(const char *c, size_t n, void *vp)
+{
+ FILE *fp=(FILE *)vp;
+
+ if (n == 0)
+ return 0;
+
+ if (fwrite(c, n, 1, fp) != 1)
+ return -1;
+ return (0);
+}
+
+static int dodecrypt(const char *gpghome,
+ const char *passphrase_fd,
+ struct mimestack **stack, int *iseof,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ FILE *fpout, int argc, char **argv,
+ const char *temp_file,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ int *errptr)
+{
+ struct gpgmime_forkinfo gpg;
+ char buf[BUFSIZ];
+ int is_closing;
+ struct mimestack *b=NULL;
+ int dowrite=1;
+ int rc;
+ const char *new_boundary;
+ const char *output;
+
+ if (libmail_gpgmime_forkdecrypt(gpghome,
+ passphrase_fd,
+ argc, argv, &dumpdecrypt, fpout, &gpg))
+ return -1;
+
+ for (;;)
+ {
+ if ( (*input_func)(buf, sizeof(buf), input_func_arg))
+ {
+ *iseof=1;
+ break;
+ }
+
+ if (dowrite)
+ libmail_gpgmime_write(&gpg, buf, strlen(buf));
+
+ if (!(b=is_boundary(*stack, buf, &is_closing)))
+ continue;
+ dowrite=0;
+ if (!is_closing)
+ continue;
+ break;
+ }
+
+ rc=libmail_gpgmime_finish(&gpg);
+ if (fflush(fpout) || ferror(fpout) || my_rewind(fpout) < 0)
+ {
+ fclose(fpout);
+ unlink(temp_file);
+ return -1;
+ }
+
+ if (*iseof)
+ return 0;
+
+ output=libmail_gpgmime_getoutput(&gpg),
+
+ new_boundary=get_boundary(*stack, output, rc ? NULL:fpout);
+
+ open_result_multipart(output_func,
+ output_func_arg, rc, new_boundary,
+ output,
+ libmail_gpgmime_getcharset(&gpg));
+
+ *errptr=rc;
+
+#if 0
+
+ /*
+ ** gnupg returns non-zero exit even if succesfully unencrypted, when
+ ** just the signature is bad.
+ */
+ if (rc == 0)
+#endif
+ {
+ if (fseek(fpout, 0L, SEEK_SET) < 0)
+ {
+ fclose(fpout);
+ unlink(temp_file);
+ return -1;
+ }
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(new_boundary, strlen(new_boundary),
+ output_func_arg);
+ (*output_func)("\n", 1, output_func_arg);
+
+ if (copyfp(fpout, output_func, output_func_arg, 1))
+ return -1;
+ }
+
+ (*output_func)("\n--", 3, output_func_arg);
+ (*output_func)(new_boundary, strlen(new_boundary),
+ output_func_arg);
+ (*output_func)("--\n", 3, output_func_arg);
+
+ libmail_mimestack_pop_to(stack, b);
+ return 0;
+}
+
+struct libmail_gpg_errhandler {
+
+ struct libmail_gpg_info *options;
+ int err_flag;
+};
+
+static void libmail_gpg_errfunc(const char *errmsg, void *vp)
+{
+ struct libmail_gpg_errhandler *eh=(struct libmail_gpg_errhandler *)vp;
+
+ if (!eh->err_flag)
+ {
+ eh->err_flag=1;
+
+ (*eh->options->errhandler_func)(errmsg,
+ eh->options->errhandler_arg);
+ }
+}
+
+static int input_func_from_fp(char *buf, size_t cnt, void *vp)
+{
+ if (fgets(buf, cnt, (FILE *)vp) == NULL)
+ return (-1);
+ return (0);
+}
+
+/*
+** When signing, but not encoding, signed text must be 7bit, as per RFC.
+**
+** Use rfc2045's rewriter to do this.
+*/
+
+static int dosignencode(int dosign, int doencode, int dodecode,
+ const char *gpghome,
+ const char *passphrase_fd,
+ int (*input_func)(char *, size_t, void *vp),
+ void *input_func_arg,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_func_arg,
+ void (*errhandler_func)(const char *, void *),
+ void *errhandler_arg,
+ int argc, char **argv,
+ int *status)
+{
+ char temp_decode_name[TEMPNAMEBUFSIZE];
+ int fdin;
+ int fdout;
+ FILE *fdin_fp;
+ char buffer[8192];
+ struct rfc2045src *src;
+ struct rfc2045 *rfcp;
+ int rc;
+
+ if (!dosign || doencode)
+ return dosignencode2(dosign, doencode, dodecode,
+ gpghome,
+ passphrase_fd,
+ input_func,
+ input_func_arg,
+ output_func,
+ output_func_arg,
+ errhandler_func,
+ errhandler_arg,
+ argc, argv, status);
+
+ /* Save the message into a temp file, first */
+
+ fdin=libmail_tempfile(temp_decode_name);
+
+ if (fdin < 0 ||
+ (fdin_fp=fdopen(fdin, "w+")) == NULL)
+ {
+ if (fdin >= 0)
+ close(fdin);
+
+ (*errhandler_func)("Cannot create temporary file",
+ errhandler_arg);
+ return (-1);
+ }
+
+ unlink(temp_decode_name);
+
+ if (!(rfcp=rfc2045_alloc_ac()))
+ {
+ (*errhandler_func)(strerror(errno), errhandler_arg);
+ fclose(fdin_fp);
+ return (-1);
+ }
+
+ while ( (*input_func)(buffer, sizeof(buffer), input_func_arg) == 0)
+ {
+ size_t l=strlen(buffer);
+
+ if (fwrite(buffer, l, 1, fdin_fp) != 1)
+ {
+ (*errhandler_func)(strerror(errno), errhandler_arg);
+ fclose(fdin_fp);
+ rfc2045_free(rfcp);
+ return (-1);
+ }
+
+ /* Parse the message at the same time it's being saved */
+
+ rfc2045_parse(rfcp, buffer, l);
+ }
+
+ if (fseek(fdin_fp, 0L, SEEK_SET) < 0)
+ {
+ (*errhandler_func)(strerror(errno), errhandler_arg);
+ fclose(fdin_fp);
+ rfc2045_free(rfcp);
+ return (-1);
+ }
+
+ if (!rfc2045_ac_check(rfcp, RFC2045_RW_7BIT))
+ {
+ rfc2045_free(rfcp);
+
+ /* No need to rewrite, just do this */
+
+ rc=dosignencode2(dosign, doencode, dodecode,
+ gpghome,
+ passphrase_fd,
+ input_func_from_fp,
+ fdin_fp,
+ output_func,
+ output_func_arg,
+ errhandler_func,
+ errhandler_arg,
+ argc, argv, status);
+
+ fclose(fdin_fp);
+
+ return rc;
+ }
+
+ /* Rewrite the message into another temp file */
+
+ fdout=libmail_tempfile(temp_decode_name);
+
+ src=rfc2045src_init_fd(fileno(fdin_fp));
+
+ if (fdout < 0 || src == NULL ||
+ rfc2045_rewrite(rfcp, src, fdout, "mimegpg") < 0 ||
+ lseek(fdout, 0L, SEEK_SET) < 0)
+ {
+ if (fdout >= 0)
+ close(fdout);
+ if (src)
+ rfc2045src_deinit(src);
+
+ (*errhandler_func)(strerror(errno), errhandler_arg);
+ rfc2045_free(rfcp);
+ fclose(fdin_fp);
+ return (-1);
+ }
+ fclose(fdin_fp);
+ rfc2045_free(rfcp);
+ rfc2045src_deinit(src);
+
+ /* Now, read the converted message, from the temp file */
+
+ if ((fdin_fp=fdopen(fdout, "w+")) == NULL)
+ {
+ close(fdout);
+
+ (*errhandler_func)("Cannot create temporary file",
+ errhandler_arg);
+ return (-1);
+ }
+
+ rc=dosignencode2(dosign, doencode, dodecode,
+ gpghome,
+ passphrase_fd,
+ input_func_from_fp,
+ fdin_fp,
+ output_func,
+ output_func_arg,
+ errhandler_func,
+ errhandler_arg,
+ argc, argv, status);
+ fclose(fdin_fp);
+ return rc;
+}
+
+int libmail_gpg_signencode(int dosign,
+ int doencode,
+ /*
+ ** One of LIBMAIL_GPG_INDIVIDUAL or
+ ** LIBMAIL_GPG_ENCAPSULATE
+ */
+ struct libmail_gpg_info *options)
+{
+ int rc;
+ struct libmail_gpg_errhandler eh;
+
+ eh.options=options;
+ eh.err_flag=0;
+
+ if (doencode != LIBMAIL_GPG_INDIVIDUAL &&
+ doencode != LIBMAIL_GPG_ENCAPSULATE)
+ doencode=0;
+
+ if (!dosign && !doencode)
+ {
+ (*options->errhandler_func)("Invalid arguments to"
+ " libmail_gpg_signencode",
+ options->errhandler_arg);
+ return -1;
+ }
+
+ rc=dosignencode(dosign, doencode, 0,
+ options->gnupghome,
+ options->passphrase_fd,
+ options->input_func,
+ options->input_func_arg,
+ options->output_func,
+ options->output_func_arg,
+ &libmail_gpg_errfunc,
+ &eh,
+ options->argc,
+ options->argv,
+ &options->errstatus);
+
+ if (rc && !eh.err_flag)
+ (*options->errhandler_func)(strerror(errno),
+ options->errhandler_arg);
+ return rc;
+}
+
+int libmail_gpg_decode(int mode,
+ /*
+ ** LIBMAIL_GPG_UNENCRYPT OR LIBMAIL_GPG_CHECKSIGN
+ */
+ struct libmail_gpg_info *options)
+{
+ int rc;
+ struct libmail_gpg_errhandler eh;
+
+ eh.options=options;
+ eh.err_flag=0;
+
+ if ((mode & (LIBMAIL_GPG_UNENCRYPT|LIBMAIL_GPG_CHECKSIGN)) == 0)
+ {
+ (*options->errhandler_func)("Invalid arguments to"
+ " libmail_gpg_decode",
+ options->errhandler_arg);
+ return -1;
+ }
+
+ rc=dosignencode(0, 0, mode,
+ options->gnupghome,
+ options->passphrase_fd,
+ options->input_func,
+ options->input_func_arg,
+ options->output_func,
+ options->output_func_arg,
+ &libmail_gpg_errfunc,
+ &eh,
+ options->argc,
+ options->argv,
+ &options->errstatus);
+
+ if (rc && !eh.err_flag)
+ (*options->errhandler_func)(strerror(errno),
+ options->errhandler_arg);
+ return rc;
+}
diff --git a/gpglib/gpglib.h b/gpglib/gpglib.h
new file mode 100644
index 0000000..784f9b8
--- /dev/null
+++ b/gpglib/gpglib.h
@@ -0,0 +1,239 @@
+#ifndef gpglib_h
+#define gpglib_h
+/*
+** Copyright 2001-2008 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "config.h"
+#include <sys/types.h>
+#include <stdlib.h>
+
+
+#define LIBMAIL_GPG_INDIVIDUAL 1
+#define LIBMAIL_GPG_ENCAPSULATE 2
+
+#define LIBMAIL_GPG_CHECKSIGN 1
+#define LIBMAIL_GPG_UNENCRYPT 2
+
+struct libmail_gpg_info {
+
+ const char *gnupghome; /* May be NULL, sets GNUPGHOME */
+
+ const char *passphrase_fd; /* NULL, or string giving */
+
+ /*
+ ** input_func gets called repeatedly to obtain the message to
+ ** encrypt/sign/decrypt/check. input_func() receives the same
+ ** arguments as fgets(), with its third argument being input_func_arg.
+ ** input_func should read up to cnt-1 bytes, or a newline, whichever
+ ** comes first, and save read data in buf, appending a single null
+ ** byte. input_func should return 0, or -1 on EOF condition.
+ */
+ int (*input_func)(char *buf, size_t cnt, void *vp);
+ void *input_func_arg;
+
+ /*
+ ** Output_func gets repeatedly invoked with the contents of the
+ ** encrypted/signed/decrypted/verified message.
+ */
+
+ void (*output_func)(const char *output, size_t nbytes,
+ void *output_arg);
+ void *output_func_arg; /* Passthru arg to output_func */
+
+ /*
+ ** In the event of an error, the error handler will be invoked with
+ ** the error message text. The error handler will be invoked
+ ** just before libmail_gpg_*() exits. Note that the memory used
+ ** by the error message text will be destroyed by the time
+ ** libmail_gpg_*() exits, so the application needs to make a copy of
+ ** it, if it intends to use it later.
+ */
+
+ void (*errhandler_func)(const char *errmsg, void *errmsg_arg);
+ void *errhandler_arg; /* Passthru arg to errhandler_func */
+
+ /* Additional, arbitrary, arguments to GnuPG */
+
+ int argc;
+ char **argv;
+
+ /* On exit, the following bits may be set: */
+
+ int errstatus;
+
+#define LIBMAIL_ERR_VERIFYSIG 1
+#define LIBMAIL_ERR_DECRYPT 2
+
+};
+
+int libmail_gpg_signencode(int dosign,
+ int doencode,
+ /*
+ ** One of LIBMAIL_GPG_INDIVIDUAL or
+ ** LIBMAIL_GPG_ENCAPSULATE
+ */
+ struct libmail_gpg_info *options);
+
+int libmail_gpg_decode(int mode,
+ /*
+ ** LIBMAIL_GPG_UNENCRYPT OR LIBMAIL_GPG_CHECKSIGN
+ */
+ struct libmail_gpg_info *options);
+
+
+ /* A convenient input_func, where vp is FILE * */
+
+int libmail_gpg_inputfunc_readfp(char *buf, size_t cnt, void *vp);
+
+ /* Other functions: */
+
+int libmail_gpg_cleanup();
+int libmail_gpg_has_gpg(const char *gpgdir);
+
+int libmail_gpg_genkey(const char *gpgdir,
+ const char *charset,
+ const char *name,
+ const char *addr,
+ const char *comment,
+ int skeylen,
+ int ekeylen,
+ unsigned expire,
+ char expire_unit,
+ const char *passphrase,
+
+ int (*dump_func)(const char *, size_t, void *),
+ int (*timeout_func)(void *),
+ void *voidarg);
+
+struct gpg_list_info {
+ const char *charset;
+ const char *disabled_msg;
+ const char *revoked_msg;
+ const char *expired_msg;
+ const char *group_msg;
+ void *voidarg;
+} ;
+
+int libmail_gpg_listkeys(const char *gpgdir,
+ int secret,
+ int (*callback_func)(const char *, const char *,
+ const char *, int,
+ struct gpg_list_info *),
+ int (*err_func)(const char *, size_t, void *),
+ struct gpg_list_info *);
+
+int libmail_gpg_listgroups(const char *gpgdir,
+ int (*callback_func)(const char *, const char *,
+ const char *,
+ int,
+ struct gpg_list_info *),
+ struct gpg_list_info *voidarg);
+
+int libmail_gpg_exportkey(const char *gpgdir,
+ int secret,
+ const char *fingerprint,
+ int (*out_func)(const char *, size_t, void *),
+ int (*err_func)(const char *, size_t, void *),
+ void *voidarg);
+
+int libmail_gpg_deletekey(const char *gpgdir, int secret, const char *fingerprint,
+ int (*dump_func)(const char *, size_t, void *),
+ void *voidarg);
+
+int libmail_gpg_signkey(const char *gpgdir, const char *signthis, const char *signwith,
+ int passphrase_fd,
+ int (*dump_func)(const char *, size_t, void *),
+ int trustlevel,
+ void *voidarg);
+
+int libmail_gpg_checksign(const char *gpgdir,
+ const char *content, /* Filename, for now */
+ const char *signature, /* Filename, for now */
+ int (*dump_func)(const char *, size_t, void *),
+ void *voidarg);
+
+ /* IMPORT A KEY */
+
+int libmail_gpg_import_start(const char *gpgdir, int issecret);
+
+int libmail_gpg_import_do(const char *p, size_t n, /* Part of the key */
+ int (*dump_func)(const char *, size_t, void *),
+ /* gpg output callback */
+
+ void *voidarg);
+
+int libmail_gpg_import_finish(int (*dump_func)(const char *, size_t, void *),
+ void *voidarg);
+
+
+
+ /* INTERNAL: */
+
+pid_t libmail_gpg_fork(int *, int *, int *, const char *, char **);
+
+#define GPGARGV_PASSPHRASE_FD(argv,i,fd,buf) \
+ ((argv)[(i)++]="--passphrase-fd", \
+ (argv)[(i)++]=libmail_str_size_t((fd),(buf)))
+
+int libmail_gpg_write(const char *, size_t,
+ int (*)(const char *, size_t, void *),
+ int (*)(const char *, size_t, void *),
+ int (*)(void *),
+ unsigned,
+ void *);
+
+int libmail_gpg_read(int (*)(const char *, size_t, void *),
+ int (*)(const char *, size_t, void *),
+ int (*)(void *),
+ unsigned,
+ void *);
+
+char *libmail_gpg_options(const char *gpgdir);
+ /* Filename of the options file. If gpgdir is NULL try
+ ** the environment variables. */
+
+
+struct rfc2045 *libmail_gpgmime_is_multipart_signed(const struct rfc2045 *);
+ /*
+ ** Return ptr to signed content if ptr is a multipart/signed.
+ */
+
+struct rfc2045 *libmail_gpgmime_is_multipart_encrypted(const struct rfc2045 *);
+ /*
+ ** Return ptr to encrypted content if ptr is a multipart/encrypted.
+ */
+
+int libmail_gpgmime_has_mimegpg(const struct rfc2045 *);
+ /*
+ ** Return non-zero if MIME content has any signed or encrypted
+ ** content.
+ */
+
+int libmail_gpgmime_is_decoded(const struct rfc2045 *, int *);
+ /*
+ ** Return non-zero if this is a multipart/mixed section generated
+ ** by mimegpg, and return the GnuPG return code.
+ */
+
+struct rfc2045 *libmail_gpgmime_decoded_content(const struct rfc2045 *);
+ /*
+ ** If is_decoded, then return the ptr to the decoded content.
+ ** (note - if decryption failed, NULL is returned).
+ */
+
+struct rfc2045 *libmail_gpgmime_signed_content(const struct rfc2045 *);
+ /*
+ ** If is_multipart_signed, return ptr to the signed content.
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/gpglib/import.c b/gpglib/import.c
new file mode 100644
index 0000000..b3cdd38
--- /dev/null
+++ b/gpglib/import.c
@@ -0,0 +1,70 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+
+/*
+** Import a key.
+*/
+
+int libmail_gpg_import_start(const char *gpgdir, int secret)
+{
+ char *argvec[5];
+
+ argvec[0]="gpg";
+ argvec[1]="--import";
+ argvec[2]="--no-tty";
+ argvec[3]=0;
+
+#if GPG_HAS_ALLOW_SECRET_KEY_IMPORT
+ if (secret)
+ {
+ argvec[3]="--allow-secret-key-import";
+ argvec[4]=0;
+ }
+#endif
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL, gpgdir, argvec) < 0)
+ return (-1);
+ return (0);
+}
+
+int libmail_gpg_import_do(const char *p, size_t n,
+ int (*dump_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ if (libmail_gpg_write(p, n, dump_func, dump_func, NULL, 0, voidarg))
+ {
+ libmail_gpg_cleanup();
+ return (-1);
+ }
+ return (0);
+}
+
+int libmail_gpg_import_finish(int (*dump_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ if (libmail_gpg_read(dump_func, dump_func, NULL, 0, voidarg))
+ return (-1);
+ return (libmail_gpg_cleanup());
+}
diff --git a/gpglib/libgpg.c b/gpglib/libgpg.c
new file mode 100644
index 0000000..7bea385
--- /dev/null
+++ b/gpglib/libgpg.c
@@ -0,0 +1,104 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include "gpg.h"
+#include "gpglib.h"
+
+int libmail_gpg_stdin= -1, libmail_gpg_stdout= -1,
+ libmail_gpg_stderr= -1;
+pid_t libmail_gpg_pid= -1;
+
+int libmail_gpg_cleanup()
+{
+ int rc=0;
+
+ if (libmail_gpg_stdin >= 0)
+ {
+ close(libmail_gpg_stdin);
+ libmail_gpg_stdin= -1;
+ }
+
+ if (libmail_gpg_stdout >= 0)
+ {
+ close(libmail_gpg_stdout);
+ libmail_gpg_stdout= -1;
+ }
+
+ if (libmail_gpg_stderr >= 0)
+ {
+ close(libmail_gpg_stderr);
+ libmail_gpg_stderr= -1;
+ }
+
+ if (libmail_gpg_pid >= 0 && kill(libmail_gpg_pid, 0) >= 0)
+ {
+ int waitstat;
+ pid_t p;
+
+ while ((p=wait(&waitstat)) != libmail_gpg_pid)
+ {
+ if (p < 0 && errno == ECHILD)
+ return (-1);
+ }
+
+ rc= WIFEXITED(waitstat) ? WEXITSTATUS(waitstat): -1;
+ libmail_gpg_pid= -1;
+ }
+ return (rc);
+}
+
+static char *optionfile(const char *gpgdir)
+{
+ char *p=malloc(strlen(gpgdir)+sizeof("/options"));
+
+ if (p)
+ strcat(strcpy(p, gpgdir), "/options");
+ return (p);
+}
+
+/*
+** Determine of GPG is available by checking for the options file, and the
+** gpg binary itself.
+*/
+
+int libmail_gpg_has_gpg(const char *gpgdir)
+{
+ char *p=optionfile(gpgdir);
+ struct stat stat_buf;
+ int rc;
+
+ if (!p)
+ return (-1);
+
+ rc=stat(p, &stat_buf);
+ free(p);
+ if (rc == 0)
+ rc=stat(GPG, &stat_buf);
+
+ return (rc);
+}
diff --git a/gpglib/list.c b/gpglib/list.c
new file mode 100644
index 0000000..7b58355
--- /dev/null
+++ b/gpglib/list.c
@@ -0,0 +1,574 @@
+/*
+** Copyright 2001-2011 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+/*
+** List keys
+*/
+
+static int dolist(int (*)(const char *, const char *, const char *, int,
+ struct gpg_list_info *),
+ int (*)(const char *, size_t, void *),
+ void *);
+
+static void definit(struct gpg_list_info *arg)
+{
+
+#define DEFINIT(c, a) if (!(c)) (c)=(a)
+
+ DEFINIT(arg->charset, "iso-8859-1");
+ DEFINIT(arg->disabled_msg, "[ This key is disabled ]");
+ DEFINIT(arg->revoked_msg, "[ This key is revoked ]");
+ DEFINIT(arg->expired_msg, "[ This key is expired ]");
+ DEFINIT(arg->group_msg, "Group: @");
+}
+
+int libmail_gpg_listkeys(const char *gpgdir,
+ int secret,
+ int (*callback_func)(const char *, const char *,
+ const char *,
+ int,
+ struct gpg_list_info *),
+ int (*err_func)(const char *, size_t, void *),
+ struct gpg_list_info *voidarg)
+{
+ char *argvec[7];
+ int rc;
+
+ argvec[0]="gpg";
+ argvec[1]= secret ? "--list-secret-keys":"--list-sigs";
+ argvec[2]="--with-colons";
+ argvec[3]="--fingerprint";
+ argvec[4]="-q";
+ argvec[5]="--no-tty";
+ argvec[6]=0;
+
+ definit(voidarg);
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout,
+ &libmail_gpg_stderr, gpgdir, argvec) < 0)
+ rc= -1;
+ else
+ {
+ int rc2;
+
+ rc=dolist(callback_func, err_func, voidarg);
+
+ rc2=libmail_gpg_cleanup();
+ if (rc2)
+ rc=rc2;
+ }
+ return (rc);
+}
+
+/*
+** Parse output of gpg into physical lines.
+*/
+
+struct gpg_callbackinfo {
+ int (*err_func)(const char *, size_t, void *);
+ /* stderr passthrough */
+
+ struct gpg_list_info *voidarg;
+ /* passthrough arg */
+
+ /* Line buffer: */
+ char *linebuffer;
+ size_t linebufsize;
+ size_t linebufcnt;
+
+ /* Function that receives collected lines, + passthrough arg */
+ int (*line_func)(char *, void *, struct gpg_list_info *);
+ void *line_callback_arg;
+} ;
+
+static int libmail_gpg_line_stderr(const char *l, size_t c, void *vp)
+{
+ return (0);
+}
+
+static int libmail_gpg_line_stdout(const char *l, size_t c, void *vp)
+{
+ struct gpg_callbackinfo *gci=(struct gpg_callbackinfo *)vp;
+ size_t i, j;
+
+ if (c + gci->linebufcnt >= gci->linebufsize)
+ {
+ /* Need bigger line buffer */
+
+ size_t news= c+gci->linebufcnt+256;
+
+ char *newp= gci->linebuffer ? realloc(gci->linebuffer, news)
+ : malloc(news);
+
+ if (!newp)
+ return (-1);
+ gci->linebuffer=newp;
+ gci->linebufsize=news;
+ }
+
+ memcpy(gci->linebuffer + gci->linebufcnt, l, c);
+ gci->linebufcnt += c;
+
+ /* Search for collected newlines, extract complete lines,
+ ** invoke the callback function.
+ */
+
+ for (;;)
+ {
+ int rc;
+
+ for (i=0; i<gci->linebufcnt; i++)
+ if (gci->linebuffer[i] == '\n')
+ break;
+ if (i >= gci->linebufcnt)
+ break;
+ gci->linebuffer[i++]=0;
+
+ rc= (*gci->line_func)(gci->linebuffer, gci->line_callback_arg,
+ gci->voidarg);
+ j=0;
+ while (i < gci->linebufcnt)
+ {
+ gci->linebuffer[j]=gci->linebuffer[i];
+ ++i;
+ ++j;
+ }
+ gci->linebufcnt=j;
+ if (rc)
+ return (rc);
+ }
+ return (0);
+}
+
+/*
+** Parse list output of gpg into distrinct keys.
+*/
+
+struct gpg_callbacklistinfo {
+
+ int (*callback_func)(const char *, const char *, const char *, int,
+ struct gpg_list_info *);
+ int invalid_flag;
+ char fingerprint[128];
+ char shortname[256];
+ char *keybuffer;
+ size_t keybufsize;
+ size_t keybuflen;
+
+ int seen_startofkey;
+} ;
+
+static int dolist_callback(char *, void *, struct gpg_list_info *);
+
+static int dolist(int (*callback_func)(const char *, const char *,
+ const char *, int,
+ struct gpg_list_info *),
+ int (*err_func)(const char *, size_t, void *),
+ void *voidarg)
+{
+ struct gpg_callbackinfo gci;
+ struct gpg_callbacklistinfo gcli;
+ int rc, rc2;
+
+ close(libmail_gpg_stdin);
+ libmail_gpg_stdin= -1;
+
+ memset(&gci, 0, sizeof(gci));
+ gci.err_func=err_func;
+ gci.voidarg=voidarg;
+
+ gci.line_func= &dolist_callback;
+ gci.line_callback_arg= &gcli;
+
+ memset(&gcli, 0, sizeof(gcli));
+ gcli.callback_func=callback_func;
+
+ rc=libmail_gpg_read( libmail_gpg_line_stdout,
+ libmail_gpg_line_stderr,
+ NULL,
+ 0,
+ &gci);
+
+
+ rc2= (*gci.line_func)(NULL, gci.line_callback_arg,
+ gci.voidarg);
+ if (rc2)
+ rc=rc2;
+
+ if (gcli.keybuffer)
+ free(gcli.keybuffer);
+
+ if (gci.linebuffer)
+ free(gci.linebuffer);
+ return (rc);
+}
+
+static char *nextword(char *);
+
+static int nyb(int c)
+{
+ static const char xdigits[]="0123456789ABCDEFabcdef";
+
+ char *p=strchr(xdigits, c);
+
+ if (!p)
+ return (0);
+
+ c= p-xdigits;
+
+ if (c >= 16)
+ c -= 6;
+ return (c);
+}
+
+static int append_key(struct gpg_callbacklistinfo *, const char *);
+static int append_date(struct gpg_callbacklistinfo *, const char *);
+
+static int dolist_callback(char *line, void *vp1,
+ struct gpg_list_info *vp)
+{
+ struct gpg_callbacklistinfo *gci=(struct gpg_callbacklistinfo *)vp1;
+
+ char *rectype;
+ char *trust;
+ char *length;
+ int algo;
+ /*char *keyid;*/
+ char *crdate;
+ char *expdate;
+ /*char *localid;*/
+ /*char *ownertrust;*/
+ char *userid;
+ char *p;
+ const char *stat;
+
+ if (!line || strncmp(line, "pub", 3) == 0
+ || strncmp(line, "sec", 3) == 0)
+ {
+ if (gci->seen_startofkey)
+ {
+ int rc=(*gci->callback_func)(gci->fingerprint,
+ gci->shortname,
+ gci->keybuflen ?
+ gci->keybuffer:"",
+ gci->invalid_flag,
+ vp);
+ gci->keybuflen=0;
+ gci->fingerprint[0]=0;
+ gci->shortname[0]=0;
+ if (rc)
+ return (rc);
+ }
+
+ if (!line)
+ return (0);
+
+ gci->seen_startofkey=1;
+ }
+
+ if (!gci->seen_startofkey)
+ return (0);
+
+ p=line;
+ rectype=p; p=nextword(p);
+ trust=p; p=nextword(p);
+ length=p; p=nextword(p);
+ algo=atoi(p); p=nextword(p);
+ /*keyid=p;*/ p=nextword(p);
+ crdate=p; p=nextword(p);
+ expdate=p; p=nextword(p);
+ /*localid=p;*/ p=nextword(p);
+ /*ownertrust=p;*/ p=nextword(p);
+ userid=p; p=nextword(p);
+
+ {
+ char *q;
+
+ for (p=q=userid; *p; )
+ {
+ int n;
+
+ if (*p != '\\')
+ {
+ *q=*p;
+ ++p;
+ ++q;
+ continue;
+ }
+ ++p;
+ if (*p != 'x')
+ {
+ *q=*p;
+ if (*p)
+ ++p;
+ ++q;
+ continue;
+ }
+ ++p;
+ n=nyb(*p);
+ if (*p)
+ ++p;
+ n=n * 16 + nyb(*p);
+ if (*p)
+ ++p;
+ *q=n;
+ ++q;
+ }
+ *q=0;
+ }
+
+ stat=0;
+
+ if (strcmp(rectype, "fpr") == 0)
+ {
+ gci->fingerprint[0]=0;
+ strncat(gci->fingerprint, userid,
+ sizeof(gci->fingerprint)-2);
+ return (0);
+ }
+
+ if (strcmp(rectype, "pub") == 0 ||
+ strcmp(rectype, "sec") == 0)
+ {
+ stat= *trust == 'd' ? vp->disabled_msg:
+ *trust == 'r' ? vp->revoked_msg:
+ *trust == 'e' ? vp->expired_msg:0;
+
+ }
+ else if (strcmp(rectype, "sub") &&
+ strcmp(rectype, "ssb") &&
+ strcmp(rectype, "uid") &&
+ strcmp(rectype, "sig"))
+ return (0);
+
+ if (append_key(gci, rectype) ||
+ append_key(gci, " "))
+ return (-1);
+
+
+ gci->invalid_flag= stat ? 1:0;
+
+ {
+ char buf[60];
+
+ sprintf(buf, "%4.4s/%-8.8s", length,
+ algo == 1 ? "RSA":
+ algo == 16 ? "ElGamal":
+ algo == 17 ? "DSA":
+ algo == 20 ? "DSA":"???");
+
+ if (algo == 0 || *length == 0)
+ sprintf(buf, "%13s", "");
+
+ if (append_key(gci, buf))
+ return (-1);
+ }
+
+ userid=libmail_u_convert_fromutf8(userid, vp->charset, NULL);
+ if (!userid)
+ return (-1);
+
+ if (strcmp(rectype, "pub") == 0 ||
+ strcmp(rectype, "sec") == 0 ||
+ (strcmp(rectype, "uid") == 0 && gci->shortname[0] == 0))
+ {
+ gci->shortname[0]=0;
+ strncat(gci->shortname, userid,
+ sizeof(gci->shortname)-2);
+
+ }
+
+ if (append_key(gci, " ")
+ || append_date(gci, crdate)
+ || append_key(gci, " ")
+ || append_date(gci, expdate)
+ || append_key(gci, " ")
+ || append_key(gci, userid)
+ || append_key(gci, "\n"))
+ {
+ free(userid);
+ return (-1);
+ }
+ free(userid);
+
+ if (stat)
+ {
+ append_key(gci, " ");
+ append_key(gci, stat);
+ append_key(gci, "\n");
+ }
+
+ return (0);
+}
+
+static char *nextword(char *p)
+{
+ while (*p && *p != ':')
+ ++p;
+
+ if (*p)
+ *p++=0;
+ return (p);
+}
+
+static int append_date(struct gpg_callbacklistinfo *gci, const char *dtval)
+{
+ char buf[20];
+
+ const char *t;
+ struct tm tmbuf;
+ time_t secs;
+
+ if (strlen(dtval) == 10 && strchr(dtval, '-'))
+ return append_key(gci, dtval); /* YYYY-MM-DD */
+
+ t=strchr(dtval, 'T');
+
+ if (t && (t-dtval) == 8) /* YYYYMMDDThhmmss */
+ {
+ sprintf(buf, "%.4s-%.2s-%.2s", dtval, dtval+4, dtval+6);
+ return append_key(gci, buf);
+ }
+
+ secs=0;
+ while (dtval)
+ {
+ if (*dtval < '0' || *dtval > '9')
+ break;
+ secs = secs * 10 + (*dtval++ - '0');
+ }
+
+ if (secs == 0 || *dtval || localtime_r(&secs, &tmbuf) == NULL)
+ return append_key(gci, " ");
+
+ buf[strftime(buf, sizeof(buf)-1, "%Y-%m-%d", &tmbuf)]=0;
+
+ return append_key(gci, buf);
+}
+
+static int append_key(struct gpg_callbacklistinfo *gci, const char *l)
+{
+ int ll=strlen(l);
+
+ if (ll + gci->keybuflen >= gci->keybufsize)
+ {
+ int n=ll + gci->keybuflen + 256;
+
+ char *p= gci->keybuffer ? realloc(gci->keybuffer, n)
+ : malloc(n);
+ if (!p)
+ return (-1);
+ gci->keybuffer=p;
+ gci->keybufsize=n;
+ }
+ strcpy(gci->keybuffer + gci->keybuflen, l);
+ gci->keybuflen += ll;
+ return (0);
+}
+
+int libmail_gpg_listgroups(const char *gpgdir,
+ int (*callback_func)(const char *, const char *,
+ const char *,
+ int,
+ struct gpg_list_info *),
+ struct gpg_list_info *voidarg)
+{
+ char *filename=libmail_gpg_options(gpgdir);
+ FILE *fp;
+ char buf[BUFSIZ];
+ char *p;
+ char *q;
+ char *r;
+ int rc;
+
+ definit(voidarg);
+
+ if (!filename)
+ return 0;
+
+ fp=fopen(filename, "r");
+ free(filename);
+
+ if (!fp)
+ return 0;
+
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ if (strncmp(buf, "group", 5) ||
+ !isspace((int)(unsigned char)buf[5]))
+ continue;
+
+ for (p=buf+5; *p && isspace((int)(unsigned char)*p); ++p)
+ ;
+ q=strchr(p, '=');
+ if (!q)
+ continue;
+ *q=0;
+
+
+ /* strip trailing spaces */
+
+ for (q=r=p; *q; q++)
+ if (!isspace((int)(unsigned char)*q))
+ r=q+1;
+ *r=0;
+
+ if (*p == 0)
+ continue;
+
+ q=libmail_u_convert_fromutf8(p, voidarg->charset, NULL);
+
+ if (!q)
+ continue;
+
+ r=malloc(strlen(q)+strlen(voidarg->group_msg)+1);
+
+ if (!r)
+ {
+ free(q);
+ continue;
+ }
+
+ strcpy(r, voidarg->group_msg);
+ if ((p=strchr(r, '@')) != 0)
+ strcat(strcpy(p, q),
+ strchr(voidarg->group_msg, '@')+1);
+
+
+ rc=(*callback_func)(p, r, r, 0, voidarg);
+ free(q);
+ free(r);
+ if (rc)
+ {
+ fclose(fp);
+ return rc;
+ }
+ }
+ fclose(fp);
+ return (0);
+}
diff --git a/gpglib/mimegpg.c b/gpglib/mimegpg.c
new file mode 100644
index 0000000..e7a8ead
--- /dev/null
+++ b/gpglib/mimegpg.c
@@ -0,0 +1,189 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#include "config.h"
+#include "gpglib.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+extern void libmail_gpg_noexec(int fd);
+
+void rfc2045_error(const char *p)
+{
+ fprintf(stderr, "%s\n", p);
+ exit(1);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: mimegpg [-s] [-e] [-c] [-d] -- [gpg options]\n");
+}
+
+static void my_output(const char *p, size_t n,
+ void *dummy)
+{
+ FILE *fp=(FILE *)dummy;
+
+ if (fwrite(p, n, 1, fp) != 1)
+ {
+ perror("write");
+ exit(1);
+ }
+}
+
+static int my_inputfunc(char *buf, size_t buf_size, void *vp)
+{
+ FILE *fp=(FILE *)vp;
+ size_t n;
+
+ if (buf_size <= 0)
+ return -1;
+
+ --buf_size;
+
+ for (n=0; n<buf_size; n++)
+ {
+ int c=fgetc(fp);
+
+ if (c == EOF)
+ {
+ buf[n]=0;
+
+ return n ? 0:-1;
+ }
+
+ buf[n]=c;
+
+ if (c == '\n')
+ {
+ n++;
+ break;
+ }
+ }
+ buf[n]=0;
+ return 0;
+}
+
+
+static void my_errhandler(const char *msg, void *dummy)
+{
+ fprintf(stderr, "ERROR: %s\n", msg);
+}
+
+int main(int argc, char **argv)
+{
+ int argn;
+ int dosign=0;
+ int doencode=0;
+ int dodecode=0;
+ int rc;
+ const char *passphrase_fd=0;
+ struct libmail_gpg_info gpg_info;
+
+ setlocale(LC_ALL, "C");
+
+ for (argn=1; argn < argc; argn++)
+ {
+ if (strcmp(argv[argn], "--") == 0)
+ {
+ ++argn;
+ break;
+ }
+
+ if (strcmp(argv[argn], "-s") == 0)
+ {
+ dosign=1;
+ continue;
+ }
+
+ if (strcmp(argv[argn], "-e") == 0)
+ {
+ doencode=LIBMAIL_GPG_INDIVIDUAL;
+ continue;
+ }
+
+ if (strcmp(argv[argn], "-E") == 0)
+ {
+ doencode=LIBMAIL_GPG_ENCAPSULATE;
+ continue;
+ }
+
+ if (strcmp(argv[argn], "-d") == 0)
+ {
+ dodecode |= LIBMAIL_GPG_UNENCRYPT;
+ continue;
+ }
+
+ if (strcmp(argv[argn], "-c") == 0)
+ {
+ dodecode |= LIBMAIL_GPG_CHECKSIGN;
+ continue;
+ }
+
+ if (strcmp(argv[argn], "-p") == 0)
+ {
+ ++argn;
+ if (argn < argc)
+ {
+ passphrase_fd=argv[argn];
+ continue;
+ }
+ --argn;
+ }
+
+ fprintf(stderr, "Unknown option: %s\n", argv[argn]);
+ exit(1);
+ }
+
+ if (!dosign && !doencode && !dodecode)
+ {
+ usage();
+ return (1);
+ }
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* Make things sane */
+
+ if (dosign || doencode)
+ dodecode=0;
+
+#if 0
+ if (dosign && !doencode)
+ reformime(argv[0]);
+#endif
+
+ memset(&gpg_info, 0, sizeof(gpg_info));
+
+ gpg_info.input_func=my_inputfunc;
+ gpg_info.input_func_arg=stdin;
+ gpg_info.output_func=my_output;
+ gpg_info.output_func_arg=stdout;
+ gpg_info.errhandler_func=my_errhandler;
+ gpg_info.errhandler_arg=NULL;
+ gpg_info.passphrase_fd=passphrase_fd;
+ gpg_info.argc=argc - argn;
+ gpg_info.argv=argv + argn;
+
+ libmail_gpg_noexec(fileno(stdin));
+ libmail_gpg_noexec(fileno(stdout));
+
+ rc=dodecode ? libmail_gpg_decode(dodecode, &gpg_info)
+ : libmail_gpg_signencode(dosign, doencode, &gpg_info);
+
+
+ if (rc == 0 && (fflush(stdout) || ferror(stdout)))
+ {
+ perror("write");
+ rc=1;
+ }
+
+ exit(rc);
+ return (0);
+}
diff --git a/gpglib/mimegpg.sgml b/gpglib/mimegpg.sgml
new file mode 100644
index 0000000..3bce16f
--- /dev/null
+++ b/gpglib/mimegpg.sgml
@@ -0,0 +1,152 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<!-- Copyright 1998 - 2008 Double Precision, Inc. See COPYING for -->
+<!-- distribution information. -->
+<refentry>
+ <info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info>
+
+ <refmeta>
+ <refentrytitle>mimegpg</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo>Double Precision, Inc.</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>mimegpg</refname>
+ <refpurpose>MIME-GPG utility</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command moreinfo="none">mimegpg</command>
+ <arg choice="opt" rep="norepeat">-s</arg>
+ <arg choice="opt" rep="norepeat">-E</arg>
+ <arg choice="opt" rep="norepeat">-e</arg>
+ <arg choice="opt" rep="norepeat">-c</arg>
+ <arg choice="opt" rep="norepeat">-d</arg>
+ <arg choice="opt" rep="norepeat">-p <replaceable>n</replaceable></arg>
+ <arg choice="req" rep="norepeat">--</arg>
+ <arg rep="repeat" choice="req">gpg options</arg>
+
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+The <command moreinfo="none">mimegpg</command> tool signs, encrypts, or decrypts MIME-formatted E-mail
+messages using GnuPG. <command moreinfo="none">mimegpg</command> does not contain any encryption code. It
+uses the GnuPG utility for all encryption and decryption functions.</para>
+
+ <para>
+The <option>-s</option> option signs an E-mail message. The <option>-E</option> or
+the <option>-e</option> option encrypts the E-mail message. Specifying both
+<option>-E/-e</option> and <option>-s</option> encrypts and signs the E-mail message
+in a single step. The <option>-d</option> option decrypts the message. The
+<option>-c</option> option checks signatures.</para>
+
+ <para>
+<command moreinfo="none">mimegpg</command> works as a filter. It reads an E-mail message from
+standard input, which must be a MIME-formatted message. <command moreinfo="none">mimegpg</command> signs,
+encrypts, and/or decrypts the message; then writes the encrypted, signed, or
+decrypted MIME message on standard output.</para>
+
+ <para>
+The standard input to <command moreinfo="none">mimegpg</command> must be a MIME E-mail message, with a
+"<literal moreinfo="none">Mime Version: 1.0</literal>" header - even if the message does not contain any
+attachments. If the message contains any attachments, they are also signed
+and/or encrypted, individually.</para>
+
+ <para>
+<command moreinfo="none">mimegpg</command> automatically runs GnuPG, with the required options.
+<command moreinfo="none">mimegpg</command>'s options may also be followed by a single <literal moreinfo="none">--</literal>
+option; any remaining command line options are passed as additional options
+to GnuPG. The <option>-E/-e</option> option usually requires at least one
+<option>-r</option> GnuPG option, which may be specified in this fashion.</para>
+
+ <para>
+The <option>-p</option> option specifies a file descriptor that contains any
+required passphrase. Any other valid GnuPG options may follow a double-dash,
+"--", as long as it makes sense for this operation (note that <command moreinfo="none">mimegpg</command>
+automatically adds any GnuPG options that are needed to perform the given
+operation). The "--no-tty" option can be useful when <command moreinfo="none">mimegpg</command> is
+used in a non-interactive mode. As always, secret keys that are
+password-protected secret keys cannot be used in the <option>--no-tty</option>
+mode, unless the <option>-p</option> option is used.</para>
+
+ <refsect2>
+ <title>SIGNING AND ENCRYPTING MESSAGES</title>
+
+ <para>
+Use the <option>-s</option> option to sign MIME message content. Use the
+<option>-E</option> option to encrypt it. Use both options to both sign and
+encrypt. Follow with <literal moreinfo="none">--</literal>, then any other GnuPG options. The
+<option>-E</option> option will require at least one <option>-r</option> GnuPG
+option.</para>
+
+ <para>
+The <option>-E</option> option encapsulates the message content and all the
+attachments into a single encrypted MIME object. Some mail software cannot
+handle encapsulated content. The <option>-e</option> option encrypts each
+attachment separately, without encapsulation.</para>
+
+ <para>
+If a secret key used for signing is password protected, the prompt to
+enter the password will be issued directly by GnuPG. Note that the
+<option>-s</option> and the <option>-e</option> (but not <option>-E</option>) options may
+issue multiple password prompts in interactive mode. <command moreinfo="none">mimegpg</command> runs
+GnuPG multiple times if the MIME message contains attachments. GnuPG will be
+invoked separately for each attachment in the MIME message, and each
+invocation will prompt for any required key password. Note that the initial
+message headers are not signed and/or encrypted, except for the MIME headers
+themselves. However, any <literal moreinfo="none">message/rfc822</literal> MIME content - attached
+messages - are encrypted/signed in their entirety, headers and content.</para>
+ </refsect2>
+
+ <refsect2>
+ <title>DECRYPTING AND CHECKING SIGNATURES</title>
+
+ <para>
+The <option>-d</option> option attempts to decrypt any encrypted content in a
+MIME message. The <option>-c</option> option attempts to verify signatures of
+any signed content. Both <option>-c</option> and <option>-d</option>
+can be specified at the
+same time. <option>-d</option> looks for any <literal moreinfo="none">multipart/encrypted</literal>
+MIME content, then attempts to decrypt it. <option>-c</option> looks for any
+<literal moreinfo="none">multipart/signed</literal> MIME content, then attempts to verify the
+signature.</para>
+
+ <para>
+The <option>-c</option> and <option>-d</option> options replace the
+<literal moreinfo="none">multipart/signed</literal> and <literal moreinfo="none">multipart/encrypted</literal> MIME
+content with a <literal moreinfo="none">multipart/x-mimepgp</literal> section, that contains an
+additional attribute called "xpgpstatus". The value of the attribute is set
+to the exit code of GnuPG. Succesfully decrypting the message and/or
+verifying the signature sets the exit code to 0. A non-zero exit code
+indicates that the signature could not be verified, or the message could not
+be decrypted.</para>
+
+ <para>
+The first section in this <literal moreinfo="none">multipart/x-mimepgp</literal> is a
+<literal moreinfo="none">text/plain</literal> section that contains any messages from GnuPG. The
+second section is any decrypted or signed content. <command moreinfo="none">mimegpg</command> will
+include the signed content even if the signature could not be verified (check
+<literal moreinfo="none">xpgpstatus</literal>). Encrypted content that could not be decrypted will
+not be included (obviously).</para>
+
+ <para>
+Note - any existing x-mimegpg MIME section will have its content-type
+quietly reset to multipart/mixed, in order to avoid confusion (except when
+this is what got decrypted).</para>
+
+ </refsect2>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>
+<ulink url="reformime.html"><citerefentry><refentrytitle>reformime</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>.</para>
+ </refsect1>
+
+</refentry>
diff --git a/gpglib/mimegpgfork.c b/gpglib/mimegpgfork.c
new file mode 100644
index 0000000..d1b849c
--- /dev/null
+++ b/gpglib/mimegpgfork.c
@@ -0,0 +1,449 @@
+/*
+** Copyright 2001-2010 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "gpg.h"
+
+#include <sys/types.h>
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include "mimegpgfork.h"
+
+static int libmail_gpgmime_fork(const char *gpgdir,
+ const char *passphrase_fd,
+ int xargc, char **xargv, int argc, char **argv,
+ struct gpgmime_forkinfo *fi)
+{
+ int pipes[3][2];
+ int n;
+ pid_t p;
+ struct sigaction sa;
+
+ for (n=0; n<3; n++)
+ {
+ if (pipe(pipes[n]) < 0)
+ {
+ while (n)
+ {
+ --n;
+ close(pipes[n][0]);
+ close(pipes[n][1]);
+ return (-1);
+ }
+ }
+ }
+
+ if (fcntl(pipes[0][1], F_SETFL, O_NONBLOCK) ||
+ fcntl(pipes[1][0], F_SETFL, O_NONBLOCK) ||
+ fcntl(pipes[2][0], F_SETFL, O_NONBLOCK))
+ {
+ for (n=0; n<3; n++)
+ {
+ close(pipes[n][0]);
+ close(pipes[n][1]);
+ }
+ return (-1);
+ }
+
+ signal(SIGCHLD, SIG_DFL);
+
+ p=fork();
+ if (p < 0)
+ {
+ for (n=0; n<3; n++)
+ {
+ close(pipes[n][0]);
+ close(pipes[n][1]);
+ }
+ return (-1);
+ }
+
+ if (p == 0)
+ {
+ char **newargv;
+ int i;
+ const char *gpg;
+
+ dup2(pipes[0][0], 0);
+ dup2(pipes[1][1], 1);
+ dup2(pipes[2][1], 2);
+
+ for (n=0; n<3; n++)
+ {
+ close(pipes[n][0]);
+ close(pipes[n][1]);
+ }
+
+ newargv=malloc( (xargc + argc + 5) * sizeof(char *));
+ if (!newargv)
+ {
+ perror("malloc");
+ _exit(1);
+ }
+
+ i=0;
+ newargv[i++]="gpg";
+ if (passphrase_fd)
+ {
+ int n=atoi(passphrase_fd);
+
+ if (lseek(n, 0L, SEEK_SET) < 0)
+ {
+ perror("passphrase-fd: seek");
+ _exit(1);
+ }
+
+ newargv[i++]="--batch";
+ newargv[i++]="--passphrase-fd";
+ newargv[i++]=(char *)passphrase_fd;
+ }
+
+ for (n=0; n<xargc; n++)
+ newargv[i++]=xargv[n];
+ for (n=0; n<argc; n++)
+ newargv[i++]=argv[n];
+
+ newargv[i]=0;
+
+ if (gpgdir)
+ {
+ char *s;
+
+ s=malloc(sizeof("GNUPGHOME=")+strlen(gpgdir));
+ if (!s)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ strcat(strcpy(s, "GNUPGHOME="), gpgdir);
+ if (putenv(s) < 0)
+ {
+ perror("putenv");
+ exit(1);
+ }
+ }
+
+ gpg=getenv("GPG");
+ if (!gpg || !*gpg)
+ gpg=GPG;
+
+ execvp(gpg, newargv);
+ perror(gpg);
+ _exit(1);
+ }
+
+ fi->gpg_errflag=0;
+ fi->togpg_fd=pipes[0][1];
+ close(pipes[0][0]);
+ fi->fromgpg_fd=pipes[1][0];
+ close(pipes[1][1]);
+ fi->fromgpg_errfd=pipes[2][0];
+ close(pipes[2][1]);
+
+ fi->gpg_writecnt=0;
+ fi->gpg_errcnt=0;
+ fi->gpg_errbuf[0]=0;
+ fi->gpg_pid=p;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler=SIG_IGN;
+ sigaction(SIGPIPE, &sa, &fi->old_pipe_sig);
+
+ fi->gpg_readhandler=0;
+ fi->gpg_voidarg=0;
+ return (0);
+}
+
+static void gpgmime_writeflush(struct gpgmime_forkinfo *);
+
+void libmail_gpgmime_write(struct gpgmime_forkinfo *fi, const char *p, size_t n)
+{
+ while (n)
+ {
+ size_t i;
+
+ if (fi->gpg_writecnt == sizeof(fi->gpg_writebuf))
+ gpgmime_writeflush(fi);
+
+ i=sizeof(fi->gpg_writebuf) - fi->gpg_writecnt;
+
+ if ((size_t)i > n)
+ i=n;
+
+ memcpy(fi->gpg_writebuf + fi->gpg_writecnt, p, i);
+
+ fi->gpg_writecnt += i;
+
+ p += i;
+ n -= i;
+ }
+}
+
+static void libmail_gpgmime_read(struct gpgmime_forkinfo *fi, fd_set *fdr)
+{
+ char readbuf[BUFSIZ];
+ int i;
+
+ if (fi->fromgpg_fd >= 0 && FD_ISSET(fi->fromgpg_fd, fdr))
+ {
+ i=read(fi->fromgpg_fd, readbuf, sizeof(readbuf));
+
+ if (i <= 0)
+ {
+ close(fi->fromgpg_fd);
+ fi->fromgpg_fd= -1;
+ }
+ else
+ {
+ if (fi->gpg_readhandler)
+ fi->gpg_errflag=
+ (*fi->gpg_readhandler)(readbuf, i,
+ fi->gpg_voidarg
+ );
+ }
+ }
+
+ if (fi->fromgpg_errfd >= 0 && FD_ISSET(fi->fromgpg_errfd, fdr))
+ {
+ i=read(fi->fromgpg_errfd, readbuf, sizeof(readbuf));
+
+ if (i <= 0)
+ {
+ close(fi->fromgpg_errfd);
+ fi->fromgpg_errfd= -1;
+ }
+ else
+ {
+ if (i >= sizeof(fi->gpg_errbuf)-1-fi->gpg_errcnt)
+ i=sizeof(fi->gpg_errbuf)-1-fi->gpg_errcnt;
+ if (i)
+ {
+ memcpy(fi->gpg_errbuf+fi->gpg_errcnt,
+ readbuf, i);
+ fi->gpg_errbuf[fi->gpg_errcnt += i]=0;
+ }
+ }
+ }
+}
+
+static void gpgmime_writeflush(struct gpgmime_forkinfo *fi)
+{
+ const char *p=fi->gpg_writebuf;
+ unsigned n=fi->gpg_writecnt;
+
+ while (n && !fi->gpg_errflag)
+ {
+ fd_set fdr, fdw;
+ int maxfd=fi->togpg_fd, i;
+
+ FD_ZERO(&fdr);
+ FD_ZERO(&fdw);
+
+ FD_SET(fi->togpg_fd, &fdw);
+
+ if (fi->fromgpg_fd >= 0)
+ {
+ FD_SET(fi->fromgpg_fd, &fdr);
+ if (fi->fromgpg_fd > maxfd)
+ maxfd=fi->fromgpg_fd;
+ }
+
+ if (fi->fromgpg_errfd >= 0)
+ {
+ FD_SET(fi->fromgpg_errfd, &fdr);
+ if (fi->fromgpg_errfd > maxfd)
+ maxfd=fi->fromgpg_errfd;
+ }
+
+ if (select(maxfd+1, &fdr, &fdw, NULL, NULL) <= 0)
+ continue;
+
+ libmail_gpgmime_read(fi, &fdr);
+
+ if (!FD_ISSET(fi->togpg_fd, &fdw))
+ continue;
+
+ i=write(fi->togpg_fd, p, n);
+
+ if (i <= 0)
+ fi->gpg_errflag=1;
+ else
+ {
+ p += i;
+ n -= i;
+ }
+ }
+ fi->gpg_writecnt=0;
+}
+
+int libmail_gpgmime_finish(struct gpgmime_forkinfo *fi)
+{
+ pid_t p2;
+ int waitstat;
+
+ gpgmime_writeflush(fi);
+ close(fi->togpg_fd);
+ sigaction(SIGPIPE, &fi->old_pipe_sig, NULL);
+
+ while (!fi->gpg_errflag &&
+ (fi->fromgpg_fd >= 0 || fi->fromgpg_errfd >= 0))
+ {
+ fd_set fdr;
+ int maxfd=0;
+
+ FD_ZERO(&fdr);
+
+ if (fi->fromgpg_fd >= 0)
+ {
+ FD_SET(fi->fromgpg_fd, &fdr);
+ if (fi->fromgpg_fd > maxfd)
+ maxfd=fi->fromgpg_fd;
+ }
+
+ if (fi->fromgpg_errfd >= 0)
+ {
+ FD_SET(fi->fromgpg_errfd, &fdr);
+ if (fi->fromgpg_errfd > maxfd)
+ maxfd=fi->fromgpg_errfd;
+ }
+
+ if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0)
+ continue;
+
+ libmail_gpgmime_read(fi, &fdr);
+ }
+
+ while ((p2=wait(&waitstat)) != fi->gpg_pid)
+ {
+ if (p2 < 0 && errno == ECHILD)
+ break;
+ }
+
+ if (fi->gpg_errflag == 0)
+ {
+ if (!WIFEXITED(waitstat))
+ fi->gpg_errflag=1;
+ else
+ fi->gpg_errflag=WEXITSTATUS(waitstat);
+ }
+ return (fi->gpg_errflag);
+}
+
+int libmail_gpgmime_forksignencrypt(const char *gpgdir,
+ const char *passphrase_fd,
+ int flags,
+ int argc, char **argv,
+ int (*output_func)(const char *,
+ size_t, void *),
+ void *output_voidarg,
+ struct gpgmime_forkinfo *gpgfi)
+{
+ char *xargvec[5];
+ int xargc=0;
+ int rc;
+
+ if (flags & GPG_SE_SIGN)
+ {
+ xargvec[xargc++]="-s";
+ if (! (flags & GPG_SE_ENCRYPT))
+ xargvec[xargc++]="-b";
+ }
+
+ if (flags & GPG_SE_ENCRYPT)
+ xargvec[xargc++]="-e";
+
+ xargvec[xargc++]="-a";
+
+ rc=libmail_gpgmime_fork(gpgdir, passphrase_fd,
+ xargc, xargvec, argc, argv, gpgfi);
+
+ gpgfi->gpg_readhandler= output_func;
+ gpgfi->gpg_voidarg=output_voidarg;
+ return (rc);
+}
+
+int libmail_gpgmime_forkchecksign(const char *gpgdir,
+ const char *passphrase_fd,
+ const char *content_filename,
+ const char *signature_filename,
+ int argc, char **argv,
+ struct gpgmime_forkinfo *gpgfi)
+{
+ char *xargvec[3];
+ char **newargv;
+ int i;
+ int rc;
+
+ xargvec[0]="--verify";
+ xargvec[1]="--charset";
+ xargvec[2]=GPG_CHARSET;
+
+ newargv=(char **)malloc((argc+2)*sizeof(char *));
+ if (!newargv)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ for (i=0; i<argc; i++)
+ newargv[i]=argv[i];
+ newargv[i++]=(char *)signature_filename;
+ newargv[i++]=(char *)content_filename;
+
+ rc=libmail_gpgmime_fork(gpgdir, passphrase_fd, 3, xargvec, i, newargv, gpgfi);
+ free(newargv);
+ return (rc);
+}
+
+int libmail_gpgmime_forkdecrypt(const char *gpgdir,
+ const char *passphrase_fd,
+ int argc, char **argv,
+ int (*output_func)(const char *, size_t, void *),
+ void *output_voidarg,
+ struct gpgmime_forkinfo *gpgfi)
+{
+ char *xargv[3];
+ int rc;
+
+ xargv[0]="--decrypt";
+ xargv[1]="--charset";
+ xargv[2]=GPG_CHARSET;
+
+ rc=libmail_gpgmime_fork(gpgdir, passphrase_fd, 3, xargv, argc, argv, gpgfi);
+
+ gpgfi->gpg_readhandler= output_func;
+ gpgfi->gpg_voidarg= output_voidarg;
+ return (rc);
+}
+
+const char *libmail_gpgmime_getoutput(struct gpgmime_forkinfo *gpgfi)
+{
+ return (gpgfi->gpg_errbuf);
+}
+
+const char *libmail_gpgmime_getcharset(struct gpgmime_forkinfo *gpgfi)
+{
+ return (GPG_CHARSET);
+}
diff --git a/gpglib/mimegpgfork.h b/gpglib/mimegpgfork.h
new file mode 100644
index 0000000..cebee60
--- /dev/null
+++ b/gpglib/mimegpgfork.h
@@ -0,0 +1,84 @@
+#ifndef mimegpgfork_h
+#define mimegpgfork_h
+
+/*
+** Copyright 2001 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct gpgmime_forkinfo {
+ int togpg_fd;
+ int fromgpg_fd;
+ int fromgpg_errfd;
+
+ char gpg_writebuf[BUFSIZ];
+ char gpg_errbuf[1024];
+
+ unsigned gpg_writecnt;
+ unsigned gpg_errcnt;
+
+ int gpg_errflag;
+ pid_t gpg_pid;
+
+ struct sigaction old_pipe_sig;
+
+ int (*gpg_readhandler)(const char *, size_t, void *);
+ void *gpg_voidarg;
+} ;
+
+int libmail_gpgmime_forksignencrypt(const char *, /* gpgdir */
+ const char *, /* passphrase fd */
+ int, /* Flags: */
+
+#define GPG_SE_SIGN 1
+#define GPG_SE_ENCRYPT 2
+
+ int, char **, /* argc/argv */
+
+ int (*)(const char *, size_t, void *),
+ /* Encrypted output */
+ void *, /* 3rd arg to encrypted output */
+
+ struct gpgmime_forkinfo *
+ /* Allocated struct */
+ );
+
+int libmail_gpgmime_forkchecksign(const char *, /* gpgdir */
+ const char *, /* passphrase fd */
+ const char *, /* content filename */
+ const char *, /* signature filename */
+ int, char **, /* argc/argv */
+ struct gpgmime_forkinfo *);
+ /* Allocated struct */
+
+int libmail_gpgmime_forkdecrypt(const char *, /* gpgdir */
+ const char *, /* passphrase fd */
+ int, char **, /* argc/argv */
+ int (*)(const char *, size_t, void *),
+ /* Output callback function */
+ void *, /* 3rd arg to callback function */
+
+ struct gpgmime_forkinfo *);
+ /* Allocated struct */
+
+void libmail_gpgmime_write(struct gpgmime_forkinfo *, const char *, size_t);
+int libmail_gpgmime_finish(struct gpgmime_forkinfo *);
+
+const char *libmail_gpgmime_getoutput(struct gpgmime_forkinfo *);
+const char *libmail_gpgmime_getcharset(struct gpgmime_forkinfo *);
+
+#ifdef __cplusplus
+} ;
+#endif
+
+#endif
diff --git a/gpglib/mimegpgheader.c b/gpglib/mimegpgheader.c
new file mode 100644
index 0000000..cb04e13
--- /dev/null
+++ b/gpglib/mimegpgheader.c
@@ -0,0 +1,393 @@
+/*
+** Copyright 2001 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "mimegpgheader.h"
+
+void libmail_readheader_init(struct read_header_context *cts)
+{
+ cts->continue_header=0;
+ cts->header_len=0;
+ cts->first=0;
+ cts->last=0;
+}
+
+int libmail_readheader(struct read_header_context *cts, const char *buf)
+{
+ int l;
+ char *s;
+ struct header *p;
+
+ l=strlen(buf);
+ p=0;
+
+ if (cts->continue_header)
+ p=cts->last;
+ else if (isspace((int)(unsigned char)buf[0]))
+ p=cts->last;
+
+ if (!p)
+ {
+ p=(struct header *)malloc(sizeof(*p));
+ if (!p)
+ return -1;
+
+ p->next=0;
+ p->header=0;
+ cts->header_len=0;
+ if (cts->last)
+ cts->last->next=p;
+ else
+ cts->first=p;
+ cts->last=p;
+ }
+
+ s=p->header ? realloc(p->header, cts->header_len + l + 1)
+ :malloc(l+1);
+
+ if (!s)
+ return (-1);
+
+ strcpy(s+cts->header_len, buf);
+ cts->header_len += l;
+ p->header=s;
+
+ cts->continue_header=!l || buf[l-1] != '\n';
+
+ return 0;
+}
+
+struct header *libmail_readheader_finish(struct read_header_context *cts)
+{
+ struct header *last;
+
+ for (last=cts->first; last; last=last->next)
+ {
+ char *q, *r;
+
+ if (!last->header)
+ continue;
+
+ for (q=r=last->header; *r; r++)
+ {
+ if (*r == '\r')
+ continue;
+ *q++ = *r;
+ }
+ *q=0;
+ }
+ return (cts->first);
+}
+
+void libmail_header_free(struct header *p)
+{
+ struct header *q;
+
+ while (p)
+ {
+ q=p->next;
+ if (p->header)
+ free(p->header);
+ free(p);
+ p=q;
+ }
+}
+
+struct header *libmail_header_find(struct header *p, const char *n)
+{
+ int l=strlen(n);
+
+ while (p)
+ {
+ if (p->header && strncasecmp(p->header, n, l) == 0)
+ return (p);
+ p=p->next;
+ }
+ return (p);
+}
+
+const char *libmail_header_find_txt(struct header *p, const char *n)
+{
+ p=libmail_header_find(p, n);
+ if (p)
+ return (p->header+strlen(n));
+ return (NULL);
+}
+
+void libmail_mimeheader_free(struct mime_header *m)
+{
+ struct mime_attribute *a;
+
+ while ((a=m->attr_list) != 0)
+ {
+ m->attr_list=a->next;
+ if (a->name)
+ free(a->name);
+ if (a->value)
+ free(a->value);
+ free(a);
+ }
+ free(m->header_name);
+ free(m);
+}
+
+static void striptrlspc(char *p)
+{
+ char *q;
+
+ for (q=p; *p; p++)
+ if (!isspace((int)(unsigned char)*p))
+ q=p+1;
+ *q=0;
+}
+
+struct mime_header *libmail_mimeheader_parse(const char *field)
+{
+ struct mime_header *header=0;
+ struct mime_attribute *last_attr=0;
+
+ while (*field || header == 0)
+ {
+ const char *p;
+
+ if (*field && isspace((int)(unsigned char)*field))
+ {
+ ++field;
+ continue;
+ }
+ for (p=field; *p; p++)
+ {
+ if (*p == ';')
+ break;
+ if (*p == '=' && header)
+ break;
+ }
+
+ if (!header)
+ {
+ if ((header=(struct mime_header *)
+ malloc(sizeof(*header))) == 0)
+ return NULL;
+
+ if ((header->header_name=malloc(p-field+1)) == 0)
+ {
+ free(header);
+ return (NULL);
+ }
+ memcpy(header->header_name, field, p-field);
+ header->header_name[p-field]=0;
+ header->attr_list=NULL;
+ striptrlspc(header->header_name);
+ field=p;
+ }
+ else
+ {
+ struct mime_attribute *a=(struct mime_attribute *)
+ malloc(sizeof(struct mime_attribute));
+ int pass, len;
+ char *s=0;
+
+ if (!a)
+ {
+ libmail_mimeheader_free(header);
+ return NULL;
+ }
+ if ((a->name=malloc(p-field+1)) == NULL)
+ {
+ free(a);
+ libmail_mimeheader_free(header);
+ return NULL;
+ }
+ memcpy(a->name, field, p-field);
+ a->name[p-field]=0;
+ striptrlspc(a->name);
+ a->value=0;
+ a->next=0;
+ if (last_attr)
+ {
+ last_attr->next=a;
+ }
+ else
+ {
+ header->attr_list=a;
+ }
+ last_attr=a;
+
+ if (*p == '=')
+ ++p;
+
+ while (*p && isspace((int)(unsigned char)*p))
+ {
+ ++p;
+ }
+
+ field=p;
+
+ len=0;
+ for (pass=0; pass<2; pass++)
+ {
+ int quote=0;
+
+ if (pass)
+ {
+ s=a->value=malloc(len);
+ if (!s)
+ {
+ libmail_mimeheader_free(header);
+ return NULL;
+ }
+ }
+ len=0;
+
+ for (p=field; *p; )
+ {
+ if (*p == ';' && !quote)
+ {
+ break;
+ }
+
+ if (*p == '"')
+ {
+ quote= !quote;
+ ++p;
+ continue;
+ }
+
+ if (*p == '\\' && p[1])
+ ++p;
+
+ if (pass)
+ s[len]= *p;
+ ++len;
+ ++p;
+ }
+ if (pass)
+ s[len]=0;
+ ++len;
+ }
+
+ striptrlspc(a->value);
+ field=p;
+ if (a->value[0] == 0)
+ {
+ free(a->value);
+ a->value=0;
+ }
+ }
+ if (*field == ';')
+ ++field;
+ }
+
+ return (header);
+}
+
+const char *libmail_mimeheader_getattr(struct mime_header *m, const char *name)
+{
+ struct mime_attribute *a;
+
+ for (a=m->attr_list; a; a=a->next)
+ {
+ if (strcasecmp(a->name, name) == 0)
+ return (a->value);
+ }
+ return (0);
+}
+
+#if 0
+void libmail_mimeheader_setattr(struct mime_header *m,
+ const char *name, const char *value)
+{
+ struct mime_attribute *a, *lasta=0;
+
+ for (a=m->attr_list; a; a=a->next)
+ {
+ if (strcasecmp(a->name, name) == 0)
+ {
+ if (a->value)
+ free(a->value);
+ a->value=0;
+ if (value)
+ {
+ a->value=strdup(value);
+ if (!a->value)
+ {
+ perror("strdup");
+ exit(1);
+ }
+ }
+ return;
+ }
+ lasta=a;
+ }
+
+ a=(struct mime_attribute *)malloc(sizeof(struct mime_attribute));
+ if (!a)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ if (!(a->name=strdup(name)))
+ {
+ free(a);
+ perror("malloc");
+ exit(1);
+ }
+
+ a->value=0;
+ a->next=0;
+ if (value && !(a->value=strdup(value)))
+ {
+ free(a->name);
+ free(a);
+ perror("malloc");
+ exit(1);
+ }
+
+ if (lasta)
+ lasta->next=a;
+ else
+ m->attr_list=a;
+}
+
+void print_mime_header(FILE *f, struct mime_header *m)
+{
+ struct mime_attribute *a;
+
+ fprintf(f, "%s", m->header_name);
+
+ for (a=m->attr_list; a; a=a->next)
+ {
+ const char *p;
+
+ fprintf(f, "; %s%s", a->name, a->value ? "=":"");
+ if (!a->value)
+ continue;
+
+ for (p=a->value; *p; p++)
+ if (strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_", *p) == NULL)
+ break;
+
+ if (!*p)
+ {
+ fprintf(f, "%s", a->value);
+ continue;
+ }
+ putc('"', f);
+ for (p=a->value; *p; p++)
+ {
+ if (*p == '"' || *p == '\\')
+ putc('\\', f);
+ putc(*p, f);
+ }
+ putc('"', f);
+ }
+}
+#endif
diff --git a/gpglib/mimegpgheader.h b/gpglib/mimegpgheader.h
new file mode 100644
index 0000000..c907708
--- /dev/null
+++ b/gpglib/mimegpgheader.h
@@ -0,0 +1,54 @@
+#ifndef mimegpgheader_h
+#define mimegpgheader_h
+/*
+** Copyright 2001 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct header {
+ struct header *next;
+ char *header;
+} ;
+
+struct read_header_context {
+ int continue_header;
+ int header_len;
+ struct header *first, *last;
+} ;
+
+void libmail_readheader_init(struct read_header_context *);
+int libmail_readheader(struct read_header_context *, const char *);
+struct header *libmail_readheader_finish(struct read_header_context *);
+#define READ_START_OF_LINE(cts) ((cts).continue_header == 0)
+
+void libmail_header_free(struct header *p);
+struct header *libmail_header_find(struct header *p, const char *n);
+const char *libmail_header_find_txt(struct header *p, const char *n);
+
+struct mime_header {
+ char *header_name;
+ struct mime_attribute *attr_list;
+} ;
+
+struct mime_attribute {
+ struct mime_attribute *next;
+ char *name, *value;
+} ;
+
+void libmail_mimeheader_free(struct mime_header *);
+struct mime_header *libmail_mimeheader_parse(const char *);
+const char *libmail_mimeheader_getattr(struct mime_header *, const char *);
+
+#ifdef __cplusplus
+} ;
+#endif
+
+#endif
diff --git a/gpglib/mimegpgstack.c b/gpglib/mimegpgstack.c
new file mode 100644
index 0000000..0eb61d9
--- /dev/null
+++ b/gpglib/mimegpgstack.c
@@ -0,0 +1,72 @@
+/*
+** Copyright 2001 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include "mimegpgstack.h"
+
+int libmail_mimestack_push(struct mimestack **s, const char *b)
+{
+ struct mimestack *ss=(struct mimestack *)
+ malloc(sizeof(**s));
+
+ if (!ss)
+ return -1;
+
+ if ((ss->boundary=strdup(b)) == NULL)
+ {
+ free(ss);
+ return -1;
+ }
+
+ ss->next= *s;
+ *s=ss;
+ return 0;
+}
+
+void libmail_mimestack_pop(struct mimestack **p)
+{
+ struct mimestack *pp= *p;
+
+ if (pp)
+ {
+ *p=pp->next;
+ free(pp->boundary);
+ free(pp);
+ }
+}
+
+void libmail_mimestack_pop_to(struct mimestack **p, struct mimestack *s)
+{
+ while (*p)
+ {
+ int last=strcmp( (*p)->boundary, s->boundary) == 0;
+ libmail_mimestack_pop(p);
+ if (last)
+ break;
+ }
+}
+
+struct mimestack *libmail_mimestack_search(struct mimestack *p, const char *c)
+{
+ int l=strlen(c);
+
+ while (p)
+ {
+ int ll=strlen(p->boundary);
+
+ if (l >= ll && strncasecmp(p->boundary, c, ll) == 0)
+ break;
+ p=p->next;
+ }
+ return (p);
+}
+
+
diff --git a/gpglib/mimegpgstack.h b/gpglib/mimegpgstack.h
new file mode 100644
index 0000000..a4b2d0b
--- /dev/null
+++ b/gpglib/mimegpgstack.h
@@ -0,0 +1,32 @@
+#ifndef mimegpgstack_h
+#define mimegpgstack_h
+
+/*
+** Copyright 2001 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mimestack {
+ struct mimestack *next;
+ char *boundary;
+} ;
+
+int libmail_mimestack_push(struct mimestack **, const char *);
+void libmail_mimestack_pop(struct mimestack **);
+
+struct mimestack *libmail_mimestack_search(struct mimestack *, const char *);
+
+void libmail_mimestack_pop_to(struct mimestack **, struct mimestack *);
+
+#ifdef __cplusplus
+} ;
+#endif
+
+#endif
diff --git a/gpglib/options.c b/gpglib/options.c
new file mode 100644
index 0000000..e758872
--- /dev/null
+++ b/gpglib/options.c
@@ -0,0 +1,41 @@
+/*
+** Copyright 2004 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "gpg.h"
+
+char *libmail_gpg_options(const char *p)
+{
+ if (!p || !*p)
+ p=getenv("GNUPGHOME");
+
+ if (p && *p)
+ {
+ char *s=malloc(strlen(p)+sizeof("/options"));
+
+ if (s)
+ return (strcat(strcpy(s, p), "/options"));
+ }
+ else
+ {
+ p=getenv("HOME");
+
+ if (p && *p)
+ {
+ char *s=malloc(strlen(p)+sizeof("/.gnupg/options"));
+
+ if (s)
+ return (strcat(strcpy(s, p),
+ "/.gnupg/options"));
+ }
+ }
+ return NULL;
+}
diff --git a/gpglib/rfc2045.c b/gpglib/rfc2045.c
new file mode 100644
index 0000000..df598ec
--- /dev/null
+++ b/gpglib/rfc2045.c
@@ -0,0 +1,115 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "rfc2045/rfc2045.h"
+
+struct rfc2045 *libmail_gpgmime_is_multipart_signed(const struct rfc2045 *q)
+{
+ struct rfc2045 *p;
+
+ if (!q->content_type || strcmp(q->content_type, "multipart/signed"))
+ return (0);
+
+ for (p=q->firstpart; p && p->isdummy; p=p->next)
+ ;
+
+ if (p && p->next && p->next->content_type &&
+ strcmp(p->next->content_type, "application/pgp-signature") == 0)
+ return (p);
+
+ return (NULL);
+}
+
+struct rfc2045 *libmail_gpgmime_is_multipart_encrypted(const struct rfc2045 *q)
+{
+ struct rfc2045 *p;
+
+ if (!q->content_type || strcmp(q->content_type, "multipart/encrypted"))
+ return (0);
+
+ for (p=q->firstpart; p && p->isdummy; p=p->next)
+ ;
+
+ if (p && p->content_type && p->next && p->next->content_type &&
+ strcmp(p->content_type, "application/pgp-encrypted") == 0 &&
+ strcmp(p->next->content_type, "application/octet-stream") == 0)
+ return (p->next);
+
+ return (NULL);
+}
+
+int libmail_gpgmime_has_mimegpg(const struct rfc2045 *q)
+{
+ if (libmail_gpgmime_is_multipart_signed(q) ||
+ libmail_gpgmime_is_multipart_encrypted(q))
+ return (1);
+
+ for (q=q->firstpart; q; q=q->next)
+ {
+ if (q->isdummy)
+ continue;
+ if (libmail_gpgmime_has_mimegpg(q))
+ return (1);
+ }
+ return (0);
+}
+
+int libmail_gpgmime_is_decoded(const struct rfc2045 *q, int *retcode)
+{
+ const char *p;
+
+ if (!q->content_type || strcasecmp(q->content_type,
+ "multipart/x-mimegpg"))
+ return (0);
+
+ p=rfc2045_getattr(q->content_type_attr, "xpgpstatus");
+ if (!p)
+ return (0);
+
+ *retcode=atoi(p);
+ return (1);
+}
+
+struct rfc2045 *libmail_gpgmime_decoded_content(const struct rfc2045 *q)
+{
+ for (q=q->firstpart; q; q=q->next)
+ {
+ if (q->isdummy)
+ continue;
+
+ return (q->next);
+ }
+ return (NULL);
+}
+
+struct rfc2045 *libmail_gpgmime_signed_content(const struct rfc2045 *p)
+{
+ struct rfc2045 *q;
+
+ for (q=p->firstpart; q; q=q->next)
+ {
+ if (q->isdummy)
+ continue;
+
+ return (q);
+ }
+ return (NULL);
+}
+
diff --git a/gpglib/sign.c b/gpglib/sign.c
new file mode 100644
index 0000000..a92a504
--- /dev/null
+++ b/gpglib/sign.c
@@ -0,0 +1,109 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "gpg.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr;
+extern pid_t libmail_gpg_pid;
+
+
+/*
+** Sign a key.
+*/
+
+static int dosignkey(int (*)(const char *, size_t, void *),
+ const char *cmdstr,
+ void *);
+
+int libmail_gpg_signkey(const char *gpgdir, const char *signthis, const char *signwith,
+ int passphrase_fd,
+ int (*dump_func)(const char *, size_t, void *),
+ int trust_level,
+ void *voidarg)
+{
+ char *argvec[12];
+ int rc;
+ char passphrase_fd_buf[NUMBUFSIZE];
+ int i;
+
+ argvec[0]="gpg";
+ argvec[1]="--command-fd";
+ argvec[2]="0";
+ argvec[3]="--default-key";
+ argvec[4]=(char *)signwith;
+ argvec[5]="-q";
+ argvec[6]="--no-tty";
+
+ i=7;
+ if (passphrase_fd >= 0 && fcntl(passphrase_fd, F_SETFD, 0) >= 0)
+ {
+ GPGARGV_PASSPHRASE_FD(argvec, i, passphrase_fd,
+ passphrase_fd_buf);
+ }
+
+ argvec[i++]="--sign-key";
+ argvec[i++]=(char *)signthis;
+ argvec[i]=0;
+
+ if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL, gpgdir, argvec) < 0)
+ rc= -1;
+ else
+ {
+ int rc2;
+
+ char cmdstr[10];
+
+#if GPG_HAS_CERT_CHECK_LEVEL
+
+ cmdstr[0]='0';
+
+ if (trust_level > 0 && trust_level <= 9)
+ cmdstr[0]='0' + trust_level;
+
+ strcpy(cmdstr+1, "\nY\n");
+
+#else
+ strcpy(cmdstr, "Y\n");
+#endif
+
+ rc=dosignkey(dump_func, cmdstr, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc2)
+ rc=rc2;
+ }
+ return (rc);
+}
+
+static int dosignkey(int (*dump_func)(const char *, size_t, void *),
+ const char *cmdstr,
+ void *voidarg)
+{
+ int rc=libmail_gpg_write( cmdstr, strlen(cmdstr),
+ dump_func, NULL, NULL, 0, voidarg);
+ int rc2;
+
+ if (rc == 0)
+ rc=libmail_gpg_read(dump_func, NULL, NULL, 0, voidarg);
+ rc2=libmail_gpg_cleanup();
+ if (rc == 0)
+ rc=rc2;
+ return (rc);
+}
diff --git a/gpglib/tempname.c b/gpglib/tempname.c
new file mode 100644
index 0000000..30a6faf
--- /dev/null
+++ b/gpglib/tempname.c
@@ -0,0 +1,77 @@
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+#include "tempname.h"
+
+static const char base64[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-,";
+
+int libmail_tempfile(char *filename_buf)
+{
+ unsigned long seed;
+ int i;
+ int fd;
+
+#if HAVE_GETTIMEOFDAY
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ seed=tv.tv_sec;
+ seed ^= tv.tv_usec << 16;
+#else
+ time_t t;
+
+ time(&t);
+ seed=t;
+#endif
+ seed ^= getpid();
+
+ for (i=0; i<1000; i++, seed += 5000)
+ {
+ char *p;
+ unsigned long n;
+
+ strcpy(filename_buf, "/tmp/mimegpg.");
+
+ p=filename_buf + strlen(filename_buf);
+
+ n=seed;
+ *p++=base64[ n % 64 ]; n /= 64;
+ *p++=base64[ n % 64 ]; n /= 64;
+ *p++=base64[ n % 64 ]; n /= 64;
+ *p++=base64[ n % 64 ]; n /= 64;
+ *p++=base64[ n % 64 ]; n /= 64;
+ *p++=base64[ n % 64 ];
+ *p=0;
+
+ if ((fd=open(filename_buf, O_RDWR | O_CREAT | O_EXCL, 0600))
+ >= 0)
+ return (fd);
+
+
+ if (errno != EEXIST)
+ break;
+ }
+ return (-1);
+}
diff --git a/gpglib/tempname.h b/gpglib/tempname.h
new file mode 100644
index 0000000..66b4bd4
--- /dev/null
+++ b/gpglib/tempname.h
@@ -0,0 +1,23 @@
+#ifndef tempname_h
+#define tempname_h
+/*
+** Copyright 2001-2003 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TEMPNAMEBUFSIZE 64
+
+int libmail_tempfile(char *);
+
+#ifdef __cplusplus
+} ;
+#endif
+
+#endif
diff --git a/gpglib/testgpg.c b/gpglib/testgpg.c
new file mode 100644
index 0000000..a08620b
--- /dev/null
+++ b/gpglib/testgpg.c
@@ -0,0 +1,184 @@
+/*
+** Copyright 2002-2006 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+
+#include "config.h"
+#include "gpglib.h"
+
+#include "unicode/unicode.h"
+#include "numlib/numlib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int dump_stdout(const char *p, size_t l, void *dummy)
+{
+ if (l == 0)
+ return 0;
+
+ if (fwrite(p, l, 1, stdout) != 1)
+ exit(1);
+ fflush(stdout);
+ return (0);
+}
+
+static int poll_wait(void *dummy)
+{
+ putchar('*');
+ fflush(stdout);
+ return (0);
+}
+
+static void genkey(const char *d)
+{
+ libmail_gpg_genkey(d, "iso-8859-1",
+ "John Smith",
+ "john@example.com",
+ "Dummy ISO-8859 Tëëst key",
+ 1024,
+ 2048,
+ 12,
+ 'm',
+ "",
+ dump_stdout,
+ poll_wait,
+ NULL);
+}
+
+static int show_key(const char *fingerprint, const char *shortname,
+ const char *key, int invalid,
+ struct gpg_list_info *dummy)
+{
+ printf("Fingerprint: %s\nShort: %s\n%s\n\n",
+ fingerprint,
+ shortname,
+ key);
+ fflush(stdout);
+ return (0);
+}
+
+static void genlist(const char *d, int flag)
+{
+ struct gpg_list_info gli;
+
+ memset(&gli, 0, sizeof(gli));
+
+ libmail_gpg_listkeys(d, flag, show_key, dump_stdout, &gli);
+}
+
+static int expkey(const char *d, const char *f, int flag)
+{
+ return (libmail_gpg_exportkey(d, flag, f, dump_stdout, dump_stdout, NULL));
+}
+
+static int delkey(const char *d, const char *f, int flag)
+{
+ return (libmail_gpg_deletekey(d, flag, f, dump_stdout, NULL));
+}
+
+static int signkey(const char *d, const char *signthis, const char *signwith)
+{
+ return (libmail_gpg_signkey(d, signthis, signwith, -1, dump_stdout, 0, NULL));
+}
+
+static int checksign(const char *d, const char *stuff, const char *sig)
+{
+ return (libmail_gpg_checksign(d, stuff, sig, dump_stdout, NULL));
+}
+
+static int import(const char *filename)
+{
+ FILE *f=fopen(filename, "r");
+ int rc;
+
+ if (!f)
+ {
+ perror(filename);
+ return (1);
+ }
+
+ if ((rc=libmail_gpg_import_start(NULL, 1)) == 0)
+ {
+ char buf[BUFSIZ];
+ int n;
+
+ while ((n=fread(buf, 1, sizeof(buf), f)) > 0)
+ {
+ if ((rc=libmail_gpg_import_do(buf, n,
+ dump_stdout, NULL)) != 0)
+ return (rc);
+ }
+
+ rc=libmail_gpg_import_finish(dump_stdout, NULL);
+ }
+ return (rc);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 3)
+ return (0);
+
+ if (strcmp(argv[2], "gen") == 0)
+ {
+ genkey(argv[1]);
+ return (0);
+ }
+
+ if (strcmp(argv[2], "listpub") == 0)
+ {
+ genlist(argv[1], 0);
+ return (0);
+ }
+
+ if (strcmp(argv[2], "listsec") == 0)
+ {
+ genlist(argv[1], 1);
+ return (0);
+ }
+
+ if (strcmp(argv[2], "exppub") == 0 && argc >= 4)
+ {
+ exit(expkey(argv[1], argv[3], 0));
+ return (0);
+ }
+
+ if (strcmp(argv[2], "expsec") == 0 && argc >= 4)
+ {
+ exit(expkey(argv[1], argv[3], 1));
+ return (0);
+ }
+
+ if (strcmp(argv[2], "delpub") == 0 && argc >= 4)
+ {
+ exit (delkey(argv[1], argv[3], 0));
+ return (0);
+ }
+
+ if (strcmp(argv[2], "delsec") == 0 && argc >= 4)
+ {
+ exit (delkey(argv[1], argv[3], 1));
+ return (0);
+ }
+
+ if (strcmp(argv[2], "sign") == 0 && argc >= 5)
+ {
+ exit(signkey(argv[1], argv[3], argv[4]));
+ return (0);
+ }
+
+ if (strcmp(argv[2], "checksign") == 0 && argc >= 5)
+ {
+ exit(checksign(argv[1], argv[3], argv[4]));
+ return (0);
+ }
+
+ if (strcmp(argv[2], "import") == 0)
+ exit(import(argv[1]));
+
+ return (0);
+}
diff --git a/gpglib/webgpg.in b/gpglib/webgpg.in
new file mode 100644
index 0000000..c2cc962
--- /dev/null
+++ b/gpglib/webgpg.in
@@ -0,0 +1,39 @@
+#! @SHELL@
+#
+#
+# Copyright 2001 Double Precision, Inc. See COPYING for
+# distribution information.
+#
+# This scripts initializes Maildir/gpg, enabling GPG code for the given
+# maildir.
+
+GPG=@GPG@
+
+maildir="$1"
+
+# Sanity check
+
+test ! -d "$maildir" && echo "$maildir: not a directory" >&2 && exit 1
+
+umask 077
+test -d "$maildir/gpg" && echo "$maildir/gpg: already exists" >&2 && exit 1
+test -f "$maildir/gpg/options" && echo "$maildir/gpg/options: already exists" >&2 && exit 1
+
+mkdir $maildir/gpg || exit 1
+cat >$maildir/gpg/options <<EOF
+
+force-v3-sigs
+escape-from-lines
+lock-once
+comment ""
+no-secmem-warning
+
+EOF
+
+owner=`@PERL@ -e '@a=stat "'$maildir'"; print "$a[4]:$a[5]";'`
+
+chown -R $owner $maildir/gpg
+
+echo "$maildir/gpg/options created, please verify that the options are correct."
+
+echo "See INSTALL for more information."