diff --git a/src/ChangeLog b/src/ChangeLog
index 2ccf2d5a3991077c965fa7d031ea48fc7551ef9e..ce2f3f372909ed12d972c8e63289b8b3669c906a 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,23 @@
+2006-01-16  Derek Price  <derek@ximbiot.com>
+
+	* client.c (handle_openpgp_signatures): Rename to...
+	(handle_openpgp_signature): ...this and handle OpenPGP-signature
+	responses as documented.
+	(client_write_sigfile): Ditto on the response handling.
+	(responses): Rename OpenPGP-signatures to OpenPGP-signature.  Handle
+	new function name.
+	* rcs.c (iRCS_get_openpgp_signatures): New function factored from...
+	(RCS_get_openpgp_signatures): ...here.  Change API to return sigs
+	after base 64 decoding.
+	(RCS_has_openpgp_signatures): New function.
+	* rcs.h (RCS_get_openpgp_signatures): Update proto.
+	(RCS_has_openpgp_signatures): New proto.
+	* server.c (server_send_signatures): Handle raw signatures and match
+	response documentation.
+	(server_base_signatures): Use RCS_has_openpgp_signatures.
+	* verify.c (verify_fileproc): Handle raw signatures.
+	* vers_ts.c (Version_TS): Save RCSNode in input finfo.
+	
 2006-01-14  Derek Price  <derek@ximbiot.com>
 
 	* cvs.h [CVS_VERIFY_CHECKOUTS_ENV, CVS_SIGN_COMMITS_ENV]: New macros.
diff --git a/src/client.c b/src/client.c
index 00ac3e7a74c01f02db20ecafccf809f312f6ef2b..ed497a38ebe46261b8207dcb379b811ca54c74c8 100644
--- a/src/client.c
+++ b/src/client.c
@@ -24,7 +24,6 @@
 #include "client.h"
 
 /* GNULIB headers.  */
-#include "base64.h"
 #include "getline.h"
 #include "save-cwd.h"
 
@@ -33,6 +32,7 @@
 #include "buffer.h"
 #include "difflib.h"
 #include "edit.h"
+#include "gpg.h"
 #include "ignore.h"
 #include "recurse.h"
 #include "repos.h"
@@ -2202,24 +2202,23 @@ handle_rcs_diff (char *args, size_t len)
 
 
 /*
- * The OpenPGP-signatures response gives the signature for the file to be
+ * The OpenPGP-signature response gives the signature for the file to be
  * transmitted in the next Base-checkout or Temp-checkout response.
  */
-static char *stored_signatures;
-static size_t stored_signatures_len;
+static struct buffer *stored_signatures;
 static void
-handle_openpgp_signatures (char *args, size_t len)
+handle_openpgp_signature (char *args, size_t len)
 {
-    if (stored_signatures)
-        error (1, 0, "OpenPGP-signatures received before last one was used");
-
-    if (!base64_decode_alloc (args, len, &stored_signatures,
-			      &stored_signatures_len))
-	error (1, 0,
-	       "Bad signature received from server (base64 decode failed).");
+    int status;
 
     if (!stored_signatures)
-	error (1, errno, "Failed to allocate memory");
+	stored_signatures = buf_nonio_initialize (NULL);
+
+    status = next_signature (global_from_server, stored_signatures);
+    if (status == -2)
+	xalloc_die ();
+    else if (status)
+	error (1, 0, "Malformed signature received from server.");
 }
 
 
