text: /* ========================================================================= *
   *                                                                           *
   *                 The Apache Software License,  Version 1.1                 *
   *                                                                           *
   *          Copyright (c) 1999-2001 The Apache Software Foundation.          *
   *                           All rights reserved.                            *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * Redistribution and use in source and binary forms,  with or without modi- *
   * fication, are permitted provided that the following conditions are met:   *
   *                                                                           *
   * 1. Redistributions of source code  must retain the above copyright notice *
   *    notice, this list of conditions and the following disclaimer.          *
   *                                                                           *
   * 2. Redistributions  in binary  form  must  reproduce the  above copyright *
   *    notice,  this list of conditions  and the following  disclaimer in the *
   *    documentation and/or other materials provided with the distribution.   *
   *                                                                           *
   * 3. The end-user documentation  included with the redistribution,  if any, *
   *    must include the following acknowlegement:                             *
   *                                                                           *
   *       "This product includes  software developed  by the Apache  Software *
   *        Foundation <http://www.apache.org/>."                              *
   *                                                                           *
   *    Alternately, this acknowlegement may appear in the software itself, if *
   *    and wherever such third-party acknowlegements normally appear.         *
   *                                                                           *
   * 4. The names  "The  Jakarta  Project",  "Jk",  and  "Apache  Software     *
   *    Foundation"  must not be used  to endorse or promote  products derived *
   *    from this  software without  prior  written  permission.  For  written *
   *    permission, please contact <apache@apache.org>.                        *
   *                                                                           *
   * 5. Products derived from this software may not be called "Apache" nor may *
   *    "Apache" appear in their names without prior written permission of the *
   *    Apache Software Foundation.                                            *
   *                                                                           *
   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES *
   * INCLUDING, BUT NOT LIMITED TO,  THE IMPLIED WARRANTIES OF MERCHANTABILITY *
   * AND FITNESS FOR  A PARTICULAR PURPOSE  ARE DISCLAIMED.  IN NO EVENT SHALL *
   * THE APACHE  SOFTWARE  FOUNDATION OR  ITS CONTRIBUTORS  BE LIABLE  FOR ANY *
   * DIRECT,  INDIRECT,   INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL *
   * DAMAGES (INCLUDING,  BUT NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE GOODS *
   * OR SERVICES;  LOSS OF USE,  DATA,  OR PROFITS;  OR BUSINESS INTERRUPTION) *
   * HOWEVER CAUSED AND  ON ANY  THEORY  OF  LIABILITY,  WHETHER IN  CONTRACT, *
   * STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
   * ANY  WAY  OUT OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF  ADVISED  OF THE *
   * POSSIBILITY OF SUCH DAMAGE.                                               *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * This software  consists of voluntary  contributions made  by many indivi- *
   * duals on behalf of the  Apache Software Foundation.  For more information *
   * on the Apache Software Foundation, please see <http://www.apache.org/>.   *
   *                                                                           *
   * ========================================================================= */
  
  /***************************************************************************
   * Description: NSAPI plugin for Netscape servers                          *
   * Author:      Gal Shachor <shachor@il.ibm.com>                           *
   * Version:     $Revision: 1.8 $                                           *
   ***************************************************************************/
  
  
  #include "jk_global.h"
  #include "jk_util.h"
  #include "jk_map.h"
  #include "jk_pool.h"
  #include "jk_service.h"
  #include "jk_worker.h"
  
  #include "nsapi.h"
  
  #define URI_PATTERN "path"
  #define DEFAULT_WORKER_NAME ("ajp13")
  
  struct nsapi_private_data {
      jk_pool_t p;
      
      int request_started;
  
      pblock *pb;
      Session *sn;
      Request *rq;
  };
  typedef struct nsapi_private_data nsapi_private_data_t;
  
  static int init_on_other_thread_is_done = JK_FALSE;
  static int init_on_other_thread_is_ok = JK_FALSE;
  
  static jk_logger_t *logger = NULL;
  static jk_worker_env_t	 worker_env;
  
  #ifdef NETWARE
  int (*PR_IsSocketSecure)(SYS_NETFD *csd); /* pointer to PR_IsSocketSecure function */
  #endif
  
  static int JK_METHOD start_response(jk_ws_service_t *s,
                                      int status,
                                      const char *reason,
                                      const char * const *header_names,
                                      const char * const *header_values,
                                      unsigned num_of_headers);
  
  static int JK_METHOD ws_read(jk_ws_service_t *s,
                               void *b,
                               unsigned l,
                               unsigned *a);
  
  static int JK_METHOD ws_write(jk_ws_service_t *s,
                                const void *b,
                                unsigned l);
  
  NSAPI_PUBLIC int jk_init(pblock *pb, 
                           Session *sn, 
                           Request *rq);
  
  NSAPI_PUBLIC void jk_term(void *p);
  
  NSAPI_PUBLIC int jk_service(pblock *pb,
                              Session *sn,
                              Request *rq);
  
  static int init_ws_service(nsapi_private_data_t *private_data,
                             jk_ws_service_t *s);
  
  static int setup_http_headers(nsapi_private_data_t *private_data,
                                jk_ws_service_t *s);
  
  static void init_workers_on_other_threads(void *init_d) 
  {
      jk_map_t *init_map = (jk_map_t *)init_d;
      /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
  	/* but where are they here in Netscape ? */
      if(wc_open(init_map, &worker_env, logger)) {
          init_on_other_thread_is_ok = JK_TRUE;
      } else {
          jk_log(logger, JK_LOG_EMERG, "In init_workers_on_other_threads, failed\n");
      }
  
      init_on_other_thread_is_done = JK_TRUE;
  }
  
  static int JK_METHOD start_response(jk_ws_service_t *s,
                                      int status,
                                      const char *reason,
                                      const char * const *header_names,
                                      const char * const *header_values,
                                      unsigned num_of_headers)
  {
      if(s && s->ws_private) {
          nsapi_private_data_t *p = s->ws_private;
          if(!p->request_started) {
              unsigned i;
  
              p->request_started = JK_TRUE;
  
              /* Remove "old" content type */
              param_free(pblock_remove("content-type", p->rq->srvhdrs));
  
              if(num_of_headers) {
                  for (i = 0 ; i < (int)num_of_headers ; i++) {
                      pblock_nvinsert(header_names[i],
                                      header_values[i],
                                      p->rq->srvhdrs);    
                  }
              } else {
                  pblock_nvinsert("content-type",
                                  "text/plain",
                                  p->rq->srvhdrs);    
              }
  
              protocol_status(p->sn,
                              p->rq,
                              status,
                              (char *)reason);
  
              protocol_start_response(p->sn, p->rq);
          }
          return JK_TRUE;
  
      }
      return JK_FALSE;
  }
  
  static int JK_METHOD ws_read(jk_ws_service_t *s,
                               void *b,
                               unsigned l,
                               unsigned *a)
  {
      if(s && s->ws_private && b && a) {
          nsapi_private_data_t *p = s->ws_private;
          
          *a = 0;
          if(l) {
              char *buf = b;
              unsigned i;
              netbuf *inbuf = p->sn->inbuf;
  
  /* Until we get a service pack for NW5.1 and earlier that has the latest */
  /* Enterprise Server, we have to go through the else version of this code*/
  #if defined(netbuf_getbytes) && !defined(NETWARE) 
              i = netbuf_getbytes(inbuf, b, l);
              if(NETBUF_EOF == i || NETBUF_ERROR == i) {
                  return JK_FALSE;
              }
  
  #else 
              int ch;
              for(i = 0 ; i < l ; i++) {
                  ch = netbuf_getc(inbuf);           
                  /*
                   * IO_EOF is 0 (zero) which is a very reasonable byte
                   * when it comes to binary data. So we are not breaking 
                   * out of the read loop when reading it.
                   *
                   * We are protected from an infinit loop by the Java part of
                   * Tomcat.
                   */
                  if(IO_ERROR == ch) {
                      break;
                  }
  
                  buf[i] = ch;
              }
  
              if(0 == i) {
                  return JK_FALSE;
              }
  #endif
              *a = i;
  
          }
          return JK_TRUE;
      }
      return JK_FALSE;
  }
  
  static int JK_METHOD ws_write(jk_ws_service_t *s,
                                const void *b,
                                unsigned l)
  {
      if(s && s->ws_private && b) {
          nsapi_private_data_t *p = s->ws_private;
  
          if(l) {
              if(!p->request_started) {
                  start_response(s, 200, NULL, NULL, NULL, 0);
              }
  
              if(net_write(p->sn->csd, (char *)b, (int)l) == IO_ERROR) {
                  return JK_FALSE;
              }
          }
  
          return JK_TRUE;
  
      }
      return JK_FALSE;
  }
  
  NSAPI_PUBLIC int jk_init(pblock *pb, 
                           Session *sn, 
                           Request *rq)
  {
      char *worker_prp_file = pblock_findval(JK_WORKER_FILE_TAG, pb); 
      char *log_level_str = pblock_findval(JK_LOG_LEVEL_TAG, pb); 
      char *log_file = pblock_findval(JK_LOG_FILE_TAG, pb); 
  
      int rc = REQ_ABORTED;
      jk_map_t *init_map;
  
      fprintf(stderr, "In jk_init.\n   Worker file = %s.\n   Log level = %s.\n   Log File = %s\n",worker_prp_file, log_level_str,  log_file);
      if(!worker_prp_file) {
          worker_prp_file = JK_WORKER_FILE_DEF;
      }
  
      if(!log_level_str) {
          log_level_str = JK_LOG_LEVEL_DEF;
      }    
      
      if(!jk_open_file_logger(&logger, log_file, 
                              jk_parse_log_level(log_level_str))) {
          logger = NULL;
      }
  
      if(map_alloc(&init_map)) {
          if(map_read_properties(init_map, worker_prp_file)) {
              int sleep_cnt;
              SYS_THREAD s;
              
              s = systhread_start(SYSTHREAD_DEFAULT_PRIORITY, 
                                  0, 
                                  init_workers_on_other_threads, 
                                  init_map);
              for(sleep_cnt = 0 ; sleep_cnt < 60 ; sleep_cnt++) {
                  systhread_sleep(1000);
                  jk_log(logger, JK_LOG_DEBUG, "jk_init, a second passed\n");
                  if(init_on_other_thread_is_done) {
                      break;
                  }
              }
  
              if(init_on_other_thread_is_done &&
                 init_on_other_thread_is_ok) {
                  magnus_atrestart(jk_term, NULL);
                  rc = REQ_PROCEED;
              }
  
  /*            if(wc_open(init_map, NULL, logger)) {
                  magnus_atrestart(jk_term, NULL);
                  rc = REQ_PROCEED;
              }
  */
          }
          
          map_free(&init_map);
      }
  
  #ifdef NETWARE
      PR_IsSocketSecure = (int (*)(void **))ImportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
  #endif
      return rc;    
  }
  
  NSAPI_PUBLIC void jk_term(void *p)
  {
  #ifdef NETWARE
      if (NULL != PR_IsSocketSecure)
      {
         UnimportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
         PR_IsSocketSecure = NULL;
      }
  #endif
      wc_close(logger);
      if(logger) {
          jk_close_file_logger(&logger);
      }
  }
  
  NSAPI_PUBLIC int jk_service(pblock *pb,
                              Session *sn,
                              Request *rq)
  {
      char *worker_name = pblock_findval(JK_WORKER_NAME_TAG, pb); 
      char *uri_pattern = pblock_findval(URI_PATTERN, pb);     
      jk_worker_t *worker;    
      int rc = REQ_ABORTED;
  
      if(uri_pattern) {
          char *uri = pblock_findval("uri", rq->reqpb);
  
          if(0 != shexp_match(uri, uri_pattern)) {
              return REQ_NOACTION;
          }        
      }
  
      if(!worker_name) {
          worker_name = DEFAULT_WORKER_NAME;
      }
  
      worker = wc_get_worker_for_name(worker_name, logger);
      if(worker) {
          nsapi_private_data_t private_data;
          jk_ws_service_t s;
          jk_pool_atom_t buf[SMALL_POOL_SIZE];
  
          jk_open_pool(&private_data.p, buf, sizeof(buf));
  
          private_data.request_started = JK_FALSE;
          private_data.pb = pb;
          private_data.sn = sn;
          private_data.rq = rq;
  
          jk_init_ws_service(&s);
  
          s.ws_private = &private_data;
          s.pool = &private_data.p;
  
          if(init_ws_service(&private_data, &s)) {
              jk_endpoint_t *e = NULL;
              if(worker->get_endpoint(worker, &e, logger)) {                
                  int recover = JK_FALSE;
                  if(e->service(e, &s, logger, &recover)) {
                      rc = REQ_PROCEED;
                  }
                  e->done(&e, logger);
              }
          }
          jk_close_pool(&private_data.p);
      }
  
      return rc;
  }
  
  static int init_ws_service(nsapi_private_data_t *private_data,
                             jk_ws_service_t *s) 
  {
      char *tmp;
      int rc;
  
      s->jvm_route = NULL;
      s->start_response = start_response;
      s->read = ws_read;
      s->write = ws_write;
      
      s->auth_type        = pblock_findval("auth-type", private_data->rq->vars);
      s->remote_user      = pblock_findval("auth-user", private_data->rq->vars);
  
      s->content_length   = 0;
      tmp = NULL;
      rc  = request_header("content-length", 
                           &tmp, 
                           private_data->sn, 
                           private_data->rq);
  
      if((rc != REQ_ABORTED) && tmp) {
          s->content_length = atoi(tmp);
      }
      
      s->method           = pblock_findval("method", private_data->rq->reqpb);
      s->protocol         = pblock_findval("protocol", private_data->rq->reqpb);
      
      s->remote_host      = session_dns(private_data->sn);
      s->remote_addr      = pblock_findval("ip", private_data->sn->client);
      
      s->req_uri          = pblock_findval("uri", private_data->rq->reqpb);
      s->query_string     = pblock_findval("query", private_data->rq->reqpb);
  
      s->server_name      = server_hostname;
      
  #ifdef NETWARE
      /* On NetWare, since we have virtual servers, we have a different way of
       * getting the port that we need to try first.
       */
      tmp = pblock_findval("server_port", private_data->sn->client);
      if (NULL != tmp)
          s->server_port = atoi(tmp);
      else
  #endif
      s->server_port      = server_portnum;
      s->server_software  = system_version();
  
  
      s->headers_names    = NULL;
      s->headers_values   = NULL;
      s->num_headers      = 0;
      
  #ifdef NETWARE
      /* on NetWare, we can have virtual servers that are secure.  
       * PR_IsSocketSecure is an api made available with virtual servers to check
       * if the socket is secure or not
       */
      if (NULL != PR_IsSocketSecure)
          s->is_ssl       = PR_IsSocketSecure(private_data->sn->csd);
      else
  #endif
      s->is_ssl           = security_active;
  
      s->ssl_key_size     = -1; /* required by Servlet 2.3 Api, added in jtc */
      if(s->is_ssl) {
          s->ssl_cert     = pblock_findval("auth-cert", private_data->rq->vars);
          if(s->ssl_cert) {
              s->ssl_cert_len = strlen(s->ssl_cert);
          }
          s->ssl_cipher   = pblock_findval("cipher", private_data->sn->client);
          s->ssl_session  = pblock_findval("ssl-id", private_data->sn->client);
      } else {
          s->ssl_cert     = NULL;
          s->ssl_cert_len = 0;
          s->ssl_cipher   = NULL;
          s->ssl_session  = NULL;
      }
  
      return setup_http_headers(private_data, s);
  }
  
  static int setup_http_headers(nsapi_private_data_t *private_data,
                                jk_ws_service_t *s) 
  {
      int need_content_length_header = (s->content_length == 0) ? JK_TRUE : JK_FALSE; 
  
      pblock *headers_jar = private_data->rq->headers;
      int cnt;
      int i;
  
      for(i = 0, cnt = 0 ; i < headers_jar->hsize ; i++) {
          struct pb_entry *h = headers_jar->ht[i];
          while(h && h->param) {
              cnt++;
              h = h->next;
          }
      }
  
      s->headers_names  = NULL;
      s->headers_values = NULL;
      s->num_headers = cnt;
      if(cnt) {
          /* allocate an extra header slot in case we need to add a content-length header */
          s->headers_names  = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
          s->headers_values = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
  
          if(s->headers_names && s->headers_values) {            
              for(i = 0, cnt = 0 ; i < headers_jar->hsize ; i++) {
                  struct pb_entry *h = headers_jar->ht[i];
                  while(h && h->param) {
                      s->headers_names[cnt]  = h->param->name;
                      s->headers_values[cnt] = h->param->value;
                      if(need_content_length_header &&
                              !strncmp(h->param->name,"content-length",14)) {
                          need_content_length_header = JK_FALSE;
                      }
                      cnt++;
                      h = h->next;
                  }
              }
              /* Add a content-length = 0 header if needed. 
               * Ajp13 assumes an absent content-length header means an unknown, 
               * but non-zero length body. 
               */ 
              if(need_content_length_header) {
                  s->headers_names[cnt] = "content-length"; 
                  s->headers_values[cnt] = "0"; 
                  cnt++;
              }
              s->num_headers = cnt;
              return JK_TRUE;
          }
      } else {
          if (need_content_length_header) {
              s->headers_names  = jk_pool_alloc(&private_data->p, sizeof(char *));
              s->headers_values = jk_pool_alloc(&private_data->p, sizeof(char *));
              if(s->headers_names && s->headers_values) {
                  s->headers_names[0] = "content-length"; 
                  s->headers_values[0] = "0"; 
                  s->num_headers++;
                  return JK_TRUE;
              }
          }
          else
              return JK_TRUE;
      }
  
      return JK_FALSE;
  }
  