You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by pi...@apache.org on 2001/09/03 13:35:54 UTC

cvs commit: jakarta-tomcat-connectors/webapp/nsapi webapp.c

pier        01/09/03 04:35:54

  Added:       webapp/nsapi webapp.c
  Log:
  First implementation of an NSAPI module. BIG THANKS to Colin Wilson-Salt
  <colin AT london.virgin.net>.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-connectors/webapp/nsapi/webapp.c
  
  Index: webapp.c
  ===================================================================
  #include <base/pblock.h>
  #include <base/util.h>
  
  #include <wa.h>
  
  void wa_log(const char *f, const int l, const char *fmt, ...) { }
  
  /* exported functions */
  int init_webapp(pblock * pb, Session * sn, Request * rq);
  int nametrans_webapp(pblock * pb, Session * sn, Request * rq);
  int service_webapp(pblock * pb, Session * sn, Request * rq);
  int snoop(pblock * pb, Session * sn, Request * rq);
  
  /* private functions */
  void processConfigFile(pblock * pb, Session * sn, Request * rq, char * filename);
  void processLine(pblock * pb, Session * sn, Request * rq, char * line);
  void processDirective(pblock * pb, Session * sn, Request * rq, char * directive, char * arguments);
  void processWebAppInfoDirective(pblock * pb, Session * sn, Request * rq, char * arguments);
  void processWebAppConnectionDirective(pblock * pb, Session * sn, Request * rq, char * arguments);
  void processWebAppDeployDirective(pblock * pb, Session * sn, Request * rq, char * arguments);
  static const char * webAppInfo(pblock * pb, Session * sn, Request * rq, char * path);
  static const char * webAppConnection(pblock * pb, Session * sn, Request * rq, char * name, char * prov, char * p);
  static const char * webAppDeploy(pblock * pb, Session * sn, Request * rq, char * name, char * cnam, char * path);
  static int readPostData(pblock * pb, Session * sn, Request * rq, char * io_buf, int readsz, int * bytes);
  
  /* Whether the WebApp Library has been initialized or not */
  static wa_boolean wam_initialized = wa_false;
  
  /* The list of configured connections */
  static wa_chain *wam_connections = NULL;
  
  static wa_virtualhost * hackhost = NULL;
  
  typedef struct iplanet_stuff {
      pblock * pb;
      Session * sn;
      Request * rq;
  } iplanet_stuff;
  
  /* Log a message associated with a request */
  void wam_handler_log(wa_request * r, const char * f, const int l, char * msg) {
      char * fn = (char *) f;
      Session * sn = ((iplanet_stuff *) r->data)->sn;
      Request * rq = ((iplanet_stuff *) r->data)->rq;
  
      log_error(LOG_INFORM, fn, sn, rq, msg);
  }
  
  /* Set the HTTP status of the response. */
  void wam_handler_setstatus(wa_request * r, int status) {
      Session * sn = ((iplanet_stuff *) r->data)->sn;
      Request * rq = ((iplanet_stuff *) r->data)->rq;
  
      protocol_status(sn, rq, status, NULL);
  }
  
  /* Set the MIME Content-Type of the response. */
  void wam_handler_setctype(wa_request * r, char * type) {
      Request * rq = ((iplanet_stuff *) r->data)->rq;
  
      if (pblock_findval("content-type", rq->srvhdrs))
  	    param_free(pblock_remove("content-type", rq->srvhdrs));
  
      pblock_nvinsert("content-type", type, rq->srvhdrs);
  }
  
  /* Set a header in the HTTP response. */
  void wam_handler_setheader(wa_request * r, char * name, char * value) {
      Request * rq = ((iplanet_stuff *) r->data)->rq;
  
      pblock_nvinsert(name, value, rq->srvhdrs);
  }
  
  /* Commit the first part of the response (status and headers). */
  void wam_handler_commit(wa_request * r) {
      Session * sn = ((iplanet_stuff *) r->data)->sn;
      Request * rq = ((iplanet_stuff *) r->data)->rq;
  
      /* int responseStatus = */ protocol_start_response(sn, rq);
  
  /* don't know how to fix this...
   *     if (responseStatus == REQ_ABORTED)
   *     	return REQ_ABORTED;
   *     else if (responseStatus == REQ_NOACTION)
   * 	return REQ_PROCEED;
   */
  }
  
  /* Flush all data in the response buffer. */
  void wam_handler_flush(wa_request * r) {
      /* what do I do here? */
  }
  
  /* Read a chunk of text from the request body. */
  int wam_handler_read(wa_request * r, char * buf, int len) {
      Session * sn = ((iplanet_stuff *) r->data)->sn;
  
      int ch;
      int inbytes;
  
      for (inbytes = 0; inbytes < len; inbytes++) {
          ch = netbuf_getc(sn->inbuf);
          if (ch == IO_ERROR)
              return 0; /* failed */
  
          buf[inbytes] = (char) ch;
      }
  
      return 0; /* succeeded */
  }
  
  /* Write a chunk of text into the response body. */
  int wam_handler_write(wa_request * r, char * buf, int len) {
      Session * sn = ((iplanet_stuff *) r->data)->sn;
  
      /* int status = */ net_write(sn->csd, buf, len);
  /*
   *     if (status == IO_ERROR)
   *         return REQ_EXIT;
   */
      return 0;
  }
  
  /* The structure holding all callback handling functions for the library. */
  static wa_handler wam_handler = {
      wam_handler_log,
      wam_handler_setstatus,
      wam_handler_setctype,
      wam_handler_setheader,
      wam_handler_commit,
      wam_handler_flush,
      wam_handler_read,
      wam_handler_write,
  };
  
  /* Initialize the module and the WebApp Library. */
  static const char * wam_init(void) {
      const char * ret = NULL;
  
      if (wam_initialized == wa_true)
      	return NULL;
  
      if ((ret = wa_init()))
      	return ret;
  
      if ((ret = wa_cvirtualhost(&hackhost, "hack-host", 80)))
      	return ret;
  	
      wam_initialized = wa_true;
      
      return NULL;
  }
  
  static void wam_startup() {
      if (!wam_initialized)
      	return;
  
      wa_startup();
  }
  
  int verbose = 0;
  
  int init_webapp(pblock * pb, Session * sn, Request * rq) {
      char * fn = "init-webapp";
  
      char * config = pblock_findval("config", pb);
      char * verboseParam = pblock_findval("verbose", pb);
  
      if (!config) {
  	pblock_nvinsert("error", "missing parameter (config)", pb);
  	return REQ_ABORTED;
      }
  
      verbose = verboseParam && !strcmp(verboseParam, "true");
  
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq, "in init-webapp");
  	log_error(LOG_INFORM, fn, sn, rq, "config = '%s'", config);
      }
  
      processConfigFile(pb, sn, rq, config);
  
  /*
      if (!processConfigFile(config)) {
  	pblock_nvinsert("error", "error reading config", pb);
  	return REQ_ABORTED;
      }
  */
  
      wam_init( /* ... */ );
  
      wam_startup();
  
      return REQ_PROCEED;
  }
  
  #define MAX_LINE_LENGTH 8096
  
  void processConfigFile(pblock * pb, Session * sn, Request * rq,
      	    	       char * filename)
  {
      char * fn = "webapp-init <processConfigFile>";
  
      FILE * fp;
      char foo[MAX_LINE_LENGTH];
      char c;
  
      if (verbose)
      	log_error(LOG_INFORM, fn, sn, rq, "in <processConfigFile>");
  
      /* open the config file */
      if (!(fp = fopen(filename, "r"))) {
      	log_error(LOG_MISCONFIG, fn, sn, rq, "fopen failed on '%s'", filename);
  	return;
      }
  
      while (!feof(fp)) {
  	if ((c = fgetc(fp)) == EOF)
  	    break;
  
  	if (c == '#') {
  	    /* skip over comment lines */
  	    fgets(foo, MAX_LINE_LENGTH, fp);
  	} else if (c == '\r' || c == '\n') {
  	    /* eat up any newlines or carriage returns */
  	    continue;
  	} else {
  	    foo[0] = c;
  	    fgets(&foo[1], MAX_LINE_LENGTH - 1, fp);
      	    processLine(pb, sn, rq, foo);
  	}
      }
  
      fclose(fp);
  }
  
  void processLine(pblock * pb, Session * sn, Request * rq, char * line) {
      char * fn = "webapp-init <processLine>";
  
      char * foo;
      char * space;
      
      char * directive;
      char * arguments;
  
      if (verbose)
      	log_error(LOG_INFORM, fn, sn, rq, "in <processLine>, line = '%s'", line);
  
      /* eat up any trailing carriage returns or newlines */
      for (foo = line + strlen(line) - 1;
  	     *foo == '\r' || *foo == '\n';
  	     --foo)
      {
  	*foo = '\0';
      }
  
      directive = line;
  
      if ((space = strchr(line, ' '))) {
  	*space = '\0';
  	arguments = space + 1;
      } else
  	arguments = line + strlen(line); /* i.e. empty */
  
      processDirective(pb, sn, rq, directive, arguments);
  }
  
  void processDirective(pblock * pb, Session * sn, Request * rq,
      	    	      char * directive, char * arguments)
  {
      char * fn = "webapp-init <processConfigFile>";
  
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <processDirective>, directive = '%s', arguments = '%s'",
  		  directive, arguments ? arguments : "(null)");
      }
      
      if (!strcmp(directive, "WebAppInfo"))
      	processWebAppInfoDirective(pb, sn, rq, arguments);
      else if (!strcmp(directive, "WebAppConnection"))
      	processWebAppConnectionDirective(pb, sn, rq, arguments);
      else if (!strcmp(directive, "WebAppDeploy"))
      	processWebAppDeployDirective(pb, sn, rq, arguments);
      else {
      	log_error(LOG_MISCONFIG, fn, sn, rq,
  	    	  "Unrecognised directive: '%s'", directive);
      }
  }
  
  void processWebAppInfoDirective(pblock * pb, Session * sn, Request * rq,
      	    	    	    	char * arguments) {
      char * fn = "webapp-init <processWebAppInfoDirective>";
  
      char * uriPath;
  
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <processWebAppInfoDirective>, arguments = '%s'",
  		  arguments ? arguments : "(null)");
      }
      
      if (!arguments || !strlen(arguments))
      	return;
  
      uriPath = arguments;
  
      webAppInfo(pb, sn, rq, uriPath);
  }
  
  void processWebAppConnectionDirective(pblock * pb, Session * sn, Request * rq,
      	    	    	    	      char * arguments)
  {
      char * fn = "webapp-init <processWebAppConnectionDirective>";
  
      char * name;
      char * provider;
      char * optionalParameter;
      char * space;
      
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <processWebAppConnectionDirective>, arguments = '%s'",
  		  arguments ? arguments : "(null)");
      }
  
      if (!arguments || !strlen(arguments))
      	return;
  
      name = arguments;
      
      if (!(space = strchr(name, ' ')))
      	return;
      
      *space = '\0';
      provider = space + 1;
      
      if ((space = strchr(provider, ' '))) {
      	*space = '\0';
      	optionalParameter = space + 1;
      } else
      	optionalParameter = NULL;
      
      webAppConnection(pb, sn, rq, name, provider, optionalParameter);
  }
  
  void processWebAppDeployDirective(pblock * pb, Session * sn, Request * rq,
      	    	    	    	  char * arguments)
  {
      char * fn = "webapp-init <processWebAppDeployDirective>";
  
      char * name;
      char * connection;
      char * uriPath;
      char * space;
      
      const char * ret;
      
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <processWebAppDeployDirective>, arguments = '%s'",
  		  arguments ? arguments : "(null)");
      }
  
      if (!arguments || !strlen(arguments))
      	return;
  
      name = arguments;
      
      if (!(space = strchr(name, ' ')))
      	return;
      
      *space = '\0';
      connection = space + 1;
      
      if (!(space = strchr(connection, ' ')))
      	return;
      
      *space = '\0';
      uriPath = space + 1;
      
      if ((ret = webAppDeploy(pb, sn, rq, name, connection, uriPath)))
      	log_error(LOG_MISCONFIG, fn, sn, rq, "webAppDeploy reports: '%s'", ret);
  }
  
  /* Process the WebAppInfo directive */
  static const char * webAppInfo(pblock * pb, Session * sn, Request * rq,
      	    	    	       char * path)
  {
      char * fn = "webapp-init <webAppInfo>";
  
      const char * ret;
  
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <webAppInfo>, path = '%s'", path ? path : "(null)");
      }
  
      /* divert this call to a WebAppConnection and a WebAppDeploy call */
      if ((ret = webAppConnection(pb, sn, rq, "_INFO_", "info", "")))
          return ret;
  	
      if ((ret = webAppDeploy(pb, sn, rq, "_INFO_", "_INFO_", path)))
          return ret ;
  
      return NULL;
  }
  
  /* Process the WebAppConnection directive. */
  static const char * webAppConnection(pblock * pb, Session * sn, Request * rq,
      	    	    	    	     char * name, char * prov, char * p)
  {
      char * fn = "webapp-init <webAppConnection>";
  
      wa_connection * conn = NULL;
      const char * ret = NULL;
      wa_chain * elem = NULL;
  
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <webAppConnection>, name = '%s', prov = '%s', p = '%s'",
  		  name ? name : "(null)", prov ? prov : "null", p ? p : "null");
      }
  
      /* Initialize the library */
      if ((ret = wam_init( /* cmd->pool */ )))
      	return ret;
  
      /* Attempt to create a new wa_connection structure */
      if ((ret = wa_cconnection(&conn, name, prov, p)))
      	return ret;
  
      /* Check if we have a duplicate connection with this name */
      elem = wam_connections;
      while (elem) {
          wa_connection * curr = (wa_connection *) elem->curr;
          if (!strcasecmp(conn->name, curr->name))
              return "Duplicate connection name";
          elem = elem->next;
      }
  
      /* We don't have a duplicate connection, store it locally */
      elem = apr_palloc(wa_pool, sizeof(wa_chain));
      elem->curr = conn;
      elem->next = wam_connections;
      wam_connections = elem;
      
      return NULL;
  }
  
  /* Process the WebAppDeploy directive */
  static const char * webAppDeploy(pblock * pb, Session * sn, Request * rq,
      	    	    	    	 char * name, char * cnam, char * path)
  {
      char * fn = "webapp-init <webAppDeploy>";
  
      wa_virtualhost * host = NULL;
      wa_application * appl = NULL;
      wa_connection * conn = NULL;
      wa_chain * elem = NULL;
      const char * ret = NULL;
  
      if (verbose) {
      	log_error(LOG_INFORM, fn, sn, rq,
  	    	  "in <webAppConnection>, "
  		  "name = '%s', cnam = '%s', path = '%s'",
  		  name ? name : "(null)",
  		  cnam ? cnam : "null",
  		  path ? path : "null");
      }
  
      /* Initialize the library and retrieve/create the host structure */
      if ((ret = wam_init( /* cmd->pool */ )) /* ||
      	(ret = wam_server(cmd->server, &host)) */ )
      {
  	return ret;
      }
  
      host = hackhost; /* HACK HACK HACK */
  
      /* Retrieve the connection */
      elem = wam_connections;
      while (elem) {
          wa_connection * curr = (wa_connection *) elem->curr;
          if (!strcasecmp(curr->name, cnam)) {
              conn = curr;
              break;
          }
          elem = elem->next;
      }
      if (!conn)
      	return "Specified connection not configured";
  
      /* Create a new wa_application member */
      if ((ret = wa_capplication(&appl, name, path)))
      	return ret;
  
      appl->host = hackhost; /* HACK! HACK! HACK! */
  
      /* Deploy the web application */
      if ((ret = wa_deploy(appl, host, conn)))
      	return ret;
  
      /* Done */
      return NULL;
  }
  
  int nametrans_webapp(pblock * pb, Session * sn, Request * rq) {
      char * fn = "nametrans-webapp";
  
      wa_virtualhost * host = NULL;
      wa_application * appl = NULL;
      wa_chain * elem = NULL;
  
      char * uri = pblock_findval("uri", rq->reqpb);
  
      if (verbose)
      	log_error(LOG_INFORM, fn, sn, rq, "in nametrans-webapp");
  
      /* Paranoid check */
      if (!wam_initialized) {
      	log_error(LOG_MISCONFIG, fn, sn, rq, "!wam_initialized");
  	return REQ_ABORTED;
      }
  
      /* Check if this host was recognized */
  /*
   *  if (!(host = ap_get_module_config(r->server->module_config,
   *  	    	    	    	      &webapp_module)))
   *  {
   *     	return REQ_NOACTION;
   *  }
   */
      host = hackhost; /* HACK HACK HACK */
  
      /* Check if the uri is contained in one of our applications root path */
      elem = host->apps;
      while (elem) {
          appl = (wa_application *) elem->curr;
  	
  	if (verbose) {
  	    log_error(LOG_INFORM, fn, sn, rq,
  	    	      "comparing mapping '%s' to uri '%s'",
  		      appl->rpth, uri);
  	}
  	
          if (!strncmp(appl->rpth, uri, strlen(appl->rpth)))
  	    break;
  
          appl = NULL;
          elem = elem->next;
      }
  
      if (!appl)
      	return(REQ_NOACTION);
  
      if (verbose)
  	log_error(LOG_INFORM, fn, sn, rq, "changing name to webapp");
  
      if (pblock_findval("name", rq->vars))
  	param_free(pblock_remove("name", rq->vars));
  
      pblock_nvinsert("name", "webapp", rq->vars);
  
      return REQ_PROCEED;
  }
  
  int service_webapp(pblock * pb, Session * sn, Request * rq) {
      char * fn = "service-webapp";
  
      wa_virtualhost * host = NULL;
      wa_application * appl = NULL;
      wa_chain * elem = NULL;
  
      char * uri = pblock_findval("uri", rq->reqpb);
      wa_request * req = NULL;
      iplanet_stuff * stuff;
      const char * msg = NULL;
      char *stmp = NULL;
      char *ctmp = NULL;
      int ret = 0;
      char * np, * vp, * name, * value, * headers;
  
      if (verbose)
      	log_error(LOG_INFORM, fn, sn, rq, "in service-webapp");
  
      /* Paranoid check */
      if (!wam_initialized) {
      	log_error(LOG_MISCONFIG, fn, sn, rq, "!wam_initialized");
  	return REQ_ABORTED;
      }
  
      /* Check if this host was recognized */
  /*
   *  if (!(host = ap_get_module_config(r->server->module_config,
   *  	    	    	    	      &webapp_module)))
   *  {
   *     	return REQ_NOACTION;
   *  }
   */
      host = hackhost; /* HACK HACK HACK */
  
      /* Check if the uri is contained in one of our applications root path */
      elem = host->apps;
      while (elem) {
          appl = (wa_application *) elem->curr;
  	
  	if (verbose) {
  	    log_error(LOG_INFORM, fn, sn, rq,
  	    	      "comparing mapping '%s' to uri '%s'",
  		      appl->rpth, uri);
  	}
  	
          if (!strncmp(appl->rpth, uri, strlen(appl->rpth)))
  	    break;
  
          appl = NULL;
          elem = elem->next;
      }
  
      if (!appl) {
      	log_error(LOG_MISCONFIG, fn, sn, rq, "!appl");
      	return REQ_NOACTION;
      }
  
      if (!(stuff = (iplanet_stuff *)
      	  apr_palloc(wa_pool, sizeof(iplanet_stuff))))
      {
  	pblock_nvinsert("error", "Cannot allocate memory", pb);
  	return REQ_ABORTED;
      }
  
      stuff->pb = pb;
      stuff->sn = sn;
      stuff->rq = rq;
  
      /* Allocate the webapp request structure */
      if ((msg = wa_ralloc(&req, &wam_handler, stuff))) {
  	pblock_nvinsert("error", msg, pb);
  	return REQ_ABORTED;
      }
  
      /* Set up the WebApp Library request structure client and server host
         data (from the connection) */
      stmp = "hack-host";
      if (!stmp)
      	req->serv->host = "";
      else
      	req->serv->host = apr_pstrdup(req->pool, stmp);
  
      ctmp = pblock_findval("ip", sn->client);
      if (!ctmp)
      	req->clnt->host = "";
      else
      	req->clnt->host = apr_pstrdup(req->pool, ctmp);
  
      req->serv->addr = "0.0.0.0";
      req->clnt->addr = pblock_findval("ip", sn->client);
      req->serv->port = conf_getglobals()->Vport;
      req->clnt->port = 88; /* how do I know? */
      /* Set up all other members of the request structure */
      req->meth = apr_pstrdup(req->pool,  pblock_findval("method", rq->reqpb));
      req->ruri = apr_pstrdup(req->pool, pblock_findval("uri", rq->reqpb));
      req->args = apr_pstrdup(req->pool, pblock_findval("query", rq->reqpb));
      req->prot = apr_pstrdup(req->pool, pblock_findval("protocol", rq->reqpb));
      req->schm = apr_pstrdup(req->pool, security_active ? "https" : "http");
      req->user = apr_pstrdup(req->pool, pblock_findval("auth-user", rq->vars));
      req->auth = apr_pstrdup(req->pool, pblock_findval("auth-type", rq->vars));
      req->clen = 0;
      req->ctyp = "\0";
      req->rlen = 0;
  
      /* Copy headers into webapp request structure */
      np = headers = pblock_pblock2str(rq->headers, NULL);
      while (*np) {
  	if (!(vp = strchr(np, '='))) {
  	    FREE(headers);
  	    return REQ_ABORTED;
  	}
  
  	*vp = '\0';
  	name = np;
  
  	vp += 2;
  	np = strchr(vp, '"');
  	*np = '\0';
  	value = vp;
  
  	np++;
  	if (*np == ' ') {
  	    np++;
  	}
  
          apr_table_add(req->hdrs, name, value);
  
          if (!strcasecmp(name, "Content-Length"))
              req->clen = atol(value);
  
          if (!strcasecmp(value, "Content-Type"))
              req->ctyp = apr_pstrdup(req->pool, value);
      }
  
      /* Check if we can read something from the request */
  /*
   *   ret=ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK);
   *   if (ret!=OK) return(ret);
   */
  
      /* Invoke the request */
      ret = wa_rinvoke(req, appl);
  
      /* Destroy the request member */
      wa_rfree(req);
  /*
   *  ap_rflush(r);
   */
  
      return(REQ_PROCEED);
  }
  
  
  
  
  int snoop(pblock * pb, Session * sn, Request * rq);
  
  /* global constants */
  
  const char * header =
      "<html>\n"
      "<head>\n"
      "<title>Snoop Plugin</title>\n"
      "</head>\n"
      "\n"
      "<body bgcolor=#FFFFFF>\n"
      "<font face=\"Arial,Helvetica\">\n"
      "\n"
      "<h2>Snoop Plugin</h2>\n"
      "\n"
      "<p>\n"
      "This NSAPI plugin returns information about the HTTP request.\n"
      "<p>\n";
  
  const char * requestInformation =
      "<h3>\n"
      "Request information\n"
      "</h3>\n"
      "\n"
      "<b>Request Method:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Request URI:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Request Protocol:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Path Info:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Path Translated:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Query String:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Content Length:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Content Type:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Server Name:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Server Port:</b> <font face=\"Courier New, Courier, monospace\">%d</font><br>\n"
      "<b>Remote User:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Remote Address:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Remote Host:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n"
      "<b>Authorization Scheme:</b> <font face=\"Courier New, Courier, monospace\">%s</font>\n"
      "\n";
  
  const char * requestHeaders =
      "<h3>\n"
      "Request headers\n"
      "</h3>\n"
      "\n";
  
  const char * postData =
      "<h3>\n"
      "POST Data\n"
      "</h3>\n"
      "\n";
  
  const char * footer =
      "</font>\n"
      "</body>\n"
      "</html>\n";
  
  int snoop(pblock * pb, Session * sn, Request * rq) {
      char * fn = "snoop";
  
      char * verboseParam = pblock_findval("verbose", pb);
      int verbose = verboseParam && !strcmp(verboseParam, "true");
  
      int responseStatus;
  
      char buffer[8092];
  
      /* request information */
      
      char * requestMethod = pblock_findval("method", rq->reqpb);
      char * requestURI = pblock_findval("uri", rq->reqpb);
      char * protocol = pblock_findval("protocol", rq->reqpb);
      char * pathInfo = pblock_findval("path-info", rq->vars);
      char * pathTranslated = pblock_findval("path-translated", rq->vars);
      char * queryString = pblock_findval("query", rq->reqpb);
      char * contentLength = NULL;
      char * contentType = NULL;
      char * serverName = util_hostname();
      int serverPort = conf_getglobals()->Vport;
      char * authUser = pblock_findval("auth-user", rq->vars);
      char * remoteAddress = pblock_findval("ip", sn->client);
      char * remoteHost = session_dns(sn) ? session_dns(sn)
                                          : pblock_findval("ip", sn->client);
      char * authType = pblock_findval("auth-type", rq->vars);
  
      char * np, * vp, * name, * value, * headers;
  
      /* this is wasteful - I do it again when I print the headers out */
  
      np = headers = pblock_pblock2str(rq->headers, NULL);
      while (*np) {
  	if (!(vp = strchr(np, '='))) {
  	    FREE(headers);
  	    return REQ_ABORTED;
  	}
  
  	*vp = '\0';
  	name = np;
  
  	vp += 2;
  	np = strchr(vp, '"');
  	*np = '\0';
  	value = vp;
  
  	np++;
  	if (*np == ' ') {
  	    np++;
  	}
  
          if (!strcasecmp(name, "content-length"))
              contentLength = value;
          else if (!strcasecmp(name, "content-type"))
              contentType = value;
      }
  
      if (verbose) {
          log_error(LOG_INFORM, fn, sn, rq, "in snoop()");
      }
  
      if (pblock_findval("content-type", rq->srvhdrs))
          param_free(pblock_remove("content-type", rq->srvhdrs));
      
      pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
  
      protocol_status(sn, rq, PROTOCOL_OK, NULL);
      
      responseStatus = protocol_start_response(sn, rq);
  
      if (responseStatus == REQ_ABORTED)
          return REQ_ABORTED;
      else if (responseStatus == REQ_NOACTION)
          return REQ_PROCEED;
  
      if (net_write(sn->csd, (char *) header, strlen(header)) == IO_ERROR)
          return REQ_EXIT;
  
      util_sprintf(buffer, requestInformation,
                   requestMethod, requestURI, protocol,
                   pathInfo, pathTranslated, queryString, contentLength,
                   contentType, serverName, serverPort, authUser,
                   remoteAddress, remoteHost, authType);
  
      if (net_write(sn->csd, (char *) buffer, strlen(buffer)) == IO_ERROR)
          return REQ_EXIT;
  
      if (net_write(sn->csd,
                    (char *) requestHeaders,
                    strlen(requestHeaders)) == IO_ERROR)
      {
          return REQ_EXIT;
      }
  
      np = headers = pblock_pblock2str(rq->headers, NULL);
      while (*np) {
          if (!(vp = strchr(np, '='))) {
              FREE(headers);
              return REQ_ABORTED;
          }
  
          *vp = '\0';
          name = np;
  
          vp += 2;
          np = strchr(vp, '"');
          *np = '\0';
          value = vp;
  
          np ++;
          if (*np == ' ') {
              np++;
          }
  
      	/* need to html encode this stuff */
  
          sprintf(buffer, "<b>%s:</b> <font face=\"Courier New, Courier, monospace\">%s</font><br>\n", name, value);
  
          if (net_write(sn->csd, (char *) buffer, strlen(buffer)) == IO_ERROR)
              return REQ_EXIT;
      }
  
      if (!strcmp(requestMethod, "POST")) {
      	int bytesToRead = atoi(contentLength);
  	int bytesActuallyRead = 0;
  	char data[bytesToRead];
  	char * start = "<font face=\"Courier New, Courier, monospace\">";
  	char * end = "</font>";
  	
  	readPostData(pb, sn, rq, data, bytesToRead, &bytesActuallyRead);
  	
          if (net_write(sn->csd, (char *) postData, strlen(postData)) == IO_ERROR)
              return REQ_EXIT;
  
          if (net_write(sn->csd, start, strlen(start)) == IO_ERROR)
              return REQ_EXIT;
  
          if (net_write(sn->csd, data, bytesActuallyRead) == IO_ERROR)
              return REQ_EXIT;
  
          if (net_write(sn->csd, end, strlen(end)) == IO_ERROR)
              return REQ_EXIT;
      }
  
      if (net_write(sn->csd, (char *) footer, strlen(footer)) == IO_ERROR)
          return REQ_EXIT;
  
      return REQ_PROCEED;
  }
  
  static int readPostData(pblock * pb, Session * sn, Request * rq, 
                          char * io_buf, int readsz, int * bytes)
  {
      int ch;
      int inbytes;
  
      for (inbytes = 0; inbytes < readsz; inbytes++) {
          ch = netbuf_getc(sn->inbuf);
          if (ch == IO_ERROR) {
              protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
              return REQ_ABORTED;
          }
          io_buf[inbytes] = (char) ch;
      }
  
      *bytes = inbytes;
      return REQ_PROCEED;
  }