@@ -2228,6 +2227,7 @@ static void
 client_write_sigfile (const char *sigfile, bool writable)
 {
     FILE *e;
+    size_t want;
 
     if (!stored_signatures)
 	return;
@@ -2235,16 +2235,28 @@ client_write_sigfile (const char *sigfile, bool writable)
     if (!writable && isfile (sigfile))
 	xchmod (sigfile, true);
     e = xfopen (sigfile, FOPEN_BINARY_WRITE);
-    if (fwrite (stored_signatures, sizeof *stored_signatures,
-		stored_signatures_len, e) != stored_signatures_len)
-	error (1, errno, "cannot write signature file `%s'", sigfile);
+
+    want = buf_length (stored_signatures);
+    while (want > 0)
+    {
+	char *data;
+	size_t got;
+
+	buf_read_data (stored_signatures, want, &data, &got);
+
+	if (fwrite (data, sizeof *data, got, e) != got)
+	    error (1, errno, "cannot write signature file `%s'", sigfile);
+
+	want -= got;
+    }
+	
     if (fclose (e) == EOF)
 	error (0, errno, "cannot close signature file `%s'", sigfile);
 
     if (!writable)
 	xchmod (sigfile, false);
 
-    free (stored_signatures);
+    buf_free (stored_signatures);
     stored_signatures = NULL;
 }
 
@@ -2448,6 +2460,9 @@ client_base_signatures (void *data_arg, List *ent_list,
 	error (1, 0,
 	       "Server sent `Base-signatures' response without signature.");
 
+    if (stored_signatures && *clear)
+	error (1, 0, "Server sent unused signature data.");
+
     /* Read REV from the server.  */
     read_line (&rev);
 
@@ -3854,7 +3869,7 @@ struct response responses[] =
 	     rs_optional),
     RSP_LINE("Base-clear-signatures", handle_base_clear_signatures,
 	     response_type_normal, rs_optional),
-    RSP_LINE("OpenPGP-signatures", handle_openpgp_signatures,
+    RSP_LINE("OpenPGP-signature", handle_openpgp_signature,
 	     response_type_normal, rs_optional),
 
     /* Possibly should be response_type_error.  */
@@ -4790,7 +4805,7 @@ start_server (void)
 		    continue;
 		if (suppress_bases && !strncmp (rs->name, "Base-", 5))
 		    continue;
-		if (suppress_bases && !strcmp (rs->name, "OpenPGP-signatures"))
+		if (suppress_bases && !strcmp (rs->name, "OpenPGP-signature"))
 		    continue;
 		if (suppress_bases && !strcmp (rs->name, "Temp-checkout"))
 		    continue;
diff --git a/src/rcs.c b/src/rcs.c
index a24ae379c33b12dbc0069dfd421e0d40ae59625b..9062702ca7958ba0f8d2c6930a04e36c820800f2 100644
--- a/src/rcs.c
+++ b/src/rcs.c
@@ -4773,8 +4773,8 @@ workfile);
 
 
 
