CVS update: /ccvs/src/

[email protected] 3 Jun 2005 18:26:12 -0000
Newsgroups gmane.comp.version-control.cvs.cvs
Message-ID <[email protected]>
User: dprice  
Date: 05/06/03 11:26:12

Modified:
 /ccvs/src/
  ChangeLog, client.c, main.c, parseinfo.c, parseinfo.h, sanity.sh, server.c,
  zlib.c

Log:
 * client.c (force_gzip): New static global.
 (handle_force_gzip): New function.
 (responses): Add `Force-gzip'.
 (start_server): Turn on encryption and compression before potentially
 sending other rooted requests.  Turn on compression when requested by
 the user or the server.
 * main.c (opt_usage):  Note that -z<level> *requests* compression
 <level> from the server.
 * parseinfo.c (new_config):  Initialize MaxCompressionLevel.
 (parse_config): Parse MinCompressionLevel & MaxCompressionLevel.
 * parseinfo.h (struct config): Add MinCompressionLevel &
 MaxCompressionLevel.
 * server.c (pending_warning_text):  New static global.
 (print_pending_error): Print pending warnings too.
 (warning_pending): New macro.
 (alloc_pending_internal): New function with much content...
 (alloc_pending): ...previously from here.
 (alloc_pending_warning): New function.
 (server_root, serve_gzip_contents, gzip_stream): Force gzip_level into
 configured restrictions.
 (serve_command_prep): Print pending errors.
 (requests): Make `Gzip-stream', `gzip-file-contents',
 `Kerberos-encrypt', `Gssapi-encrypt', & `Gssapi-authenticate' requests
 rootless to allow them before compression starts.
 (serve_valid_requests): Send `Force-gzip' response when needed.
 (server): Abort if a rootless compression request forced compression
 outside restricted levels.
 * zlib.c (struct compress_buffer, compress_buffer_initialize): Store
 compression level.
 (compress_buffer_output): Reset compression level when global
 gzip_level has changed.
 * sanity.sh (config2): New tests for compression restrictions.

File Changes:

Directory: /ccvs/src/
=====================

File [changed]: ChangeLog
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/ChangeLog?r1=1.3210&r2=1.3211
Delta lines:  +35 -0
--------------------
--- ChangeLog	3 Jun 2005 16:27:21 -0000	1.3210
+++ ChangeLog	3 Jun 2005 18:26:08 -0000	1.3211
@@ -1,5 +1,40 @@
 2005-06-03  Derek Price  <[email protected]>
 
+	* client.c (force_gzip): New static global.
+	(handle_force_gzip): New function.
+	(responses): Add `Force-gzip'.
+	(start_server): Turn on encryption and compression before potentially
+	sending other rooted requests.  Turn on compression when requested by
+	the user or the server.
+	* main.c (opt_usage):  Note that -z<level> *requests* compression
+	<level> from the server.
+	* parseinfo.c (new_config):  Initialize MaxCompressionLevel.
+	(parse_config): Parse MinCompressionLevel & MaxCompressionLevel.
+	* parseinfo.h (struct config): Add MinCompressionLevel &
+	MaxCompressionLevel.
+	* server.c (pending_warning_text):  New static global.
+	(print_pending_error): Print pending warnings too.
+	(warning_pending): New macro.
+	(alloc_pending_internal): New function with much content...
+	(alloc_pending): ...previously from here.
+	(alloc_pending_warning): New function.
+	(server_root, serve_gzip_contents, gzip_stream): Force gzip_level into
+	configured restrictions.
+	(serve_command_prep): Print pending errors.
+	(requests): Make `Gzip-stream', `gzip-file-contents',
+	`Kerberos-encrypt', `Gssapi-encrypt', & `Gssapi-authenticate' requests
+	rootless to allow them before compression starts.
+	(serve_valid_requests): Send `Force-gzip' response when needed.
+	(server): Abort if a rootless compression request forced compression
+	outside restricted levels.
+	* zlib.c (struct compress_buffer, compress_buffer_initialize): Store
+	compression level.
+	(compress_buffer_output): Reset compression level when global
+	gzip_level has changed.
+	* sanity.sh (config2): New tests for compression restrictions.
+
+2005-06-03  Derek Price  <[email protected]>
+
 	* zlib.c (compress_buffer_input): Update comment.
 
 2005-06-03  Derek Price  <[email protected]>

File [changed]: client.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/client.c?r1=1.427&r2=1.428
Delta lines:  +148 -126
-----------------------
--- client.c	3 Jun 2005 13:14:41 -0000	1.427
+++ client.c	3 Jun 2005 18:26:08 -0000	1.428
@@ -444,6 +444,9 @@
 
 #ifdef CLIENT_SUPPORT
 
+/* Whether the server asked us to force compression.  */
+static bool force_gzip;
+
 /*
  * The Repository for the top level of this command (not necessarily
  * the CVSROOT, just the current directory at the time we do it).
@@ -549,6 +552,12 @@
     }
 }
 
+static void
+handle_force_gzip (char *args, size_t len)
+{
+    force_gzip = true;
+}
+
 
 
 /* Has the server told us its name since the last redirect?
@@ -3046,6 +3055,8 @@
     RSP_LINE("error", handle_error, response_type_error, rs_essential),
     RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
        rs_essential),
+    RSP_LINE("Force-gzip", handle_force_gzip, response_type_normal,
+       rs_optional),
     RSP_LINE("Referrer", handle_referrer, response_type_normal, rs_optional),
     RSP_LINE("Redirect", handle_redirect, response_type_redirect, rs_optional),
     RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
@@ -3967,6 +3978,7 @@
 {
     bool rootless;
     int status;
+    bool have_global;
 
     do
     {
@@ -4032,6 +4044,119 @@
 	if (get_server_responses ())
 	    exit (EXIT_FAILURE);
 
+	have_global = supported_request ("Global_option");
+
+	/* Encryption needs to come before compression.  Good encryption can
+	 * render compression useless in the other direction.
+	 */
+	if (cvsencrypt && !rootless)
+	{
+#ifdef ENCRYPTION
+	    /* Turn on encryption before turning on compression.  We do
+	     * not want to try to compress the encrypted stream.  Instead,
+	     * we want to encrypt the compressed stream.  If we can't turn
+	     * on encryption, bomb out; don't let the user think the data
+	     * is being encrypted when it is not.
+	     */
+#  ifdef HAVE_KERBEROS
+	    if (current_parsed_root->method == kserver_method)
+	    {
+		if (!supported_request ("Kerberos-encrypt"))
+		    error (1, 0, "This server does not support encryption");
+		send_to_server ("Kerberos-encrypt\012", 0);
+	       initialize_kerberos4_encryption_buffers (&global_to_server,
+							&global_from_server);
+	    }
+	    else
+#  endif /* HAVE_KERBEROS */
+#  ifdef HAVE_GSSAPI
+	    if (current_parsed_root->method == gserver_method)
+	    {
+		if (!supported_request ("Gssapi-encrypt"))
+		    error (1, 0, "This server does not support encryption");
+		send_to_server ("Gssapi-encrypt\012", 0);
+		initialize_gssapi_buffers (&global_to_server,
+					   &global_from_server);
+		cvs_gssapi_encrypt = 1;
+	    }
+	    else
+#  endif /* HAVE_GSSAPI */
+		error (1, 0,
+"Encryption is only supported when using GSSAPI or Kerberos");
+#else /* ! ENCRYPTION */
+	    error (1, 0, "This client does not support encryption");
+#endif /* ! ENCRYPTION */
+	}
+
+	/* Send this before compression to enable supression of the
+	 * "Forcing compression level Z" messages.
+	 */
+	if (quiet)
+	{
+	    if (have_global)
+	    {
+		send_to_server ("Global_option -q\012", 0);
+	    }
+	    else
+		error (1, 0,
+		       "This server does not support the global -q option.");
+	}
+	if (really_quiet)
+	{
+	    if (have_global)
+	    {
+		send_to_server ("Global_option -Q\012", 0);
+	    }
+	    else
+		error (1, 0,
+		       "This server does not support the global -Q option.");
+	}
+
+	/* Compression needs to come before any of the rooted requests to
+	 * work with compression limits.
+	 */
+	if (!rootless && (gzip_level || force_gzip))
+	{
+	    if (supported_request ("Gzip-stream"))
+	    {
+		char *gzip_level_buf = Xasprintf ("%d", gzip_level);
+		send_to_server ("Gzip-stream ", 0);
+		send_to_server (gzip_level_buf, 0);
+		free (gzip_level_buf);
+		send_to_server ("\012", 1);
+
+		/* All further communication with the server will be
+		   compressed.  */
+
+		global_to_server =
+		    compress_buffer_initialize (global_to_server, 0,
+					        gzip_level, NULL);
+		global_from_server =
+		    compress_buffer_initialize (global_from_server, 1,
+						gzip_level, NULL);
+	    }
+#ifndef NO_CLIENT_GZIP_PROCESS
+	    else if (supported_request ("gzip-file-contents"))
+	    {
+		char *gzip_level_buf = Xasprintf ("%d", gzip_level);
+		send_to_server ("gzip-file-contents ", 0);
+		send_to_server (gzip_level_buf, 0);
+		free (gzip_level_buf);
+		send_to_server ("\012", 1);
+
+		file_gzip_level = gzip_level;
+	    }
+#endif
+	    else
+	    {
+		fprintf (stderr, "server doesn't support gzip-file-contents\n");
+		/* Setting gzip_level to 0 prevents us from giving the
+		   error twice if update has to contact the server again
+		   to fetch unpatchable files.  */
+		gzip_level = 0;
+	    }
+	}
+
 	if (client_referrer && supported_request ("Referrer"))
 	{
 	    send_to_server ("Referrer ", 0);
@@ -4067,10 +4192,6 @@
      *
      * -l -t -r -w -q -n and -Q need to go to the server.
      */
-
-    {
-	bool have_global = supported_request ("Global_option");
-
 	if (noexec)
 	{
 	    if (have_global)
@@ -4081,26 +4202,6 @@
 		error (1, 0,
 		       "This server does not support the global -n option.");
 	}
-	if (quiet)
-	{
-	    if (have_global)
-	    {
-		send_to_server ("Global_option -q\012", 0);
-	    }
-	    else
-		error (1, 0,
-		       "This server does not support the global -q option.");
-	}
-	if (really_quiet)
-	{
-	    if (have_global)
-	    {
-		send_to_server ("Global_option -Q\012", 0);
-	    }
-	    else
-		error (1, 0,
-		       "This server does not support the global -Q option.");
-	}
 	if (!cvswrite)
 	{
 	    if (have_global)
@@ -4122,7 +4223,6 @@
 		error (1, 0,
 		       "This server does not support the global -t option.");
 	}
-    }
 
     /* Find out about server-side cvswrappers.  An extra network
        turnaround for cvs import seems to be unavoidable, unless we
@@ -4144,84 +4244,6 @@
 	}
     }
 
-    if (cvsencrypt && !rootless)
-    {
-#ifdef ENCRYPTION
-	/* Turn on encryption before turning on compression.  We do
-           not want to try to compress the encrypted stream.  Instead,
-           we want to encrypt the compressed stream.  If we can't turn
-           on encryption, bomb out; don't let the user think the data
-           is being encrypted when it is not.  */
-#ifdef HAVE_KERBEROS
-	if (current_parsed_root->method == kserver_method)
-	{
-	    if (! supported_request ("Kerberos-encrypt"))
-		error (1, 0, "This server does not support encryption");
-	    send_to_server ("Kerberos-encrypt\012", 0);
-           initialize_kerberos4_encryption_buffers (&global_to_server,
-                                                    &global_from_server);
-	}
-	else
-#endif /* HAVE_KERBEROS */
-#ifdef HAVE_GSSAPI
-	if (current_parsed_root->method == gserver_method)
-	{
-	    if (! supported_request ("Gssapi-encrypt"))
-		error (1, 0, "This server does not support encryption");
-	    send_to_server ("Gssapi-encrypt\012", 0);
-	    initialize_gssapi_buffers(&global_to_server, &global_from_server);
-	    cvs_gssapi_encrypt = 1;
-	}
-	else
-#endif /* HAVE_GSSAPI */
-	    error (1, 0,
-"Encryption is only supported when using GSSAPI or Kerberos");
-#else /* ! ENCRYPTION */
-	error (1, 0, "This client does not support encryption");
-#endif /* ! ENCRYPTION */
-    }
-
-    if (gzip_level && !rootless)
-    {
-	if (supported_request ("Gzip-stream"))
-	{
-	    char *gzip_level_buf = Xasprintf ("%d", gzip_level);
-	    send_to_server ("Gzip-stream ", 0);
-	    send_to_server (gzip_level_buf, 0);
-	    free (gzip_level_buf);
-	    send_to_server ("\012", 1);
-
-	    /* All further communication with the server will be
-               compressed.  */
-
-	    global_to_server = compress_buffer_initialize (global_to_server, 0,
-                                                           gzip_level, NULL);
-	    global_from_server = compress_buffer_initialize (global_from_server,
-                                                             1, gzip_level,
-							     NULL);
-	}
-#ifndef NO_CLIENT_GZIP_PROCESS
-	else if (supported_request ("gzip-file-contents"))
-	{
-            char *gzip_level_buf = Xasprintf ("%d", gzip_level);
-	    send_to_server ("gzip-file-contents ", 0);
-	    send_to_server (gzip_level_buf, 0);
-	    free (gzip_level_buf);
-	    send_to_server ("\012", 1);
-
-	    file_gzip_level = gzip_level;
-	}
-#endif
-	else
-	{
-	    fprintf (stderr, "server doesn't support gzip-file-contents\n");
-	    /* Setting gzip_level to 0 prevents us from giving the
-               error twice if update has to contact the server again
-               to fetch unpatchable files.  */
-	    gzip_level = 0;
-	}
-    }
-
     if (cvsauthenticate && ! cvsencrypt && !rootless)
     {
 	/* Turn on authentication after turning on compression, so

File [changed]: main.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/main.c?r1=1.244&r2=1.245
Delta lines:  +1 -1
-------------------
--- main.c	17 Mar 2005 17:15:19 -0000	1.244
+++ main.c	3 Jun 2005 18:26:08 -0000	1.245
@@ -258,7 +258,7 @@
     "    -d CVS_root  Overrides $CVSROOT as the root of the CVS tree.\n",
     "    -f           Do not use the ~/.cvsrc file.\n",
 #ifdef CLIENT_SUPPORT
-    "    -z #         Use compression level '#' for net traffic.\n",
+    "    -z #         Request compression level '#' for net traffic.\n",
 #ifdef ENCRYPTION
     "    -x           Encrypt all net traffic.\n",
 #endif

File [changed]: parseinfo.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/parseinfo.c?r1=1.77&r2=1.78
Delta lines:  +11 -0
--------------------
--- parseinfo.c	3 Jun 2005 15:36:35 -0000	1.77
+++ parseinfo.c	3 Jun 2005 18:26:09 -0000	1.78
@@ -290,6 +290,9 @@
     new->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
     new->UserAdminOptions = xstrdup ("k");
     new->MaxCommentLeaderLength = 20;
+#ifdef SERVER_SUPPORT
+    new->MaxCompressionLevel = 9;
+#endif /* SERVER_SUPPORT */
 #ifdef PROXY_SUPPORT
     new->MaxProxyBufferSize = (size_t)(8 * 1024 * 1024); /* 8 megabytes,
                                                           * by default.
@@ -584,6 +587,14 @@
 	else if (!strcmp (line, "UseArchiveCommentLeader"))
 	    readBool (infopath, "UseArchiveCommentLeader", p,
 		      &retval->UseArchiveCommentLeader);
+#ifdef SERVER_SUPPORT
+	else if (!strcmp (line, "MinCompressionLevel"))
+	    readSizeT (infopath, "MinCompressionLevel", p,
+		       &retval->MinCompressionLevel);
+	else if (!strcmp (line, "MaxCompressionLevel"))
+	    readSizeT (infopath, "MaxCompressionLevel", p,
+		       &retval->MaxCompressionLevel);
+#endif /* SERVER_SUPPORT */
 	else
 	    /* We may be dealing with a keyword which was added in a
 	       subsequent version of CVS.  In that case it is a good idea

File [changed]: parseinfo.h
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/parseinfo.h?r1=1.4&r2=1.5
Delta lines:  +4 -0
-------------------
--- parseinfo.h	11 May 2005 20:01:47 -0000	1.4
+++ parseinfo.h	3 Jun 2005 18:26:09 -0000	1.5
@@ -51,6 +51,10 @@
 #ifdef PROXY_SUPPORT
     size_t MaxProxyBufferSize;
 #endif /* PROXY_SUPPORT */
+#ifdef SERVER_SUPPORT
+    size_t MinCompressionLevel;
+    size_t MaxCompressionLevel;
+#endif /* SERVER_SUPPORT */
 #ifdef PRESERVE_PERMISSIONS_SUPPORT
     bool preserve_perms;
 #endif /* PRESERVE_PERMISSIONS_SUPPORT */

File [changed]: sanity.sh
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/sanity.sh?r1=1.1067&r2=1.1068
Delta lines:  +82 -0
--------------------
--- sanity.sh	2 Jun 2005 19:05:18 -0000	1.1067
+++ sanity.sh	3 Jun 2005 18:26:09 -0000	1.1068
@@ -18685,6 +18685,7 @@
 	  # postadmin: admin
 	  # postwatch: devcom3
 	  # config: config
+	  # config2: MinCompressionLevel and MaxCompressionLevel in config
 
 	  # On Windows, we can't check out CVSROOT, because the case
 	  # insensitivity means that this conflicts with cvsroot.
@@ -19902,6 +19903,87 @@
 
 
 
+	config2)
+	  # Tests of the CVSROOT/config file.  See the comment at the
+	  # "info" tests for a full list of administrative file tests.
+
+	  # No point in testing compression effects in local mode.
+          if $remote; then :; else
+            remoteonly config2
+	    continue
+	  fi
+
+	  # On Windows, we can't check out CVSROOT, because the case
+	  # insensitivity means that this conflicts with cvsroot.
+	  mkdir wnt
+	  cd wnt
+
+	  # Set MinCompressionLevel and MaxCompressionLevel in config.
+	  dotest config2-init-1 "$testcvs -q co CVSROOT" "U CVSROOT/$DOTSTAR"
+	  cd CVSROOT
+	  cat << EOF > config
+MinCompressionLevel=5
+MaxCompressionLevel=6
+EOF
+	  dotest config2-init-2 \
+"$testcvs -q ci -m set-compression-constraints" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+	  # Verify that the server reports forcing compression to an allowed
+	  # level.
+
+	  # Too high.
+	  dotest config2-1 "$testcvs -z9 update" \
+"$SPROG server: Forcing compression level 6 (allowed: 5 <= z <= 6)\.
+$SPROG update: Updating \."
+	  # Too low.
+	  dotest config2-2 "$testcvs -z1 update" \
+"$SPROG server: Forcing compression level 5 (allowed: 5 <= z <= 6)\.
+$SPROG update: Updating \."
+	  # From zero.
+	  dotest config2-3 "$testcvs update" \
+"$SPROG server: Forcing compression level 5 (allowed: 5 <= z <= 6)\.
+$SPROG update: Updating \."
+	  # Just right.
+	  dotest config2-3 "$testcvs -z5 update" \
+"$SPROG update: Updating \."
+
+	  # Check that compression may be forced to 0.
+	  cat << EOF > config
+MaxCompressionLevel=0
+EOF
+	  dotest config2-init-3 "$testcvs -q ci -m no-compression" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+
+	  # Too high.
+	  dotest config2-5 "$testcvs -z9 update" \
+"$SPROG server: Forcing compression level 0 (allowed: 0 <= z <= 0)\.
+$SPROG update: Updating \."
+	  # Just right.
+	  dotest config2-6 "$testcvs update" \
+"$SPROG update: Updating \."
+
+	  # And verify effect without restrictions.
+	  echo '# No config is a good config' > config
+	  dotest config2-init-4 "$testcvs -q ci -m change-to-comment" \
+"$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+$SPROG commit: Rebuilding administrative file database"
+	  dotest config2-7 "$testcvs update" \
+"$SPROG update: Updating \."
+
+	  dokeep
+	  restore_adm
+	  cd ../..
+	  rm -r wnt
+	  ;;
+
+
+
 	serverpatch)
 	  # Test remote CVS handling of unpatchable files.  This isn't
 	  # much of a test for local CVS.

File [changed]: server.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/server.c?r1=1.431&r2=1.432
Delta lines:  +169 -46
----------------------
--- server.c	26 May 2005 17:48:06 -0000	1.431
+++ server.c	3 Jun 2005 18:26:09 -0000	1.432
@@ -440,12 +440,34 @@
  * last line should not end with a newline.
  */
 static char *pending_error_text;
+static char *pending_warning_text;
 
 /* If an error is pending, print it and return 1.  If not, return 0.
+   Also prints pending warnings, but this does not affect the return value.
    Must be called only in contexts where it is OK to send output.  */
 static int
 print_pending_error (void)
 {
+    /* Check this case first since it usually means we are out of memory and
+     * the buffer output routines might try and allocate memory.
+     */
+    if (!pending_error_text && pending_error)
+    {
+	print_error (pending_error);
+	pending_error = 0;
+	return 1;
+    }
+
+    if (pending_warning_text)
+    {
+	buf_output0 (buf_to_net, pending_warning_text);
+	buf_append_char (buf_to_net, '\n');
+	buf_flush (buf_to_net, 0);
+
+	free (pending_warning_text);
+	pending_warning_text = NULL;
+    }
+
     if (pending_error_text)
     {
 	buf_output0 (buf_to_net, pending_error_text);
@@ -462,18 +484,29 @@
 	pending_error_text = NULL;
 	return 1;
     }
-    else if (pending_error)
-    {
-	print_error (pending_error);
-	pending_error = 0;
-	return 1;
-    }
-    else
+
 	return 0;
 }
 
+
+
 /* Is an error pending?  */
 # define error_pending() (pending_error || pending_error_text)
+# define warning_pending() (pending_warning_text)
+
+/* Allocate SIZE bytes for pending_error_text and return nonzero
+   if we could do it.  */
+static inline int
+alloc_pending_internal (char **dest, size_t size)
+{
+    *dest = malloc (size);
+    if (!*dest)
+    {
+	pending_error = ENOMEM;
+	return 0;
+    }
+    return 1;
+}
 
 
 
@@ -487,13 +520,20 @@
 	   this case.  But we might as well handle it if they don't, I
 	   guess.  */
 	return 0;
-    pending_error_text = xmalloc (size);
-    if (pending_error_text == NULL)
-    {
-	pending_error = ENOMEM;
+    return alloc_pending_internal (&pending_error_text, size);
+}
+
+
+
+/* Allocate SIZE bytes for pending_error_text and return nonzero
+   if we could do it.  */
+static int
+alloc_pending_warning (size_t size)
+{
+    if (warning_pending ())
+	/* Warnings can be lost here.  */
 	return 0;
-    }
-    return 1;
+    return alloc_pending_internal (&pending_warning_text, size);
 }
 
 
@@ -873,6 +913,35 @@
 # endif /* PROXY_SUPPORT */
 
 
+    /* Now that we have a config, verify our compression level.  Since 
+     * most clients do not send Gzip-stream requests until after the root
+     * request, wait until the first request following Root to verify that
+     * compression is being used when level 0 is not allowed.
+     */
+    if (gzip_level)
+    {
+	bool forced = false;
+
+	if (gzip_level < config->MinCompressionLevel)
+	{
+	    gzip_level = config->MinCompressionLevel;
+	    forced = true;
+	}
+
+	if (gzip_level > config->MaxCompressionLevel)
+	{
+	    gzip_level = config->MaxCompressionLevel;
+	    forced = true;
+	}
+
+	if (forced && !quiet
+	    && alloc_pending_warning (120 + strlen (program_name)))
+	    sprintf (pending_warning_text,
+"E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
+		     program_name, gzip_level, config->MinCompressionLevel,
+		     config->MaxCompressionLevel);
+    }
+
     path = xmalloc (strlen (current_parsed_root->directory)
 		   + sizeof (CVSROOTADM)
 		   + 2);
@@ -5379,6 +5448,7 @@
 serve_gzip_contents (char *arg)
 {
     int level;
+    bool forced = false;
 
 # ifdef PROXY_SUPPORT
     assert (!proxy_log);
@@ -5387,7 +5457,26 @@
     level = atoi (arg);
     if (level == 0)
 	level = 6;
-    file_gzip_level = level;
+
+    if (config && level < config->MinCompressionLevel)
+    {
+	level = config->MinCompressionLevel;
+	forced = true;
+    }
+    if (config && level > config->MaxCompressionLevel)
+    {
+	level = config->MaxCompressionLevel;
+	forced = true;
+    }
+
+    if (forced && !quiet
+	&& alloc_pending_warning (120 + strlen (program_name)))
+	sprintf (pending_warning_text,
+"E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
+		 program_name, level, config->MinCompressionLevel,
+		 config->MaxCompressionLevel);
+
+    gzip_level = file_gzip_level = level;
 }
 
 
@@ -5396,23 +5485,43 @@
 serve_gzip_stream (char *arg)
 {
     int level;
-
-    /* If we received this request before the `Root' request, the buffer index
-     * maintained to allow the write proxy to replace the `Root' request would
-     * be relative to the decompressed stream rather than the secondary log.
-     */
-    assert (current_parsed_root);
+    bool forced = false;
 
     level = atoi (arg);
-    if (level == 0)
-	level = 6;
 
-    /* All further communication with the client will be compressed.  */
+    if (config && level < config->MinCompressionLevel)
+    {
+	level = config->MinCompressionLevel;
+	forced = true;
+    }
+    if (config && level > config->MaxCompressionLevel)
+    {
+	level = config->MaxCompressionLevel;
+	forced = true;
+    }
+
+    if (forced && !quiet
+	&& alloc_pending_warning (120 + strlen (program_name)))
+	sprintf (pending_warning_text,
+"E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
+		 program_name, level, config->MinCompressionLevel,
+		 config->MaxCompressionLevel);
+	
+    gzip_level = level;
+
+    /* All further communication with the client will be compressed.
+     *
+     * The deflate buffers need to be initialized even for compression level
+     * 0, or the client will no longer be able to understand us.  At
+     * compression level 0, the correct compression headers will be created and
+     * sent, but data will thereafter simply be copied to the network buffers.
+     */
 
     /* This needs to be processed in both passes so that we may continue to
      * understand client requests on both the socket and from the log.
      */
-    buf_from_net = compress_buffer_initialize (buf_from_net, 1, level,
+    buf_from_net = compress_buffer_initialize (buf_from_net, 1,
+					       0 /* Not used. */,
 					       buf_from_net->memory_error);
 
     /* This needs to be skipped in subsequent passes to avoid compressing data
@@ -5618,7 +5727,7 @@
     bool ditch_log;
 # endif /* PROXY_SUPPORT */
 
-    if (error_pending ()) return;
+    if (print_pending_error ()) return;
 
     redirect_supported = supported_response ("Redirect");
     if (redirect_supported
@@ -5779,21 +5888,28 @@
   REQ_LINE("Argument", serve_argument, RQ_ESSENTIAL),
   REQ_LINE("Argumentx", serve_argumentx, RQ_ESSENTIAL),
   REQ_LINE("Global_option", serve_global_option, RQ_ROOTLESS),
-  REQ_LINE("Gzip-stream", serve_gzip_stream, 0),
+  /* This is rootless, even though the client/server spec does not specify
+   * such, to allow error messages to be understood by the client when they are
+   * sent.
+   */
+  REQ_LINE("Gzip-stream", serve_gzip_stream, RQ_ROOTLESS),
   REQ_LINE("wrapper-sendme-rcsOptions",
 	   serve_wrapper_sendme_rcs_options,
 	   0),
   REQ_LINE("Set", serve_set, RQ_ROOTLESS),
 #ifdef ENCRYPTION
+  /* These are rootless despite what the client/server spec says for the same
+   * reasons as Gzip-stream.
+   */
 #  ifdef HAVE_KERBEROS
-  REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, 0),
+  REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, RQ_ROOTLESS),
 #  endif
 #  ifdef HAVE_GSSAPI
-  REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, 0),
+  REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, RQ_ROOTLESS),
 #  endif
 #endif
 #ifdef HAVE_GSSAPI
-  REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, 0),
+  REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, RQ_ROOTLESS),
 #endif
   REQ_LINE("expand-modules", serve_expand_modules, 0),
   REQ_LINE("ci", serve_ci, RQ_ESSENTIAL),
@@ -5813,7 +5929,7 @@
   REQ_LINE("add", serve_add, 0),
   REQ_LINE("remove", serve_remove, 0),
   REQ_LINE("update-patches", serve_ignore, 0),
-  REQ_LINE("gzip-file-contents", serve_gzip_contents, 0),
+  REQ_LINE("gzip-file-contents", serve_gzip_contents, RQ_ROOTLESS),
   REQ_LINE("status", serve_status, 0),
   REQ_LINE("rdiff", serve_rdiff, 0),
   REQ_LINE("tag", serve_tag, 0),
@@ -5874,6 +5990,14 @@
 	    buf_output0 (buf_to_net, rq->name);
 	}
     }
+
+    if (config && config->MinCompressionLevel
+	&& supported_response ("Force-gzip"))
+    {
+	    buf_output0 (buf_to_net, "\n");
+	    buf_output0 (buf_to_net, "Force-gzip");
+    }
+
     buf_output0 (buf_to_net, "\nok\n");
 
     /* The client is waiting for the list of valid requests, so we
@@ -6368,27 +6492,26 @@
 		if (!(rq->flags & RQ_ROOTLESS)
 		    && current_parsed_root == NULL)
 		{
-		    /* For commands which change the way in which data
-		       is sent and received, for example Gzip-stream,
-		       this does the wrong thing.  Since the client
-		       assumes that everything is being compressed,
-		       unconditionally, there is no way to give this
-		       error to the client without turning on
-		       compression.  The obvious fix would be to make
-		       Gzip-stream RQ_ROOTLESS (with the corresponding
-		       change to the spec), and that might be a good
-		       idea but then again I can see some settings in
-		       CVSROOT about what compression level to allow.
-		       I suppose a more baroque answer would be to
-		       turn on compression (say, at level 1), just
-		       enough to give the "Root request missing"
-		       error.  For now we just lose.  */
 		    if (alloc_pending (80))
 			sprintf (pending_error_text,
 				 "E Protocol error: Root request missing");
 		}
 		else
+		{
+		    if (config && config->MinCompressionLevel && !gzip_level
+			&& !(rq->flags & RQ_ROOTLESS))
+		    {
+			/* This is a rootless request, a minimum compression
+			 * level has been configured, and no compression has
+			 * been requested by the client.
+			 */
+			if (alloc_pending (80 + strlen (program_name)))
+			    sprintf (pending_error_text,
+"E %s [server aborted]: Compression must be used with this server.",
+				     program_name);
+		    }
 		    (*rq->func) (cmd);
+		}
 		break;
 	    }
 	if (rq->name == NULL)

File [changed]: zlib.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/zlib.c?r1=1.30&r2=1.31
Delta lines:  +18 -1
--------------------
--- zlib.c	3 Jun 2005 16:27:21 -0000	1.30
+++ zlib.c	3 Jun 2005 18:26:09 -0000	1.31
@@ -46,8 +46,10 @@
 {
     /* The underlying buffer.  */
     struct buffer *buf;
+
     /* The compression information.  */
     z_stream zstr;
+    int level;
 };
 
 static void compress_error (int, int, z_stream *, const char *);
@@ -96,6 +98,7 @@
     memset (n, 0, sizeof *n);
 
     n->buf = buf;
+    n->level = level;
 
     if (input)
 	zstatus = inflateInit (&n->zstr);
@@ -264,7 +267,15 @@
 
 
 
-/* Output data to a compression buffer.  */
+extern int gzip_level;
+
+/* Output data to a compression buffer.
+ *
+ * GLOBALS
+ *   gzip_level		If GZIP_LEVEL has changed to a value different from
+ *			CLOSURE->level, then set the compression level on the
+ *			stream to the new value.
+ */
 static int
 compress_buffer_output (void *closure, const char *data, size_t have,
 			size_t *wrote)
@@ -278,6 +289,12 @@
     if (!buffer)
 	buffer = pagealign_xalloc (BUFFER_DATA_SIZE);
 
+    if (cb->level != gzip_level)
+    {
+	cb->level = gzip_level;
+	deflateParams (&cb->zstr, gzip_level, Z_DEFAULT_STRATEGY);
+    }
+
     cb->zstr.avail_in = have;
     cb->zstr.next_in = (unsigned char *) data;