-const char *
-RCS_get_openpgp_signatures (RCSNode *rcs, const char *rev, size_t *len)
+static const char *
+iRCS_get_openpgp_signatures (RCSNode *rcs, const char *rev, size_t *len)
 {
     RCSVers *vers;
     Node *n;
@@ -4784,7 +4784,8 @@ RCS_get_openpgp_signatures (RCSNode *rcs, const char *rev, size_t *len)
 
     n = findnode (rcs->versions, rev);
     if (!n)
-	error (1, 0, "internal error: no revision information for %s", rev);
+	error (1, 0, "internal error: no revision information for r%s of %s",
+	       rev, rcs->print_path);
     vers = n->data;
 
     n = findnode (vers->other_delta, "openpgp-signatures");
@@ -4798,6 +4799,50 @@ RCS_get_openpgp_signatures (RCSNode *rcs, const char *rev, size_t *len)
 
 
 
+/* Returns false on error.  It is not an error if the requested revision has
+ * no OpenPGP signatures, but *OUT will be set to NULL.
+ */
+bool
+RCS_get_openpgp_signatures (struct file_info *finfo, const char *rev,
+			    char **out, size_t *len)
+{
+    const char *b64sig;
+    size_t b64len;
+
+    b64sig = iRCS_get_openpgp_signatures (finfo->rcs, rev, &b64len);
+
+    if (!b64sig)
+    {
+	*out = NULL;
+	*len = 0;
+	return true;
+    }
+
+    if (!base64_decode_alloc (b64sig, b64len, out, len))
+    {
+	error (0, 0, "Failed to decode base64 signature for `%s'",
+	       finfo->fullname);
+	return false;
+    }
+    else if (!*out)
+	xalloc_die ();
+
+    return true;
+}
+
+
+
+/* Return true if the specified revision has any OpenPGP signature data
+ * attached.
+ */
+bool
+RCS_has_openpgp_signatures (struct file_info *finfo, const char *rev)
+{
+    return iRCS_get_openpgp_signatures (finfo->rcs, rev, NULL);
+}
+
+
+
 void
 RCS_add_openpgp_signature (struct file_info *finfo, const char *rev)
 {
diff --git a/src/rcs.h b/src/rcs.h
index cba1964141cdf70106bdb0e05f810c08cc2dd8e9..c597d04cc7994866326a82138bce2672c0ca4a46 100644
--- a/src/rcs.h
+++ b/src/rcs.h
@@ -275,8 +275,9 @@ char *RCS_getexpand (RCSNode *);
 void RCS_setexpand (RCSNode *, const char *);
 int RCS_checkout (RCSNode *, const char *, const char *, const char *,
                   const char *, const char *, RCSCHECKOUTPROC, void *);
-const char *RCS_get_openpgp_signatures (RCSNode *rcs, const char *rev,
-					size_t *len);
+bool RCS_get_openpgp_signatures (struct file_info *finfo, const char *rev,
+				 char **out, size_t *len);
+bool RCS_has_openpgp_signatures (struct file_info *finfo, const char *rev);
 void RCS_add_openpgp_signature (struct file_info *finfo, const char *rev);
 int RCS_delete_openpgp_signatures (struct file_info *finfo, const char *rev,
 				   uint32_t keyid);
diff --git a/src/sanity.sh b/src/sanity.sh
index 327078c64441f90ff6744e8db620c5d2ca8b0639..8e1c3ae391759bc719f2f7cd0bc9cbfdf2ec3008 100755
--- a/src/sanity.sh
+++ b/src/sanity.sh
@@ -32361,7 +32361,7 @@ PrimaryServer=$PRIMARY_CVSROOT"
 	  mv $TESTDIR/save-root $PRIMARY_CVSROOT_DIRNAME
 
 	  dotest writeproxy-noredirect-5 "$CVS_SERVER server" \
-"Valid-requests Root Valid-responses valid-requests Command-prep Referrer Repository Directory Relative-directory Max-dotdot Static-directory Sticky Entry Kopt Checkin-time Modified Signature Is-modified UseUnchanged Unchanged Notify Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log rlog list rlist global-list-quiet ls add remove update-patches gzip-file-contents status rdiff tag rtag import admin export history release watch-on watch-off watch-add watch-remove watchers editors edit init annotate rannotate noop version
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer Repository Directory Relative-directory Max-dotdot Static-directory Sticky Entry Kopt Checkin-time Modified Signature Is-modified UseUnchanged Unchanged Notify Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log rlog list rlist global-list-quiet ls add remove update-patches gzip-file-contents sign status rdiff tag rtag import admin export history release watch-on watch-off watch-add watch-remove watchers editors edit init annotate rannotate noop version
 ok
 ok
 ok
@@ -32393,7 +32393,7 @@ EOF
 	  cd firstdir
 	  echo now you see me >file1
 	  dotest writeproxy-noredirect-6 "$CVS_SERVER server" \
-"Valid-requests Root Valid-responses valid-requests Command-prep Referrer Repository Directory Relative-directory Max-dotdot Static-directory Sticky Entry Kopt Checkin-time Modified Signature Is-modified UseUnchanged Unchanged Notify Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log rlog list rlist global-list-quiet ls add remove update-patches gzip-file-contents status rdiff tag rtag import admin export history release watch-on watch-off watch-add watch-remove watchers editors edit init annotate rannotate noop version
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer Repository Directory Relative-directory Max-dotdot Static-directory Sticky Entry Kopt Checkin-time Modified Signature Is-modified UseUnchanged Unchanged Notify Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log rlog list rlist global-list-quiet ls add remove update-patches gzip-file-contents sign status rdiff tag rtag import admin export history release watch-on watch-off watch-add watch-remove watchers editors edit init annotate rannotate noop version
 ok
 ok
 ok
@@ -32423,7 +32423,7 @@ EOF
 	  echo /file1/0/dummy+timestamp// >>CVS/Entries
 
 	  dotest writeproxy-noredirect-7 "$CVS_SERVER server" \
-"Valid-requests Root Valid-responses valid-requests Command-prep Referrer Repository Directory Relative-directory Max-dotdot Static-directory Sticky Entry Kopt Checkin-time Modified Signature Is-modified UseUnchanged Unchanged Notify Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log rlog list rlist global-list-quiet ls add remove update-patches gzip-file-contents status rdiff tag rtag import admin export history release watch-on watch-off watch-add watch-remove watchers editors edit init annotate rannotate noop version
+"Valid-requests Root Valid-responses valid-requests Command-prep Referrer Repository Directory Relative-directory Max-dotdot Static-directory Sticky Entry Kopt Checkin-time Modified Signature Is-modified UseUnchanged Unchanged Notify Hostname LocalDir Questionable Argument Argumentx Global_option Gzip-stream wrapper-sendme-rcsOptions Set ${DOTSTAR}expand-modules ci co update diff log rlog list rlist global-list-quiet ls add remove update-patches gzip-file-contents sign status rdiff tag rtag import admin export history release watch-on watch-off watch-add watch-remove watchers editors edit init annotate rannotate noop version
 ok
 ok
 Mode u=rw,g=rw,o=r
diff --git a/src/server.c b/src/server.c
index 7b09fa0c313c0e27c846acf5e77cedf5f603c4b5..aaa025df32f2c118dad26d9e2ca990c1235c72e1 100644
--- a/src/server.c
+++ b/src/server.c
@@ -8214,20 +8214,46 @@ cvs_output_tagged (const char *tag, const char *text)
 
 
 static void
-server_send_signatures (RCSNode *rcs, const char *rev)
+server_send_signatures (struct file_info *finfo, const char *rev)
 {
-    const char *signatures;
-    size_t siglen;
+    char *sig;
+    size_t len;
+    int status;
+    struct buffer *inbuf, *outbuf;
 
-    if (!supported_response ("OpenPGP-signatures"))
+    if (!supported_response ("OpenPGP-signature"))
 	return;
 
-    if (!(signatures = RCS_get_openpgp_signatures (rcs, rev, &siglen)))
+    if (!RCS_get_openpgp_signatures (finfo, rev, &sig, &len))
+	error (1, 0,
+	       "Corrupt signature in r%s of `%s' (base64 decode failed).",
+	       rev, finfo->fullname);
+
+    if (!sig)
+	/* No signature data available.  */
 	return;
 
-    buf_output0 (protocol, "OpenPGP-signatures ");
-    buf_output (protocol, signatures, siglen);
-    buf_output (protocol, "\n", 1);
+    /* Copy the sig into an input buffer.  */
+    inbuf = buf_nonio_initialize (NULL);
+    buf_output (inbuf, sig, len);
+    free (sig);
+
+    /* Create an output buffer.  */
+    outbuf = buf_nonio_initialize (NULL);
+
+    /* Read each signature, copying them to the net.  */
+    do
+    {
+	status = next_signature (inbuf, outbuf);
+	if (!status)
+	{
+	    buf_output0 (protocol, "OpenPGP-signature\n");
+	    buf_append_buffer (protocol, outbuf);
+	} else if (status == -2)
+	    xalloc_die ();
+	/* else status == EOF */
+    } while (!status);
+
     buf_send_counted (protocol);
 }
 
@@ -8277,7 +8303,7 @@ iserver_base_checkout (RCSNode *rcs, struct file_info *finfo, const char *prev,
 	 */
 	return;
 
-    server_send_signatures (rcs, rev);
+    server_send_signatures (finfo, rev);
 
     /* FIXME: It would be more efficient if diffs could be sent when the
      * revision numbers haven't changed but the keywords have.
@@ -8450,10 +8476,11 @@ server_base_signatures (struct file_info *finfo, const char *rev)
 	|| !supported_response ("Base-clear-signatures"))
 	return;
 
-    server_send_signatures (finfo->rcs, rev);
-
-    if (RCS_get_openpgp_signatures (finfo->rcs, rev, NULL))
+    if (RCS_has_openpgp_signatures (finfo, rev))
+    {
+	server_send_signatures (finfo, rev);
 	buf_output0 (protocol, "Base-signatures ");
+    }
     else
 	buf_output0 (protocol, "Base-clear-signatures ");
 
diff --git a/src/verify.c b/src/verify.c
index 7e77067ffe475a8b1ba55086ce4d1aab917df3fa..a504d2bafda1272f91df86f9652ec85c8690c533 100644
--- a/src/verify.c
+++ b/src/verify.c
@@ -30,7 +30,6 @@
 #include <string.h>
 
 /* GNULIB headers.  */
-#include "base64.h"
 #include "error.h"
 #include "xalloc.h"
 
@@ -553,25 +552,15 @@ verify_fileproc (void *callerdat, struct file_info *finfo)
     else
     {
 	/* In local mode, the signature data is still in the archive.  */
-
-	const char *b64sig;
-	size_t b64len;
-
 	assert (finfo->rcs);
-	if ((b64sig = RCS_get_openpgp_signatures (finfo->rcs, e->version,
-						  &b64len)))
+	if (!RCS_get_openpgp_signatures (finfo, e->version, &sigdata,
+					 &siglen))
 	{
-	    if (!base64_decode_alloc (b64sig, b64len, &sigdata, &siglen))
-	    {
-		error (0, 0, "Failed to decode base64 signature for `%s'",
-		       finfo->fullname);
-		errors = true;
-	    }
-	    else if (!b64sig)
-		xalloc_die ();
-	    /* else, got usable signature data in SIGDATA... fall out.  */
+	    error (0, 0, "Failed to decode base64 signature for `%s'",
+		   finfo->fullname);
+	    errors = true;
 	}
-	else
+	else if (!sigdata)
 	{
 	    error (0, 0, "No signature available for `%s'",
 		   finfo->fullname);
diff --git a/src/vers_ts.c b/src/vers_ts.c
index b9ebccee6ce4d98664982bbd557b885049d8c93c..02f5103f043dbc01ff8baf845f3fe5cf739dc7ad 100644
--- a/src/vers_ts.c
+++ b/src/vers_ts.c
@@ -34,6 +34,10 @@ static void time_stamp_server (const char *, Vers_TS *, Entnode *);
  *   set_time		If set, set the last modification time of the user file
  *			specified by FINFO to the checkin time of RET->vn_rcs.
  *
+ * OUTPUTS
+ *   finfo		FINFO->RCS will be updated if it was empty and RCS data
+ *			is found and parsed.
+ *
  * RETURNS
  *   Vers_TS structure for FINFO.
  */
@@ -195,6 +199,11 @@ Version_TS (struct file_info *finfo, const char *options, const char *tag,
     {
 	/* squirrel away the rcsdata pointer for others */
 	vers_ts->srcfile = rcsdata;
+	if (!finfo->rcs)
+	{
+	    rcsdata->refcount++;
+	    finfo->rcs = rcsdata;
+	}
 
 	if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
 	{